深入浅出话事件(下)

简介:
深入浅出话事件(下)
二.事件的由来
       在传统的面向对象的概念中是没有“事件”这个概念的。传统的面向对象概念中只有数据(Data,也称为field、域、成员变量)和方法(Method,也就是成员函数、function)。如果我没记错,那么事件这个概念最早出现在微软的COM技术中,又因为VB是基于ActiveX(COM的一种)的,所以“事件”这一概念便通过VB广而推之、为众多程序员所熟知并使用的——我就是其中的一员。
       .NET Framework实际上是对COM的更高层级的封装——要知道,早先.NET Framework这个名字没有出来之前,它叫“COM3”来着——自然就保留了对事件的支持。
三.事件的意义
       《进化论》说:“物竞天择,合理即存在。”
       微软说:“我是老大,存在即合理!”
       姑且不管微软是不是在耍大牌、搞霸权——事件的存在的确给程序的开发带来了很多方便。从设计层面上讲,它使程序在逻辑思维方面变得简洁清晰、便于维护;从技术层面上讲,它把坚涩难懂的Windows消息传递机制包装的漂漂亮亮,极大地降低了程序员入职的门槛儿。
从软件工程的角度上来看,事件是一种通知机制,是类与类之间保持同步的途径。
问曰:什么同步?
答曰:消息同步!
四.事件的本质——对消息传递的封装
事件可以被激发(Fire,也有称为“引发”的),一个类所包含的成员事件可以在多种情况下被激发。最典型的:一个按钮的Click事件,可以由用户使用鼠标来激发它,也可以由测试这个软件的另一个软件通过Win32 API函数来激发它。
我们来简要讨论一下这个Click事件:
其实,如果你了解Win32的本质,你应该明白用户是不可能直接接触到某个控件的。表面上看,的确是用户用鼠标点击了一下按钮。而实际上,当用户按下鼠标左键的时候是通过鼠标向Windows操作系统发送了一个“左键单击[x,y]点”消息,然后Windows再根据[x,y]的位置把这个消息分配(路由)给应该接收它的控件——这就是Windows的消息传递/路由机制。
同理,当你移动鼠标的时候,看似好像指针在随你的意愿移动,而实际上是你的鼠标在以每秒钟几百次的频率把当前位置汇报给Windows操作系统,然后Windows再把一个漂亮的指针“画”在屏幕上给你看——哈哈,我们都被骗了!
然而这些内容对于C#程序员都是不可见的——都被封装成了“事件”。因此,从Windows系统的机理上讲,事件机制就是对Windows消息传递机制的包装。
       下面的代码是对Visual Studio 2005自动生成的WinForm程序的一个模拟。读懂之后,大家可以自己写一个WinForm,对照剖析其中的机理。
代码:
//============水之真谛============//
//                                                                  //
//           [url]http://blog.csdn.net/FantasiaX[/url]       //
//                                                                 //
//========上善若水,润物无声=========//
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms; //先添加对System.Windows.Forms和System.Drawing程序集的引用哦!
using System.Drawing;
namespace EmulateWinForm
{
    // 自定义的EmulateForm类,派生自Form类。
    class EmulateForm : Form
    {
        //两个控件
        private Button myButton;
        private TextBox myTextBox;
        //初始化各个控件和窗体本身,并把控件加入窗体的Controls数组。
        private void InitializeComponent()
        {
            myButton = new Button();
            myTextBox = new TextBox();
            myButton.Location = new System.Drawing.Point(195, 38);
            myButton.Size = new System.Drawing.Size(75, 23);
            myButton.Text = "Click Me";
            myButton.Click += new EventHandler(myButton_Click);//挂接事件处理函数
            myTextBox.Location = new System.Drawing.Point(12, 12);
            myTextBox.Size = new System.Drawing.Size(258, 20);
            Controls.Add(myButton);
            Controls.Add(myTextBox);
            Text = "EmulateForm";
        }
        //myButton的Click事件发生时,EmulateForm类给予的事件响应函数(Event Handler)
        void myButton_Click(object sender, EventArgs e)
        {
            myTextBox.Text = "Hello, Event World!";
        }
        //在EmulateForm类的构造函数中执行上面的初始化方法
        public EmulateForm()
        {
            InitializeComponent();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            EmulateForm myForm = new EmulateForm();
            Application.Run(myForm);
        }
    }
}
代码剖析:
1.        要想引用using System.Drawing; using System.Windows.Forms;这两个Namespace,首先要手动添加对System.Drawing和System.Windows.Forms两个程序集(Assembly)的引用。
2.        EmulateForm类是自定义的,注意,它派生自Form类。为了清晰起见,我已经把代码简化到了几乎最简……只有两个成员变量。myButton是Button类的一个实例;myTextBox是TextBox类的一个实例。EmulateForm类的成员方法private void InitializeComponent()完全是对真正WinForm程序的模仿,在它的函数体里,对成员变量进行了实例化、初始化(比如确定大小和位置),并将它们加入了窗体的Controls数组里。这个函数将在EmulateForm的构造函数里被执行。
3.        本例中最重要的部分就是对myButton的初始化。注意这句:myButton.Click += new EventHandler(myButton_Click);
myButton.Click是myButton的Click事件,你可能会奇怪:这回怎么不用自己去声明一个事件了呢?呵呵,因为.NET Framework已经为我们准备好了这个事件,你直接用就好了。不过,我们是为了探寻底细而来,所以我还得仔细说一说这个事件。
4.        详细剖析Button.Click事件:首先,事件都是基于委托的,那么myButton.Click事件是基于哪个委托呢?通过查找MSDN,你可以发现myButton.Click是继承自Control类,并基于EventHandler这一委托——下面是EventHandler委托的声明
[SerializableAttribute]
[ComVisibleAttribute(true)]
public delegate void EventHandler (Object sender, EventArgs e)
如果你不太了解事件是怎么声明的,回过头去温习一下《深入浅出话事件(上)》。
在这个声明中,方括号中的是Attribute,你暂时不用去理会它。关键是看EventHandler这个委托:这个委托的参数列表要求它所挂接的函数(对于事件来说就是挂接的事件处理函数)应该具有两个参数——Object类型的sender和EventArgs类型的e。这两个参数起什么作用呢?呵呵,其实非常好玩儿——前面说过了,事件机制是对消息机制的封装,你可以把消息理解成一枚炮弹,sender就是“谁发射的炮弹”,e就是“炮弹里装的什么东西”,炮弹的目标当然就是消息的接收处理者了。我们仔细回顾一下上篇亲手写的那个FireEventArgs类:这个类里不是有两个成员变量吗?一个是代表着火楼层的floor,一个是代表火级的fireLevel,随着Building类实例的FireAlarmRing事件引发,FireEventArgs类的实例e就被发射到了Employee类和Fireman类的实例那里,这两个实例再打开“炮弹”根据发射过来的内容给出相应处理。就像真实的战争中的炮弹有常规弹、穿甲弹、燃烧弹等等一样,我们的“消息炮弹”也不只一种,信手拈来几个与大家共赏一下:
① EventArgs类:这个就是Click事件中使用的那个。算是常规弹吧。因为用户点击按钮是个非常简单的事件,不需要它携带更多的信息了。
② MouseEventArgs类:是由MouseMove、MouseUp、MouseDown事件发射出来。它的实例携带了很多其它的信息,其中最常用的就是一个X和一个Y——用腿肚子想也能想明白,那是鼠标当前的位置。后面的例子中我们给出演示。
③ PaintEventArg类:由Paint事件发送出来。这颗炮弹可不简单,那些非常漂亮的自定义控件都离不开它!在它的肚子里携带有一个Graphics,代表的是你可以在上面绘画的一块“画布”……
OK,先列举3个吧MSDN里有它们的全家福,位置是System.EventArgs的Derived Classes树。微软在.NET Framework方面可谓下足了功夫,从这些Event Args(事件参数),到各种委托,再到五花八门的事件,都已经为我们做了良好的封装,我们只需要拿出来用就是了。
5.        void myButton_Click(object sender, EventArgs e)是EmulateForm类对myButton.Click事件的响应函数(也称事件处理器,Eventhandler)。注意它的参数列表,是不是与EventHandler委托一致啊:p
6.        主程序没什么好说的了——new一个EmulateForm的实例出来,用Application.Run方法执行程序就好了。
7.        顺便在这里做一个纠偏:上面已经解释过sender是什么了——它是消息的发送者。我屡次在一些书中发现诸如“事件发送者”这类的话,这是不对的!你想啊,事件只能引发、激发、发生,怎么可能“发送”呢?不合逻辑……
作业1:
      
       建立一个WinForm程序,如图。包含1个Panel,3个TextBox,1个Button。
要求:
1.    当鼠标在Panel里滑动时,textBox1和textBox2分别显示鼠标当前的X和Y。
2.    当鼠标点击按钮时,textBox3要显示Hello Events World!字样。
提示:
1.    留心MouseMove事件的e
2.    留心Visual Studio 2005使用的是C# 2.0,并且使用partial关键字将Form1类的代码分别存储在了Form1.cs和Form1.Designer.cs两个文件里。
作业2:
       将《深入浅出话事件(上)》中嘎子炸鬼子的程序升级至使用事件的版本。(代码我将在以后的日子里给出)。
 
OVER
 
法律声明:本文章受到知识产权法保护,任何单位或个人若需要转载此文,必需保证文章的完整性(未经作者许可的任何删节或改动将视为侵权行为)。若您需要转载,请务必注明文章出处为51cto和CSDN以保障网站的权益;请务必注明文章作者为刘铁猛,并向 [email]bladey@tom.com[/email]发送邮件,标明文章位置及用途。转载时请将此法律声明一并转载,谢谢!
 









本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/18754,如需转载请自行联系原作者
目录
相关文章
|
14天前
|
人工智能 大数据
碰撞事件探究
【10月更文挑战第21天】碰撞事件是一个充满奥秘和挑战的研究领域。深入理解碰撞的原理和规律,不仅有助于我们解决实际问题,也能让我们更好地认识自然界的运行机制。随着科学技术的不断进步,我们对碰撞事件的研究将不断深入,为人类的发展和进步提供更多的知识和启示。还可以结合具体的实验案例、实际应用和科学发现,进一步拓展和丰富对碰撞事件的探讨,使其更加全面和深入。
25 0
|
3月前
|
C#
由浅入深理解C#中的事件
由浅入深理解C#中的事件
108 19
|
3月前
|
存储 算法 安全
C#语言进阶(二)—事件全解
C#语言进阶(二)—事件全解
36 0
|
3月前
|
存储 前端开发 JavaScript
PixiJS源码分析系列:第四章 响应 Pointer 交互事件(上篇)
PixiJS源码分析系列:第四章 响应 Pointer 交互事件(上篇)
|
JavaScript
WebApi入门第三章(事件介绍及注册事件 )
WebApi入门第三章(事件介绍及注册事件 )
129 0
WebApi入门第三章(事件介绍及注册事件 )
|
C++ Windows 定位技术
16、深入浅出MFC学习笔记,事件与消息
一、基本概念 1、Windows程序的本质是基于消息的事件驱动。 Windows程序分为程序代码和UI资源两大部分。如图所示,资源的实际内容是二进制代码,借助各种工具产生。 在32位操作系统中不再有small/medium/large等内存模式之分。
910 0
|
JavaScript 前端开发
【重温基础】20.事件
【重温基础】20.事件
123 0
|
JavaScript 前端开发 Java
前端基础四之JavaScriptDOM与事件
前端基础四之JavaScriptDOM与事件
132 0
前端基础四之JavaScriptDOM与事件
|
Web App开发
移动端基础事件和交互(未完待续)
移动端基础事件和交互
1286 0