C#委托本质探索 五、点击事件的真实原理
// 研究控件点击事件的真实原理
// 这里定义了一个控件,编译后,将这个控件放到窗体上,找到MyClick事件,自己加点代码就可以测试
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class MyButton : Control
{
const int WM_LBUTTONUP = 0x202;
#region " 字 段 声 明 "
EventHandlerList _myEvents;
object _eventClick = new object();
#endregion
#region " 属 性 定 义 "
public EventHandlerList MyEvents
{
get
{
if (this._myEvents == null)
{
this._myEvents = new EventHandlerList();
}
return this._myEvents;
}
}
#endregion
#region " 事 件 定 义 "
public event EventHandler MyClick
{
add
{
Events.AddHandler(_eventClick, value);
}
remove
{
Events.RemoveHandler(_eventClick, value);
}
//AsyncCallback
//Activator
}
#endregion
#region " 私 有 方 法 "
private void MyOnClick(EventArgs e)
{
EventHandler handler = (EventHandler)base.Events[_eventClick];
if (handler != null)
{
handler(this, e);
}
}
private void WmMouseUp(ref Message m, System.Windows.Forms.MouseButtons button, int clicks)
{
//验证状态、将传入的消息加工成事件参数
// ...
MouseEventArgs args = new MouseEventArgs(button, clicks, SignedLOWORD(m.LParam), SignedHIWORD(m.LParam), 0);
this.MyOnClick(args);
}
private int SignedLOWORD(IntPtr n)
{
return (short)((int)((long)n) & 0xffff);
}
private int SignedHIWORD(IntPtr n)
{
return (short)(((int)((long)n) >> 0x10) & 0xffff);
}
#endregion
#region " 公 有 方 法 "
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_LBUTTONUP:
this.WmMouseUp(ref m, System.Windows.Forms.MouseButtons.Left, 1);
break;
}
}
#endregion
}
// MyButton.MyClick的真实处理情况是这样的:
// 首先,向外公布一个MyClick方法变量,对这个变量的操作支持add和remove操作.
// 当Add操作时(也就是从外边作+=操作时),将传入值写入一个列表,起个名字叫做事件列表.
// 在MyButton内部的消息处理过程WndProc中,当截获到WM_LBUTTONUP消息时,调用WmMouseUp方法处理.
// 在WmMouseUp实体中调用MyOnClick方法.
// 在MyOnClick实体中,从事件列表中取到方法实例handler并调用方法.
// MyButton.MyClick的工作原理与例4所描述的不同
// 1.方法变量MyClick没有直接用来存放外部的方法实例,而是象属性那样,存放在一个字段_myEvents中.
// 2.消息处理过程WndProc也没有MyClick的调用,而是在MyOnClick中调用方法字段.
// 从本质上讲WndProc还是处理了MyClick的,但实现思路上区别很大.
// 好像少了一个环节,MyClick在内部没实例化过.MyOnClick中进行了方法字段是否为空的判断.
// 这说明方法变量在内部不一定需要传入实例,只要内部调用时进行非空判断就可以了.
// 以上研究都是把方法变量当做一般变量来处理.
// delegate与消息循环结合,这就是我们看到的事件.
// 事件带来的效果,实现了一个对象将自己的方法公布给别人,并由别人进一步完善这个方法.
// 这又绕回到最开始举的例子:领导委托下属做某件事!
// 最初的例子委托用错了地方,把下属的方法定义了一个变量来调用.
// 应该是把领导这个对象内定义一个方法变量,由下属具体实现.
// 下一例重构最初的例子.
}