第6章 委托和事件(4)

2020-02-22 11:00

? 168 ? 第Ⅰ部分 C# 语 言 有相同的签名。显然事件委托也有这个要求。下面是Button_Click方法的定义:

Private void Button_Click(object sender, Eventargs e) { }

这个方法有几个重要的地方。首先,它总是没有返回值。事件处理程序不能有返回值。其次是参数。只要使用EventHandler委托,参数就应是object和EventArgs。第一个参数是引发事件的对象,在这个例子中是btnOne或btnTwo,这取决于被单击的按钮。把一个引用发送给引发事件的对象,就可以把同一个的事件处理程序赋予多个对象。例如,可以为几个按钮定义一个按钮单击处理程序,接着根据sender参数确定单击了哪个按钮。

第二个参数EventArgs是包含有关事件的其他有用信息的对象。这个参数可以是任意类型,只要它派生于EventArgs即可。MouseDown事件使用MouseDownEventArgs,它包含所使用按钮的属性、指针的X和Y坐标,以及与事件相关的其他信息。注意,其命名模式是在类型的后面加上EventArgs。本章的后面将介绍如何创建和使用基于EventArgs的定制对象。

方法的命名也应注意。按照约定,事件处理程序应遵循“object_event.object”的命名约定。object就是引发事件的对象,而event就是被引发的事件。从可读性来看,应遵循这个命名约定。

本例最后在处理程序中添加了一些代码,以完成一些工作。记住有两个按钮使用同一个处理程序。所以首先必须确定是哪个按钮引发了事件,接着调用应执行的操作。在本例中,只是在窗体的一个标签控件上输出一些文本。把一个标签控件从工具箱拖放到窗体上,并将其命名为lblInfo,然后在Button_Click方法中编写如下代码:

if(((Button)sender).Name == \ lblInfo.Text = \else

lblInfo.Text = \

注意,由于sender参数作为对象发送,所以必须把它的数据类型转换为引发事件的对象类型,在本例中就是Button。本例使用Name属性确定是哪个按钮引发了对象,也可以使用其他属性。例如Tag属性就可以处理这种情形,因为它可以包含任何内容。为了了解事件委托的多播功能,给btnTwo的Click事件添加另一个方法,使用默认的方法名。窗体的构造函数如下所示:

btnOne.Click += new EventHandler(Button_Click); btnTwo.Click += new EventHandler(Button_Click); btnTwo.Click += new EventHandler(btnTwo_Click);

如果让Visual Studio创建存根(stub),就会在源文件的末尾得到如下方法。但是,必须添加对MessageBox函数的调用:

Private void btnTwo_Click(object sender, EventArgs e) {

第6章 委托和事件 MessageBox.Show(\}

? 169 ? 如果使用匿名方法,就不需要Button_Click方法和btnTwo_Click方法了。事件的代码如下:

btnOne.Click += new EventHandler(lblInfo.Text = \btnTwo.Click += new EventHandler(lblInfo.Text = \btnTwo.Click += new EventHandler(MessageBox.Show

(\

在运行这个例子时,单击btnOne会改变标签上的文本。单击btnTwo不仅会改变文本,还会显示消息框。注意,不能保证标签文本在消息框显示之前改变,所以不要在处理程序中编写具有依赖性的代码。

我们已经学习了许多概念,但要在接收器中编写的代码量是很小的。记住,编写事件接收器常常比编写事件发送器要频繁得多。至少在Windows用户界面上,Microsoft已经编写了所有需要的事件发送器(它们都在.NET基类中,在Windows.Forms命名空间中)。

6.3.2 生成事件

接收事件并响应它们仅是事件的一个方面。为了使事件真正发挥作用,还需要在代码中生成和引发事件。下面的例子将介绍如何创建、引发、接收和取消事件。

这个例子包含一个窗体,它会引发另一个类正在监听的事件。在引发事件后,接收对象就确定是否执行一个过程,如果该过程未能继续,就取消事件。本例的目标是确定当前时间的秒数是大于30还是小于30。如果秒数小于30,就用一个表示当前时间的字符串设置一个属性;如果秒数大于30,就取消事件,把时间字符串设置为空。

用于生成事件的窗体包含一个按钮和一个标签。下载的示例代码把按钮命名为btnRaise,标签命名为lblInfo,您也可以给标签使用其他名称。在创建窗体,添加两个控件后,就可以创建事件和相应的委托了。在窗体类的类声明部分,添加如下代码:

public delegate void ActionEventHandler(object sender, ActionCancel EventArgs ev); public static event ActionEventHandler Action;

这两行代码的作用是什么?首先,我们声明了一种新的委托类型ActionEventHandler。必须创建一种新委托,而不使用.NET Framework预定义的委托,其原因是后面要使用定制的EventArgs类。方法签名必须与委托匹配。有了一个要使用的委托后,下一行代码就定义事件。在本例中定义了Action事件,定义事件的语法要求指定与事件相关的委托。还可以使用在.NET Framework中定义的委托。从EventArgs类中派生出了近100个类,应该可以找到一个自己能使用的类。但本例使用的是定制的EventArgs类,所以必须创建一个与之匹配的新委托类型。

基于EventArgs的新类ActionCancelEventHandler实际上派生于CancelEventArgs,而CancelEventArgs派生于EventArgs。CancelEventArgs添加了Cancel属性,该属性是一个布尔值,它通知sender对象,接收器希望取消或停止事件的处理。在ActionEventHandler类中,还添加了Message属性,这是一个字符串属性,包含事件处理状态的文本信息。下面是ActionCancel-

? 170 ? 第Ⅰ部分 C# 语 言 EventHandler类的代码:

public class ActionCancelEventHandler : System.ComponentModel. CancelEventArgs {

string _msg = \

public ActionCancelEventArgs() : base() {}

public ActionCancelEventArgs(bool cancel) : base(cancel) {}

public ActionCancelEventArgs(bool cancel, string message) : base(cancel) {

_msg = message; }

public string Message {

get {return _msg;} set {_msg = value;} } }

可以看出,所有基于EventArgs的类都负责在发送器和接收器之间来回传送事件的信息。在大多数情况下,EventArgs类中使用的信息都被事件处理程序中的接收器对象使用。但是,有时事件处理程序可以把信息添加到EventArgs类中,使之可用于发送器。这就是本例使用EventArgs类的方式。注意在EventArgs类中有两个可用的构造函数。这种额外的灵活性增加了该类的可用性。

目前声明了一个事件,定义了一个委托,并创建了EventArgs类。下一步需要引发事件。真正需要做的是用正确的参数调用事件,如本例所示:

ActionCancelEventArgs ev = new CancelEventArgs(); Action(this, ev);

这非常简单。创建新的ActionCancelEventArgs类,并把它作为一个参数传递给事件。但是,这有一个小问题。如果事件不会在任何地方使用,该怎么办?如果还没有为事件定义处理程序,该怎么办?Action事件实际上是空的。如果试图引发该事件,就会得到一个空引用异常。如果要派生一个新的窗体类,并使用该窗体,把Action事件定义为基事件,则只要引发了Action事件,就必须执行其他一些操作。目前,我们必须在派生的窗体中激活另一个事件处理程序,这样才能访问它。为了使这个过程容易一些,并捕获空引用错误,就必须创建一个方法OnEvent Name,其中EventName是事件名。在这个例子中,有一个OnAction方法,下面是OnAction方法的完整代码:

protected void OnAction(object sender, ActionCancelEventArgs ev) {

if(Action != null) Action(sender, ev); }

第6章 委托和事件 ? 171 ? 代码并不多,但完成了需要的工作。把该方法声明为protected,就只有派生类可以访问它。事件在引发之前还会进行空引用测试。如果派生一个包含该方法和事件的新类,就必须重写OnAction方法,然后连接事件。为此,必须在重写代码中调用base.OnAction()。否则就不会引发该事件。在整个.NET Framework中都用这个命名约定,并在.NET SDK文档中对这一命名规则进行了说明。

注意传送给OnAction方法的两个参数。它们看起来很熟悉,因为它们与需要传送给事件的参数相同。如果事件需要从另一个对象中引发,而不是从定义方法的对象中引发,就需要把访问修饰符设置为internal或public,而不能设置为protected。有时让类只包含事件声明和从其他类中调用的事件是有意义的。仍可以创建OnEventName方法,但它们是静态方法。

目前,我们已经引发了事件,还需要一些代码来处理它。在项目中创建一个新类,在这个例子中把该类称为BusEntity。本项目的目的是检查当前时间的秒数,如果它小于30,就把一个字符串值设置为时间;如果它大于30,就把字符串设置为::,并取消事件。下面是代码:

using System; using System.IO;

using System.ComponentModel;

namespace SimpleEvent {

public class BusEntity {

string _time =\ public BusEntity() {

Form1.Action += new Form1.ActionEventHandler(Form1_Action); }

private void Form1_Action(object sender, ActionCancelEventArgs ev) {

ev.Cancel = !DoAction(); if(ev.Cancel)

ev.Message =\’t the right time.\ }

private bool DoAction() {

bool retVal = false;

DateTime tm = DateTime.Now;

if(tm.second) < 30) {

_time =\ retVal = true;

? 172 ? } else

_time = \

return retVal; }

public string TimeString {

get {return _time;} } } }

第Ⅰ部分 C# 语 言 在构造函数中声明了Form1.Action事件的处理程序。注意其语法非常类似于前面Click事件的语法。由于声明事件使用的模式都是相同的,所以语法也应保持一致。还要注意如何获取Action事件的引用,而无需在BusEntity类中有对Form1的引用。在Form1类中,将Action事件声明为静态,这并不是必需的,但这样更易于创建处理程序。我们可以把事件声明为public,但接着需要引用Form1的一个实例。

在构造函数中编写事件时,调用添加到委托列表中的方法Form1_Action,并遵循命名标准。在处理程序中,需要确定是否取消事件。DoActions方法根据前面描述的时间条件返回一个布尔值,并把_time字符串设置为正确的值。

在DoActions返回值后,就把该值赋给ActionCancelEventArgs的Cancel属性。EventArgs类一般仅在事件发送器和接收器之间来回传递值。如果取消了事件(ev.Cancel = true),Message属性就设置为一个字符串值,以说明事件为什么被取消。

如果再次查看btnRaise_Click事件处理程序的代码,就可以看出Cancel属性的使用方式:

private void btnRaise_Click(object sender, EventArgs e) {

ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs(); OnAction(this, cancelEvent); If(cancelEvent.Cancel)

lblInfo.Text = cancelEvent.Message; else

lblInfo.Text = _busEntity.TimeString; }

注意创建了ActionCancelEventArgs对象。接着引发了事件Action,并传递了新建的ActionCancelEventArgs对象。在调用OnAction方法,引发事件时,BusEntity对象中Action事件处理程序的代码就会执行。如果还有其他对象注册了事件Action,它们也会执行。记住,如果其他对象也处理这个事件,它们就会看到同一个ActionCancelEventArgs对象。如果需要确定是哪个对象取消了事件,而且如果有多个对象取消了事件,就需要在ActionCancelEventArgs类

第6章 委托和事件 ? 173 ? 中包含某种基于列表的数据结构。

在与事件委托一起注册的处理程序执行完毕后,就可以查询ActionCancelEventArgs对象,确定它是否被取消了。如果是,lblInfo就包含Message属性值;如果事件没有被取消,lblInfo就会显示当前时间。

本节这基本上说明了如何利用事件和事件中基于EventArgs的对象,在应用程序中传递信息。

6.4 小结

本章介绍了委托和事件的基本知识,解释了如何声明委托,如何给委托列表添加方法,并讨论了声明事件处理程序来响应事件的过程,以及如何创建定制事件,使用引发事件的模式。

.NET开发人员将大量使用委托和事件,特别是开发Windows Forms应用程序。事件是.NET开发人员监视应用程序执行时出现的各种Windows消息的方式,否则就必须监视WndProc,捕获WM_MOUSEDOWN消息,而不是获取按钮的鼠标Click事件。

在设计大型应用程序时,使用委托和事件可以减少依赖性和层的关联,并能开发出具有更高复用性的组件。


第6章 委托和事件(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:教师资格证考试中学综合素质考点

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: