Framework 类库的事件编程

 转载于http://study.qqcf.com,不过没有作者,然后语言是VB的,但是应该相同的

EventHandler 委托
自定义的事件参数
参数化自定义事件
小结

本月的内容是专门介绍事件编程的系列专栏(共三期)的最后一期。在前两期专栏中,我已经介绍了如何定义和引发事件(请参见 Basic Instincts:Programming with Events Using .NET 和 Basic Instincts:Static Event Binding Using WithEvents)。我还解释了如何使用动态和静态事件绑定来绑定事件处理程序。本月,我将通过一些在 Microsoft .NET Framework 中处理较常用的事件处理实例来总结我对事件的介绍。

EventHandler 委托


当您使用 Windows® 窗体或 ASP.NET 构建应用程序时,您会看到,在所遇到的事件中有相当大的比率是根据一个名为 EventHandler 的通用委托类型定义的。EventHandler 类型存在于 System 命名空间中并具有以下定义:

Delegate Sub EventHandler(sender As Object, e As EventArgs)

委托类型 EventHandler 在它的调用签名中定义了两个参数。第一个参数(名为 sender)是基于通用 Object 类型的。sender 参数用于传递指向事件源对象的引用。例如,当 Button 对象引发基于 EventHandler 委托类型的事件时,作为事件源的它将传递一个对自身的引用。

由 EventHandler 定义的第二个参数名为 e,它是 EventArgs 类型的对象。在许多情况下,事件源传递的参数值等于 EventArgs.Empty,这表明没有额外参数信息。如果事件源希望在 e 参数中传递额外的参数化信息,则它应该传递一个从 EventArgs 类的派生类创建的对象。

图 1 所示的示例在 Windows 窗体应用程序中包含了两个事件处理程序,它们使用静态事件绑定来绑定。Form 类的 Load 事件和 Button 类的 Click 事件都是根据委托类型 EventHandler 定义的。

您还应该注意到,图 1中的两个事件处理程序方法的名称和格式与 Visual Studio .NET IDE 为您生成的一致。例如,如果您在设计视图中双击某个窗体或命令按钮,Visual Studio .NET 将自动创建类似的事件处理程序方法主干。您需要做的仅仅是填充这些方法的实现,以便为您的事件处理程序赋予预期的行为。

您也许会注意到,Visual Studio .NET IDE 是使用 Visual Basic 6.0 要求的命名方案来生成处理程序方法的。然而,您应当记住的是,Visual Basic .NET 中的静态事件绑定并不真正与处理程序方法的名称有关。与其相关的是 Handles 子句。您可以随意将处理程序方法重命名为所需的任何名称。

您可以重写这两个事件处理程序,以便它们使用动态事件绑定(而非静态事件绑定)来绑定。例如,图 2 中从 Form 派生的类提供了与图 1中从 Form 派生的类完全相同的事件绑定行为。唯一的区别是,后者使用了动态事件绑定,并且不需要 WithEvents 关键字或 Handles 关键字。在许多情况下,您将根据 EventHandler 委托类型来编写处理程序方法的实现,而不是引用 sender 参数或 e 参数。例如,当您为从 Form 派生的类的 Load 事件编写处理程序时,这些参数值并没有实际的作用。sender 不会提供任何值,因为它只是传递 Me 引用。e 参数传递 EventArgs.Empty:

Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'*** these tests are always true
Dim test1 As Boolean = sender Is Me
Dim test2 As Boolean = e Is EventArgs.Empty
End Sub

您也许想知道,为什么 Load 事件的调用签名没有针对其需要进行更多自定义。毕竟,如果 Load 事件根本不包含任何参数,情况将不会这么令人困惑。要找到其他基于 EventHandler 委托类型的事件(并且其 sender 参数或 e 参数不传递任何值)的示例很容易。

请回答以下问题。如果该委托类型具有这样的通用调用签名,为什么您会认为有这么多事件根据 EventHandler 建模?.NET Framework 的设计者为什么不根据具有适合其需要的调用签名的自定义委托来为每个事件建模?如您所知,.NET Framework 开发中的一个设计目标就是限制用于事件处理的委托的数量。以下几条是更进一步的解释。

最小化委托类型数量的第一个目的是,为了更有效地利用应用程序所使用的内存。加载更多类型意味着占用更多内存。如果由 Windows 窗体框架中的类定义的每个事件都基于一个自定义委托,则每次运行 Windows 窗体应用程序时都必须将上百个委托类型加载到内存中。Windows 窗体框架可依赖很少的委托类型在 Form 类和各种控件类中定义上百个事件,从而提供更好的内存利用率。

最小化委托类型数量的第二个目的是,利用可插接式处理程序方法来增加实现多态性的可能。当您使用与 EventHandler 委托匹配的调用签名来编写处理程序方法时,可以将其绑定到大多数由窗体及其控件引发的事件上。

让我们来看一些编写通用事件处理程序的示例。首先介绍这样一个示例:在这个示例中,可以通过将用户输入改为大写来响应窗体中多个文本框的 TextChanged 事件。没必要为每个控件都创建单独的事件处理程序。相反,您可以只创建一个事件处理程序,然后将其绑定到多个不同文本框的 TextChanged 事件上(请参见图 3)。

对于这个示例,首先应该注意的是,Handles 子句并不仅限于一个事件。您可以在 Handles 关键字后面使用由逗号分隔的列表来包括任意数量的事件。在本示例中,使用了 TextChangedHandler 方法来创建三个不同的事件处理程序。因此,当用户更改这三个文本框中任意一个的文本时,都将执行这个方法。

当执行 TextChangedHandler 方法时,如何知道是哪个 TextBox 对象引发该事件呢?这就是 sender 参数要解决的问题。请记住,sender 参数是根据通用类型 Object 传递的。这意味着,在针对其编程之前,必须将它转换成一个更具体的类型。在前面的示例中,要访问 sender 参数的 Text 属性,就必须将该参数转换为 TextBox。

如果您曾经使用 Visual Basic 的早期版本生成了基于窗体的应用程序,则您可能习惯于使用控件数组。在 Visual Basic 6.0 中使用控件数组的主要优势在于,此功能使得创建一个能够响应由多个不同控件引发的事件的处理程序方法成为可能。Visual Basic .NET 不支持控件数组。然而,您无需过度紧张,因为您刚才已经看到,Visual Basic .NET 提供了一种替代技术,可以将一个处理程序方法绑定到多个不同的事件上。

.NET Framework 的事件体系结构还为您提供了控件数组无法实现的功能。例如,您可以创建一个处理程序方法来响应由多个不同类型的控件所引发的事件。图 4 显示了一个处理程序方法示例,它绑定到三个不同控件类型上的三个不同的事件上。

正如您所看到的,将处理程序方法绑定到事件的方案相当灵活。唯一的要求是,处理程序方法和它绑定到的事件应基于相同的委托类型。而 .NET Framework 中有相当多的事件都是基于 EventHandler 委托类型的,这使得编写通用处理程序方法十分简单。

当您编写通用处理程序方法时,有时需要编写代码来执行条件操作,而这些操作只在事件源是某种特定类型的对象时才执行。例如,您的处理程序方法可以使用 TypeOf 运算符来检查 sender 参数。这使得您的处理程序方法可以在事件源为 Button 对象时执行一组操作,而在事件源为 CheckBox 对象时执行另一组操作,如下所示:

Sub GenericHandler1(sender As Object, e As EventArgs)
If (TypeOf sender Is Button) Then
Dim btn As Button = CType(sender, Button)
'*** program against btn
ElseIf (TypeOf sender Is CheckBox) Then
Dim chk As CheckBox = CType(sender, CheckBox)
'*** program against chk
End If
End Sub

返回页首
自定义的事件参数


基于 EventHandler 委托的事件通知通常不在 e 参数中发送任何有意义的信息。e 参数通常是无用的,因为它包含 EventArgs.Empty 值或 Nothing 值。然而,.NET Framework 的设计者创建了一个将参数化信息从事件源传递到其事件处理程序的约定。此约定包括自定义事件参数类和自定义委托类型的创建。

由 Form 类引发的鼠标事件为应该如何使用此约定提供了一个很好的示例。有关鼠标位置和按下哪个鼠标键的参数化信息在一个名为 MouseEventArgs的类中建模。MouseEventArgs 类包含了用于跟踪鼠标位置的 X 和 Y 属性,以及用于指示按下哪个鼠标键的 Button 属性。请注意,按照约定,MouseEventArgs 类必须从通用类 EventArgs 继承。

在事件通知中传递参数化信息的约定需要一个自定义委托来补充自定义事件参数类。因此,有一个名为 MouseEventHandler 的委托用于补充 MouseEventArgs 类。该处理程序委托的定义如下:

Delegate Sub MouseEventHandler(sender As Object, e As MouseEventArgs)

现在,假设您希望对一个与鼠标有关的事件(如 Form 类的 MouseDown 事件)作出响应。您可以编写如图 5 所示的处理程序方法。

请注意,e 参数在该处理程序方法的实现中非常有用。e 参数用于确定鼠标位置以及按下哪个鼠标键。所有这些参数化信息都可以通过设计 MouseEventArgs 类来实现。

您可以找到在 Windows 窗体框架中使用的这种参数化约定的其他示例。例如,有一个名为 KeyPressEventArgs 的类,它由一个名为 KeyPressEventHandler 的委托类型补充。此外,ItemChangedArgs 类由一个名为 ItemChangedHan
本文来自: 乘风原创程序(http://www.qqcf.com) 详细出处参考:http://study.qqcf.com/web/270/37791.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值