窗体(windows),事件(event),消息(message)
我们大部分人用的操作系统都是微软的windows系列。windows顾名思义是窗口,窗体。它最大的特点就是几乎所有操作都是图形界面。在面向对象开发中是一切皆对象,那在windows的操作中是一切皆窗体。窗体背后对应的是消息机制。是以消息以基础,以事件为驱动。所谓窗体就是我们能用眼睛看得到的一个个图形界面,几乎任何可显示的实体都是窗口,有时弹出的对话框也算个小窗体,一个按钮也算窗体。一个界面可以由多个窗体组合而成。事件就代表着所有的操作,比如鼠标动一下,点一下,窗口拉大拉小,文本框输入文字等…而事件发生后是以消息的形式发给应用程序对应的函数去处理。所以窗体,事件,消息三者的关系是我们对窗体的操作就是一个事件,事件会产生消息。
windows操作系统是事件驱动,那运行在它上面的绝大部分应用程序自然也是事件驱动。应用程序大部分时候是傻傻的呆那不动的,只有你去点下它才会动起来,做相应的操作。当然了有些应用程序可能会有些后台操作,比如设个时钟,每过多久自动刷新下或做些其他啥操作。
我觉得人其实也有点像个事件驱动的机器了。我们接受各种内部或外部的刺激,然后产生各种反应。就算在睡觉时也在接受刺激做些本能的反应。行为主义心理学派就认为人根本没有所谓的自由意志,就只知道通过一些刺激做些反应。只不过各种刺激非常复杂,并且反应也是一系列的连锁反应,所以我们不容易感觉的到。当然各大 心理学派所持的观点不一样,那些理论我们也不容易证否。跟算法里面着名的NP难问题有点类似,我们不知道NP难问题是不是能解决。不能证明它能解决,也不能证明它不能解决。
消息控件一般分三层结构。
第一层,windows内核。里面维护一个消息队列,像鼠标,键盘等一些IO设备产生事件后不是直接传给应用程序,而是先被操作系统处理,转换成一个个的“消息”.由于创建窗体时会指定一个应用程序句柄,所以当操作某个窗体产生时操作系统知道哪些消息是属于哪个应用程序的,操作系统为每个应用程序维护一个消息队列。
第二层,应用程序。在windows API的编程(www.cppentry.com)中一般通过一个while循环,用GetMessage或PeekMessage来获取消息队列中的消息。不过这些信息还只相当于是原材料,需要进一步加工。通过TranslateMessage把它进一步加工,做一些转换,或者如果是些无用的消息就会被直接忽略掉。加工完了后又通过DispatchMessage把信息重新传回操作系统。每一个消息实际上是一个一个结构体,里面有些相关信息,里面有一个窗体句柄。这样我们就知道这个消息是哪个窗体产生的。
第三层,窗体过程(windows procedure),创建窗体时会指定一个窗体过程,实际上就是窗体对应的一些处理消息的函数。当应用程序把消息重新传回操作系统后。操作系统再把消息发送给窗口过程,窗口过程就根据消息中的一些信息做些判断做些不同的处理。
C#中的事件处理机制
在C#中做了很多的封装,我们根本看不到消息的处理过程。通过层层封装最后我们看到的只是事件(event)这个概念,在C#中event是个关键字,也可以看做某种类型。当对窗体做一些操作时可以生成一个个的事件。比如点击按键btnOK,它对应的事件是btnOK.Click.(Click是C#中事先定义好了的事件,我们只要拿来用就行。)其实我们可以这样简单的理解,消息控制的三层结构中,前面所有操作都给你封装了,直到最后指定一个个的窗体过程,而且不同的消息对应的窗体过程都指定好的了。只不过那个过程为空,有点相当于个函数指针,要你自己去指定一个具体的函数。在C#中没有指针,但有个概念叫代理(delegate),它类似于函数指针。而事件相当于是对代理的封装,或者说是代理的一种应用。代理还可以用在其他很多地方。
比如定义事件Click是这样定义的。
(1)public event EventHandler Click; // 其中EventHandler是一个代理类型。定义事件的规范格式就是用关键字event然后加代理类型,然后再加变量名。定义代理EventHandler的格式是这样的。
(2)public delegate void EventHandler(object sender, EventArgs e);//它表示EventHandler可以指向任意以object,EventArgs为参数,返回类型是void的函数。我们前面说了事件Click还只相当于一个空的窗体过程。那我们怎么去指定具体的函数来。我们可以通过+=这样的关键字来指定一个事件处理程序。比如
(3)btnOK.Click += new System.EventHandler(btnOK_Click); //其中btnOK_Click是函数名,你也可以取其他任何名字。然后在class中任何地方定义函数
而且绑定多个像+=添加字符串一样直添加。当一个事件绑定了多个函数时,事件触发时多个函数会全部执行。当然我们还能通过-=这样的关键字解除某个函数的绑定。
void btnOK_Click(object sender, EventArgs e)
{
//操作相应操作的代码
}
反正C#中所有与窗口或控件相关的事件都给你封装好了,你直接拿来用,只要绑定个具体的函数就行。另外事件绑定的函数类型一般是两个参数,其中第一个是object sender,它表明是哪个窗体对象触发的这个事件。如果是按钮的话可以把把这个object对象转换成button对象,然后获取按钮其他信息(button)sender.另外一个参数是EventArgs,其实这个参数有时会不一样,如果是键盘触发的事件它就是KeyPressEventArgs了,它是附带有其他一些信息,比如键盘按的哪个按钮啊。
[1] [2] 下一页