频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

7.4.2 生成事件

作者: Christian Nagel等著 李铭 译 黄静 审校 出处:清华大学出版社  (  ) 砖  (  ) 好  评论 ( ) 条  进入论坛
更新时间:2008-01-29 09:52
关 键 词:委托  C#  .NET  运算符  类型  C#2005&.NET_3.0高级编程
阅读提示:《C#2005&.NET_3.0高级编程》(第5版)第七章将学习.NET如何将委托用作实现事件的方式。本文主要讲了生成事件。

7.4.2  生成事件

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

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

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

public delegate void ActionEventHandler(object sender, ActionCancelEventArgs 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属性,这是一个字符串属性,包含事件处理状态的文本信息。下面是ActionCancelEventHandler类的代码:

public class ActionCancelEventArgs : System.ComponentModel.CancelEventArgs
{
string message = String.Empty;

public ActionCancelEventArgs() : base() {}

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

public ActionCancelEventArgs(bool cancel, string message) : base(cancel)
{
this.message = message;
}
public string Message
{
get {return message;}
set {message = value;}
}
}

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

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

ActionCancelEventArgs e = new ActionCancelEventArgs();
Action(this, e);

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

protected void OnAction(object sender, ActionCancelEventArgs ev)
{
if(Action != null)
{
Action(sender, ev);
}
}


代码并不多,但完成了需要的工作。把该方法声明为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 Wrox.ProCSharp.Delegates
{
public class BusEntity
{
string time = String.Empty;

public BusEntity()
{
Form1.Action += new Form1.ActionEventHandler(Form1_Action);
}
private void Form1_Action(object sender, ActionCancelEventArgs e)
{
e.Cancel = !DoActions();
if(e.Cancel)
e.Message = "Wasn’t the right time.";
}
private bool DoActions()
{
bool retVal = false;
DateTime tm = DateTime.Now;
if(tm.Second < 30)
{
time = "The time is " + DateTime.Now.ToLongTimeString();
retVal = true;
}
else
time = "";

return retVal;
}
public string TimeString
{
get {return time;}
}
}
}

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

在构造函数中编写事件时,调用添加到委托列表中的方法Form1_Action,并遵循命名标准。在处理程序中,需要确定是否取消事件。DoActions方法根据前面描述的时间条件返回一个布尔值,并把_time字符串设置为正确的值。
之后,把DoActions的返回值赋给ActionCancelEventArgs的Cancel属性。EventArgs类一般仅在事件发送器和接收器之间来回传递值。如果取消了事件(ev.Cancel = true),Message属性就设置为一个字符串值,以说明事件为什么被取消。
如果再次查看buttonRaise_Click事件处理程序的代码,就可以看出Cancel属性的使用方式:

private void buttonRaise_Click(object sender, EventArgs e)
{
ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();
OnAction(this, cancelEvent);
if(cancelEvent.Cancel)brackets
labelInfo.Text = cancelEvent.Message;
else
labelInfo.Text = busEntity.TimeString;
}

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

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

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

【责任编辑:雪花 TEL:(010)68476606】

回书目   上一节   下一节
发表
查看
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
名称:Vista精品应用黄皮书
简介:《Vista精品应用黄皮书》囊括了Vista的各方面内容。此次的精简版,是将里面的内容做了提取,便于用户下载和使用。内容包含了各种Vista的安装与实施、技巧与解析以及各种Vista相关学习文档和相关软件的安全下载。该电子书是了解和应用Vista人员必备的工具手册,并且也是第一本