委托和事件在.NET Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里憋得慌,浑身不自在。近日,项目中需要在服务器上搭建一个虚拟仪表界面来实时显示各外围设备端连接状态,使用C#的委托、反射机制解决了问题。由于项目工程代码比较复杂,现结合一个在工作线程中实时刷新主界面空间内容的Winform小例子,对C#委托方法在桌面应用程序开发中刷新控件功能的方法进行小小的总结。
一、委托的使用方法
简单说,可以把委托理解为一个通知器,它可以把信息通知给事先绑定好的程序(函数)。我们只需要控制好要两点:1、委托的绑定;2、处理信息的函数的定义,就可以把委托简单的使用起来。
public delegate void DelegateTest(可以带参数); // 定义委托类型
public DelegateTest delegateTest; // 声明一个委托对象
delegateTest = new DelegateTest(函数名字,比如: FuncTest(...)); // 绑定委托
public void FuncTest(...) // 信息处理函数定义。需要注意的是函数类型,参数类型必须和委托的类型以及参数类型保持一致
{
.... //这样就可以对委托传过来的信息进行处理
}
下面看一下具体的小例子,代码中已经给出详细的注释。
一、新建一个C# winform工程,向其中添加一个工作线程类:Work,代码如下:
1 using System;
2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 using System.Threading; 7 8 namespace 更新主界面控件 9 { 10 public class Work 11 { 12 private volatile static Work instanceWork; // 单例 13 14 public delegate void InvokeListBox(int i); 15 public InvokeListBox invokeOthers; 16 17 /// <summary> 18 /// 构造函数 19 /// </summary> 20 public Work() 21 { 22 23 } 24 25 /// <summary> 26 /// 对外接口,获取单例 27 /// </summary> 28 /// <returns></returns> 29 public static Work GetInstance() 30 { 31 if (null == instanceWork) 32 { 33 instanceWork = new Work(); 34 } 35 36 return instanceWork; 37 } 38 39 /// <summary> 40 /// 业务函数,在工作过程中将状态传给主界面 41 /// </summary> 42 public void DoSomething() 43 { 44 for (int i = 0; i < 20; i++) 45 { 46 Thread.Sleep(1000); 47 invokeOthers(i); 48 } 49 } 50 } 51 }
二、主界面布局如下图所示,
代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 using System.Threading; 11 12 namespace 更新主界面控件 13 { 14 public partial class Form_MainUI : Form 15 { 16 private delegate void InvokeListBox(string strCon); // 刷新ListBox的委托。 17 private InvokeListBox invokeListBox; 18 19 private Work wk; //工作线程对象 20 21 public Form_MainUI() 22 { 23 InitializeComponent(); 24 25 wk = Work.GetInstance(); //单例模式初始化工作线程对象 26 wk.invokeOthers = new Work.InvokeListBox(ReciveData); // 绑定,接收工作线程过来的数据 27 invokeListBox = new InvokeListBox(RefrushListBox); // 绑定,刷新界面ListBox控件 28 } 29 30 /// <summary> 31 /// 启动工作线程 32 /// </summary> 33 /// <param name="sender"></param> 34 /// <param name="e"></param> 35 private void button_StartWork_Click(object sender, EventArgs e) 36 { 37 Thread th = new Thread(new ThreadStart(wk.DoSomething)); 38 th.Start(); 39 } 40 41 /// <summary> 42 /// 接收工作线程过来的数据,更新界面 43 /// </summary> 44 /// <param name="i"></param> 45 public void ReciveData(int i) 46 { 47 string strConten = i.ToString() + " 更新"; 48 49 if (this.listBox_state.InvokeRequired) 50 { 51 this.listBox_state.Invoke(invokeListBox, new object[] { strConten }); 52 } 53 } 54 55 /// <summary> 56 /// 具体刷新界面函数 57 /// </summary> 58 /// <param name="_str"></param> 59 public void RefrushListBox(string _str) 60 { 61 this.listBox_state.Items.Add(_str); 62 } 63 } 64 }
三、说明:
本例子中有两个委托:1、Work.cs中的委托变量 ivokeOthers,作用是将信息传递给给主界面对应的函数:ReceiveData()。2、Form_MainUI.cs中的委托变量 invokeListBox,作用是刷新界面控件ListBox_state的内容。点击“开始工作”按钮后,工作线程启动,调用Work.cs中DoSomeThing()函数。由于Form_MainUI.cs中的函数ReceiveData()函数绑定了Work.cs中的委托ivokeOthers,所以当Work.cs中DoSomeThing被调用时ivokeOthers会把变量i传递给ReceiveData()。ReceiveData()接收到数据后,通过Invoke调用刷函数RefrushListBox()修改主界面ListBox_State的内容。程序运行效果如下:
总结:
1、不要惧怕委托、事件的复杂的概念,动手操作练习一下,就简单的把它们当做传递消息的载体。
2、在工作线程中修改主界面其它控件的内容、状态、图片等操作类似,可以自己动手试试。
3、本小结在实现刷新主界面控件的时候使用了单例模式,不使用单例模式也完全可以实现,加上单例是为了让调用更加方便,在大项目开发中就会体现其优势。