求一个C#事件处理范例。

 我来答
LuckZhang_LA
2012-04-12
知道答主
回答量:16
采纳率:100%
帮助的人:17.6万
展开全部
6.2 自定义事件
自定义事件,是委托的一个典型应用!开发人员在编写WinForm或者ASP.NET应用程序时,都会使用Button的Click事件。那么,这种事件触机制是如何实现的呢?我们能不能编写自己定义的事件;另外,我们需要在什么时候编写自己定义的事件呢?
6.2.1 为什么要使用自定义事件
很多开发人员都会向笔者提出这样的问题—“我们为什么要自己编写事件?控件里面已经集成了很多的事件,这还不够我们用吗?”。
实现上,如果不清楚掌握如果编写自定义事件,想要开发出真正能用的应用程序是很困难的。其主要原因有以下两点:
1. 自定义控件
通常情况下,开发人员为了减少重复界面功能代码的编写量,往往会开发出一些用户控件或者是自定义控件。这时,我们就会产生问题,微软为我们提供的控件中包括了大量的事件,那么我们自己编写的控件中,是不是也会存在很多的事件呢?
2. 自己编写的组件及类库
事件并不是UI层的控件类所专有的,很多底层的类也都包括了事件,比如SqlConnection类,该类的类视图如下所示:

在这个类中就包括了一个InfoMessage事件,该事件作用是“当 SQL Server 返回一个警告或信息性消息时发生”。也就是说,在我们自己编写一个类的时候,往往会发生一种特殊的数据传递情况,即:数据的传递的方向是由下向上的。为了解释这种“由下向上”的传递方式,我们先来看一个应用程序的分层结构图:

上图是一个比较常规的Socket通信软件的三层构架图,客户端及服务器的界面是两个“EXE”程序,但是为了系统的逻辑更好管理,我们通常是将所有的通信逻辑封装到业务逻辑层的两个“DLL”文件中。图中箭头方向说明整个系统功能的实现,是界面的“EXE”调用底层的“DLL”实现的。
然而,所有通信的功能都是在DLL中所定义的类里实现的,那么,当客户端的DLL向服务器的DLL发送了一个消息,并被服务器的“DLL”所截获之后,服务器界面的“EXE”又是怎么得知的呢?我们平时在使用QQ,MSN的时候有没有想过这样的问题呢?
这个时候,又出现了我们刚才提到的那个问题—“由下向上”,即,由下层“DLL”中的类,通知上层“EXE”中的界面。我们总不能再使用DLL调用一次EXE吧!
注意:在一般情况下.NET平台的DLL是可以引用EXE的,但是如上图所示,EXE已经对DLL进行了引用,此时IDE就不再允许DLL对EXE进行引用,这是为了防止发生循环引用产生死锁。即使IDE允许我们再利用DLL引用EXE,引用之后所创建的实例和当前运行的界面的实例也是位于不同内存的,所以任何操作都不会生效。
此时,利用委托实现的事件机制就可以为我们解决问题!
6.2.2 控件中的事件
为了理解事件机制,首先从控件中的事件谈起。我们以Button的Click事件为例进行说明。
当开发人员双击了Button之后,IDE会将设计器转向代码视图,并为我们生成一个用于事件回调的方法,如下图所示:

注意:这里的button1_Click并不是事件,它仅仅是事件发生时所调用的方法!
我们的问题是,为什么在Button1的Click事件发生之后,button1_Click方法就会被调用呢?实际上,在我们双击Button1的时候,IDE自动的添加了一段代码,该段代码位于“Form1.Designer.cs”中(.NET1.1中并不包括Form1.Designer.cs),“Form1.Designer.cs”的位置如下图所示:

打开Form1.Designer.cs,并展开“InitializeComponent()”方法,找到第42行,如下图所示:

我们可以看到如下代码:
this.button1.Click += new System.EventHandler(this.button1_Click);
实际上这段代码,也就是所谓的事件注册代码。该代码的意思是:如果this.button1的Click事件发生之后,就转向this. button1_Click方法进行处理。
为了更好的理解事件的注册过程,我们先第42行代码进行修改,如下图所示:

这里,我们将原来的
this.button1.Click += new System.EventHandler(this.button1_Click);
修改为
this.button1.Click = new System.EventHandler(this.button1_Click);
在这个程序里,这里的修改是为了更好理解,当然这种写法是语法错误的。
下面我们对其进行分析:
首先,观察“=”右面的表达式。
new System.EventHandler(this.button1_Click);
通过6.1.1一节中的说明,大家可以发现,这段代码实际上是建立了一个委托类型的实例,并让该委托指向了this.button1_Click方法。也就是说,在程序运行的“某一时刻”,系统会通过这个委托实例间接的调用this.button1_Click方法。
然后,我们再来观察“=”左面的表达示。在C风格的语言中“=”是赋值表达式,也就是说,“=”两侧表达式的数据类型应该是一样的。因此,既然“=”右侧的表达式是一个委托类型(System.EventHandler)的实例,那么this.button1.Click也应该是一个委托类型(System.EventHandler)。
通过上面的说明,我们得到一个信息,前面这段事件注册代码,是让this.button1.Click和System.EventHandler(this.button1_Click)指向了同一段内存空间,简单来讲,就是让this.button1.Click指向了this.button1_Click方法,调用了this.button1.Click,就相当于调用了this.button1_Click方法。因此,我们说,当this.button1的Click事件发生之后,方法this.button1_Click就会被调用。
在程序运行的时候,系统会自己检测this.button1是否被点击了,如果被点击了,就在button1的内部调用button1.Click,这时,Windows窗口中的button1_Click方法就会被执行。
当然,事件注册代码完全可以手写。因为,除了控件中事件注册代码是自动生成以外,其他类中的事件注册都是手写的。手工注册事件的方法如下:
首先,可以在事件发生之前的任何代码中添加事件(通常是在窗口的构造方法中),下面我们来手工注册button1的MouseMove事件,如下图所示:

当我们写完“=”时,会出现一个提示“Press TAB to insert”,这时,我们只需要按2下“TAB”键,事件的注册以及用于回调的方法,就会自己添加到代码窗口里,如下图所示:

自动生成的代码是将this.button1的MouseMove事件指向了button1_MouseMove方法。这样手写的代码和IDE自动生成的代码是完全一样的。
当然,作为控件的事件,我们完全可以自动生成,如果想自动生成button1的其他事件,只需要查看button1的属性窗口,并点击“ ”按钮,就会出现该控件的事件列表,如下图所示:

然后双击你想要的事件,代码就会自动生成了。
在前的面代码中为了更好理解事件注册,我们曾将
this.button1.Click += new System.EventHandler(this.button1_Click);
修改为
this.button1.Click = new System.EventHandler(this.button1_Click);
我们会发现,无论是自己写的事件注册代码,还是自动生成的代码,都是使用“+=”来实现的,实际上,作为事件注册的代码,我们仅仅能够使用“+=”来实现注册,简单的使用“=”是语法错误的!!!
“+=”操作符在C风格语言中是常用的操作符,比如
int i=0;
i+=1;
等同于
int i=0;
i=i+1;
因此,
this.button1.Click += new System.EventHandler(this.button1_Click);
在原则上等同于
this.button1.Click = this.button1.Click +
new System.EventHandler(this.button1_Click);
用自然语言来描述上面的代码就是“一个委托=这个委托本身+另外一个委托”。那么委托相加意味着什么呢?
在6.1.3一节中,我们讨论过MultiDelegate(多播委托),而事件本身也是委托,并且所有委托都是System.MultiDelegate类的派生类,在6.1.3中,我们曾经演示过,多个委托类型实例相加,就是将这些委托实例存放在一个多播委托的调用链中,当调用多播委托时,该多播委托的调用链中的所有委托都会顺序的被调用。
利用多播委托的原理,我们可以将多个方法注册给一个事件,如下所示:
this.button1.Click +=new System.EventHandler(this.button1_Click);
this.button1.Click +=new System.EventHandler(this.button1_Click1);
this.button1.Click +=new System.EventHandler(this.button1_Click2);
上面的代码,就将三个方法注册到了button1的Click事件中,当button1的Click事件触发之后,方法button1_Click,button1_Click1,button1_Click2将会被顺序调用。这样作的好处是,我们可以将多个功能以及逻辑完全独立的操作放在不同的方法中,当事件发生之后,这些方法将会顺序的被调用,以实现我的需要的级联操作。
6.2.3 控件中事件的回调方法
说完了事件的注册,下面我们来谈一下事件的回调方法。首先,我们还要再一次回顾事件注册的代码:
this.button1.Click +=new System.EventHandler(this.button1_Click);
上面代码中,使用“new System.EventHandler(this.button1_Click)”将一个System.EventHandler委托类型的实例指向了this.button1_Click方法。通过6.1.1一节中所谈到的内容,我们知道,如果想让一个委托指向一个方法,那么该委托以及所被指向的方法一定要具备相同的签名(Signature,具备相同的参数列表,相同的返回值)。因此,System.EventHandler类型和this.button1_Click方法具备相同的签名,下面,我们来看一下System.EventHandler委托的签名是什么样的:
public delegate void EventHandler(
Object sender,
EventArgs e
)
System.EventHandler的签名是:返回值为void;有两个参数,Object sender, EventArgs e。因此button1_Click方法也具备相同形式,代码如下:
private void button1_Click(object sender, EventArgs e)
{
}
实际上,我们所能够看到的事件回调方法的签名基本上都着不多,只不过第二个参数略有区别,下面,我们对该方法的参数进行说明。
 Object sender
从该参数的命名上,可以看出其作用,sender(发送者)的意思是:谁触发的这个事件,那么sender就是谁,由于所有的类型在理论上讲都可以包括事件,因此sender的类型被定义成Object类型,当多个事件同时指向一个事件回调方法的时候,通过该参数可以区分出是哪一个类触发的事件,以便做出不同的处理,此时,需要对参数sender作出类型转化。
 案例操作020603:多个事件指向同一个回调方法
首先,添加三个Button,一个TextBox
界面如下:

然后,在主窗口中添加一个方法ButtonClick,这三个按钮的Click事件将调用该方法。
代码如下:
protected void ButtonClick(object sender, EventArgs e)
{
Button bt = sender as Button;
this.textBox1.Text ="我是:"+ bt.Text;
}
上面代码中,为了知道点击的是哪个按钮,我们将sender转化成了Button类型。
下面来指定这三个按钮的Click事件回调方法
首先,切换到button1的属性窗口(F4),点击“ ”按钮,找到“Click”事件,并设置所调用的方法名为ButtonClick ,如下图所示。

然后,以相同的方法设置button2,button3的Click事件,并它们都指向ButtonClick方法。
最后,运行程序,下面是运行情况:
点击button1:

点击button2:

点击button3:

 EventArgs e
EventArgs类型是事件参数,当事件发生时,可以通过该参数来传递一些数据。当然EventArgs类本身是传递不了什么数据的,该类的类视图如下:

从类视图中不难发现,该类中的成员很少,通常情况下,如果想传递数据,那么事件参数类一般会是一个EventArgs类的派生类。比如TextBox类的KeyDown事件中事件参数就是一个KeyEventArgs类型的参数,事件回调方法的原型如下:
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
}
KeyEventArgs类的类视图如下所示:

该类中有三个属性:“Alt”,“Control”,“Shift”分另用来表示按下键盘某一个键的同时,有没有按下这三个功能键。
另外“KeyCode”属性可以用来表示当前用户所按的键位名。
在下一节中,我们会说明如何编写自定义事件参数。
 案例操作020604:利用TextBox的KeyDown事件来模拟QQ聊天窗口
新建一个Windows窗口,包括以下控件
一个RichTextBox控件(rtbMessage):用来显示聊天信息
一个TextBox控件(tbxInput):用来输入聊天信息
一个Button控件( btSubmit):用来提交
界面如下所示:

功能如下:
点击button可以让消息传递到上面的RichTextBox中。当然,如果按“Ctrl+Enter”也可以使文字传递到RichTextBox中。
首先,我们在Windows窗口中添加一个方法,代码如下:
public void Display()
{
this.rtbMessage.AppendText(this.tbxInput .Text +"n");
this.tbxInput.Text = "";
}
该方法的功能就是将文本框中的文字添加到RichTextBox中。
下一步,编写Button的Click事件,代码如下:
private void btSubmit_Click(object sender, EventArgs e)
{
this.Display();
}
下一步编写TextBox的KeyDown事件,代码如下:
private void tbxInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control ==true&&e.KeyCode .ToString ()=="Return")
{
this.Display();
}
}
该方法在执行前先检测用户是否同时按下的Control和回车键,然后再进行显示操作。
程序运行的效果如下:

按下Control+回车之后,文字上屏,效果如下:

c#委托与事件问题

20
[ 标签:c#委托,事件问题 ]
委托和时间不怎么太明白。我看书上讲怎么声明委托的时候能看明白。可是书上讲到如何实例化委托使用场合的时候就不明白了。声明的委托,在类里面的委托的参数与声明的不一样。很是纠结。求高手能给我详细的讲解委托的使用。
. 回答:10 人气:10 解决时间:2011-08-09 07:52
满意答案
好评率:0%
委托是C#中事件机制的核心,网上常常将委托神话了。将其比作一个门槛,过了的人很欢喜,没过的人在门外干着急,很无语。
本文将与你一同探讨委托的实现和观察者(Observer)设计模式的关系。
在学C#的时候,委托对于我就是一道门槛,而且是很高的门槛。我好几次都搁在门槛上,摔得头破血流。很久以来都对它产生恐惧感。网上文章看了不少,理解起来生硬费力,不是人家讲的不好,而是自己太浅薄了。而网上文章多以实例配合事件机制讲解,对于C++的程序员来说可能更好理解,说白了委托就是一种类似指针的东西。
在学习委托之前,我们来了解一下观察者设计模式,委托和时间很好的实现了观察者设计模式。举个在head first中的例子,然后再举个天气预告板的例子将它改造成C#中基于委托来实现。
出版社要出版一个杂志,为了更好适应这个市场,出版社是花样百出,结果很多读者都来订阅这个杂志了。我们把出版社看做是一个主题或者被观察者,读者看做观察者。当出版社更新杂志时,读者将得到通知,以获取最新的内容。因为杂志办的越来越火,因此吸引了越来越多的读者,新的读者想要获得杂志,那么必须向出版社订购,这个过程我们理解为注册。后来由读者A不满意了,就不想订购了,那么就提起撤销申请,接着出版社就把该读者A名单移除了,当新的一期杂志出炉时,就不会再通知该读者A来获得信息。一旦读者A无聊又想要杂志了,那么他必须重新订购才行。
从上面的例子中我们看出,出版社(主题或被观察者)有几个动作,接受一个读者的订购请求,移除一个读者,给每个读者都分发最新的杂志。
下面的代码演示一个没有基于委托的时候我们该怎么实现:
public class Public Publisher {
List scribers; //保存新增的读者
    public Publisher() {
      this.scribers = new ArrayList();
    }
    public void registerScriber(Scriber sc) {
      scribers.add(sc);
    }
    public void removeScriber(Scriber sc) {
      int index = scribers.indexof(sc);
      if (index > 0)
        scribers.remove(sc);
    }
//当有新的杂志更新时将执行
    public void newMagazine() {
for (int i = 0; i < scribers.length; i++) {
        Scriber sc = (Scriber)scribers.get(i);
        sc.update();
}
}
}
public class Scriber {
  //包含一个指向主题的引用
  private Publisher pub;
  public Scriber(Publisher pub) {
    this.pub = pub;
    pub.registerScriber(this);//注册
  }
  public void update() {
    Sysout.out.println("您有新的杂志啦!!");
  }
}
public class Test{
  public static void main(String[] args) {
    Publisher pub = new Publisher();
    Scriber sc = new Scriber(pub);
    pub.newMagazine();
  }
}
/*output:
您有新的杂志啦!!
*/
通过上面的一个读者和出版社的例子演示了一个简单的实现观察者模式。当新的杂志出来了,将通知每个读者,我们可以定义多个读者,也就是说出版社和读者的关系式1对多的关系。理解了观察者模式,
lasic
2012-04-11 · TA获得超过374个赞
知道小有建树答主
回答量:460
采纳率:100%
帮助的人:405万
展开全部
public delegate void MyEventHandler(object sender,EventArgs e);

public class A{
public event MyEventHandler MyEvent;

private void DoEvent
{
if(MyEvent!=null)
MyEvent(this,null);
}

public class B
{
private A ;
public B()
{
A a=new A();
a.MyEvent+=new MyEventHandler(a_MyEvent);
}

private void a_MyEvent(object sender,EventArgs e)
{
//***************添加自己的处理逻辑
}

}
}

以上代码实现A中自定义事件,在私有方法DoEvent中触发该事件,
B中包含对A实例的引用,并订阅该事件。则当a触发该事件时,B的实例将执行a_MyEvent(object sender,EventArgs e)方法。a需要传过去的事件参数就在触发该事件时的MyEvent(this,null)来实现。

纯手写,未排版。
本回答被网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
styshoo1986
2012-04-11 · TA获得超过893个赞
知道小有建树答主
回答量:832
采纳率:100%
帮助的人:262万
展开全部
winform中的按钮点击,form面加载,都是事件处理的范例,而且绝对专业。
可以自己试试,理解的更深刻。
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
收起 更多回答(1)
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式