在博客园的“听雨轩”中找到了这样一篇文章,对委托和事件介绍的非常非常详细,摘抄一部分,作为给自己的参考。
摘抄完了发现没有把“听雨轩”的地址记录下来,好吧,在此声明,下面的东西大凡是比较专业的,都不是我写的。贴上来是为了以后用的时候方便找到。
要点一:委托--委托是函数指针,那就是说,它能够引用函数,也可以把委托看作为一个类当对它进行实例化的时候要提供一个引用函数把这个引用函数作为它构造函数的参数
1. 申明委托:Delegate string deleg(string a)是一个委托申明,deleg是一个委托名,a为形参,这个委托返回一个string类型。
2.在申明了委托之后,可以对委托进行实例化,实例化时要提供一个引用函数,这个引用函数必须有和委托相同的参数(类型及个数,当然名称可以不一样)并且返回值(如果有的话)也要和定义的委托相同,例如对deleg这个委托,实例化时提供的函数可以如下:string func(string str)
实例化: deleg dl=new deleg(func);
事件--事件是委托类型的变量,意思是,如果给事件命名,要用event关键字和要使用的委托类型去申明
public event EventHandler Click
当要触发一个事件的时候,可以这样:
this.Button1.Click+=new EventHandler(事件处理函数名称);
EventHandler 仅是一个委托,更确切的表示应该是:Delegate。但是微软从不这样认为,如MouseDelegate或者PaintDelegate,而是称谓:MouseEventHandler 或者 PaintEventHandler。所以EventHandler比Delegate听起来更方便一些。
来看事件的委托的申明:
public delegate void EventHandler(object sender, EventArgs e); 
这个委托有一个void返回值,有两个形参,一个是object类型的,一个是EventArgs,因此当委托被实例化时,作为实参传入的委托函数也要有和委托相同类型的形参和返回值。
定义委托的形参EventArgs
任何形参类的定义都是继承EventArgs类的,不明白为什么这么定义,没有讲。
public class NumberReachedEventArgs : EventArgs 

 private int _reached; 
 public NumberReachedEventArgs(int num) 
 { 
 this._reached = num; 
 } 
 public int ReachedNumber 
 { 
 get 
 { 
 return _reached; 
 } 
 } 

一个委托的应用--跨线程调用Windows窗体控件
背景:异步操作与临界资源,当使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。
From MSDN:
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。
.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException .
比方说:现在我在主窗体线程中重新开一个线程,而这个线程要对窗体上的某个控件进行操作,如果在这个线程的处理程序中直接访问这个控件就会出问题

 

private void Form1_Load(object sender, EventArgs e)
{
 Thread th;
 XmlDocument xd = new XmlDocument();
 xd.Load("e:\\layout.xml");
 XmlNode xmlRootNode = xd.DocumentElement; //root
 populateTreeControl(xmlRootNode, this.treeView1.Nodes);
 this.treeView1.ExpandAll();
 this.treeView1.TabStop = false; //让treeview失去焦点
 this.textBox1.Text = "";

 th = new Thread(new ThreadStart(refreshTree)); //主窗体线程中开启了一个新的线程th
 th.Start();
}
public void refreshTree() 
{
 File.Delete("e:\\layout.xml");
 XmlDocument xd = new XmlDocument();
 xd.Load("e:\\1.xml");
 XmlNode xmlRootNode = xd.DocumentElement; //root
 populateTreeControl(xmlRootNode, this.treeView1.Nodes); //新线程th的线程处理程序会对主窗体上的TreeView控件进行操作
}

private void populateTreeControl(System.Xml.XmlNode document, System.Windows.Forms.TreeNodeCollection nodes)
{
 nodes.Clear(); //操作时会蹦出InvalidOperationException异常
 foreach (System.Xml.XmlNode node in document.ChildNodes)
 {
 TreeNode new_child = new TreeNode();
 string value = (node.Value != null ? node.Value : (node.Attributes != null && node.Attributes.Count > 0) ? node.Attributes[0].Value : node.Name);
 new_child.Name = value;

 string text = (node.Value != null ? node.Value : (node.Attributes != null && node.Attributes.Count > 1) ? node.Attributes[1].Value : node.Name);
 new_child.Text = text;


........
}[url=img/C406521492A90275_321_0.jpg] [/url]
如何从辅助线程以线程安全方式调用 Windows 窗体控件呢?
查询控件的 InvokeRequired  属性。 
如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke 。 
如果 InvokeRequired 返回 false,则直接调用控件。
上面的程序可以这么改: 
delegate void SetControlCallback(); //定义委托
public void refreshTree() //在调用Windows控件的另一个线程的线程处理方法中使用委托
{
 File.Delete("e:\\layout.xml");
 XmlDocument xd = new XmlDocument();
 xd.Load("e:\\1.xml");
 XmlNode xmlRootNode = xd.DocumentElement; //root
 if (this.treeView1.InvokeRequired)
 {
 //则使用实际调用控件的委托来调用 Invoke控件
 SetControlCallback d = new SetControlCallback(refreshTree);
 this.Invoke(d);
 }
 else
 {
 //直接调用控件
 populateTreeControl(xmlRootNode, this.treeView1.Nodes);
 }
}

posted on 2010-10-13 15:42  Jessica Lu  阅读(236)  评论(0)    收藏  举报