9

.NET简谈路由事件

 3 years ago
source link: https://www.cnblogs.com/wangiqngpei557/archive/2011/07/18/2109666.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

.NET简谈路由事件

本篇文章讲解关于路由事件的相关原理。

什么叫路由事件,字面理解就是事件是可以传递,路由的意思也好理解。路由事件其实就是,事件是会随着某种变化,来回传递。路由事件其实在.NET2.0时期就已经存在了,只不过在一般开发过程中用不到。

从C#3.0开始,就已经封装了关于路由事件的机制。其实这种实现应该可以换个名字来解释。我们可以给路由事件起个便于理解的名字,“事件的路由设计模式”。我们都知道,任何大的框架都是从微小的基本语法开始编写的,平台、语言给我们提供的仅仅是一些能满足日常需求的东西;好东西还得我们自己去写、去创新。在常见的设计模式中,少不了对事件的使用,本人深有体会。是不是高手,不能用他会哪种框架、会哪种语言,而是要看他对他使用的语言所理解程度,能否将一门语言玩的炉火纯青,能否写出高效、简单的框架;这才是高手。这也是很多初学者所喜欢犯的毛病。

路由事件在一些复杂的系统设计中至关重要,比如我有一个对象,这个对象是一个属于容器类的对象,就好比我们Windows应用程序中的Form窗体,这个窗体用来承载一些其他的子窗体。然而这样的递归性的设计,经常性的出现。我们在搭建一个界面时,往这个界面上堆积了很多小的窗口。这些小的窗口又堆积了一些更小的窗口。在设计具有层次性的架构时,我们需要考虑这些对象不能被埋的太深,但是又要保持对象的结构原理,就像下图中所示;

2011071816083678.png

上图可能画的不太形象,能表达意思就行了。有一个大的对象上面堆积了很多小的对象,每个小的对象又堆积了一些小的对象。这样的层次结构,我们经常遇见。在.NET平台上开发,基本上都是基于控件的拖拉进行开发的,但是这些控件都是被封装过的,里面又包含了一些小的对象。在2.0的开发中,控件是不支持事件路由的,比如我们在订阅一个控件的事件时,这个事件可能被它上面的事件所处理了;做WINFORM的朋友经常喜欢捕获鼠标单击事件,然后编写事件触发代码。但是会发现只要这个控件被其他控件挡住了,那这个控件肯定是收不到Windows发给它的鼠标单击消息,因为事件没有路由。上面的父控件没有考虑到它的子孙们需要这个消息,在WPF中就提供了事件路由的机制,我们可以捕获到子控件的事件。

其实实现原理就是将事件向下传递,父控件要循环的判断每一个子控件是否被订阅了相关事件,如果父控件捕获到的这个事件子控件也需要,那么就可以将事件向下路由了;

2011071816091080.png

如果我们需要框架支持路由事件的化,那么我们在前期设计的时候,需要将对象进行提取,对需要路由事件的对象进行基类封装;就好比我们从Control控件的基类开始。

下面我们来看一个小例子,以帮助大家能理解原理,在自己开发项目的时候能用的上。

一:容器对象代码:

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
/// <summary> 
/// 容器类 
/// </summary> 
public class Container 
/// <summary> 
/// 鼠标单击事件         
/// </summary> 
public event EventHandler Click; 
/// <summary> 
/// 子对象集合 
/// </summary> 
private List<Child> childlist = new List<Child>(); 
/// <summary> 
/// 触发当前对象的Click事件 
/// </summary> 
public void OnClick() 
Click("父对象接受到Click事件", null);//触发当前父容器的事件 
foreach (Child c in childlist) 
c.OnClick(); 
/// <summary> 
/// 添加子对象方法 
/// </summary> 
/// <param name="c"></param> 
public void Add(Child c) 
childlist.Add(c); 
}

二:子对象代码:

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
/// <summary> 
/// 子对象 
/// </summary> 
public class Child
{
/// <summary> 
/// 鼠标单击事件 
/// </summary> 
public event EventHandler Click;
public void OnClick()
{
Click("子对象接受到Click事件", null);
}
}
}

三:调用代码:

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//实例化容器对象
Container containerobject = new Container();
//订阅容器Click事件
containerobject.Click += new EventHandler(containerobject_Click);
//实例化子类对象
Child childobject = new Child();
//订阅子类Click事件
childobject.Click += new EventHandler(childobject_Click);
//将子类添加到容器类
containerobject.Add(childobject);
if (Console.ReadLine() == "StartClick")
{
//触发容器类Click事件,这时候事件会路由到子对象中;
containerobject.OnClick();
}
}
/// <summary>
/// 子对象Click事件
/// </summary>
/// <param name="sender">这是从子对象传出来的数据</param>
/// <param name="e"></param>
static void childobject_Click(object sender, EventArgs e)
{
Console.WriteLine((sender as string));
Console.ReadLine();
}
/// <summary>
/// 容器对象Click事件
/// </summary>
/// <param name="sender">这是从容器对象传出来的数据</param>
/// <param name="e"></param>
static void containerobject_Click(object sender, EventArgs e)
{
Console.WriteLine((sender as string));
Console.ReadLine();
}
}
}

最终效果图:

2011071911490849.png

路由事件大概就讲完了,希望能对朋友有所帮助;


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK