WPF原理剖析——路由事件
一、路由事件与传统事件
传统事件的触发者和处理者是紧密相连的,而路由事件则不是,路由事件允许一个元素的事件有另外的元素触发。
也即就是说路由事件的拥有者和响应者之间没有显示的订阅关系。事件的拥有者只负责激发事件,事件将由谁处理他们并不知道(可以只引发事件没有响应,但是如果这个在传统事件里面的话是会产生异常的。),
1、事件触发者

//这个类负责事件的定义和触发
class MailManager
{
//第二步:定义事件成员
public event EventHandler<NewMailEventArgs> NewMail;
//第三步:定义一个负责引发事件的方法通知已经登记的对象
protected virtual void OnNewMail(NewMailEventArgs e)
{
// if (NewMail != null)//如果我们没有在引发事件的时候订阅事件,会产生异常(未将对象的引用设置到实例)
NewMail(this, e);
}
//第四步:将输入转化为期望事件,这里其实是对第三步的一个包装
public void RaiseEvent(string from, string to, string subject)
{
NewMailEventArgs e = new NewMailEventArgs(from, to, subject);
OnNewMail(e);
}
}

2、事件响应者

class Program
{
static void Main(string[] args)
{
MailManager mailManager = new MailManager();
//订阅事件
//mailManager.NewMail += new EventHandler<NewMailEventArgs> (mailManager_NewMail);//假设我们在引发事件之前没有订阅事件(相当于实例化一个事件)
//调用方法引发事件
mailManager.RaiseEvent("a", "b", "事件邮件");
Console.ReadKey();
}
//处理事件
static void mailManager_NewMail(object sender, NewMailEventArgs e)
{
Console.WriteLine("收到一封主题为{0},是从{1}到{2}", e.Subject, e.From, e.To);
}
}

大家可以看到我在编写传统事件的时候在事件响应者中去掉了事件的订阅,然后我直接引发了事件,这样就会产生一个“未将对象引用设置到对象的实例”,因为我们的事件是一个用Event修饰委托,而委托又会在编译的时候生成一个类。当我们没有事件订阅者的时候相当于,我们声明了一个类,但是没有创建他的实例,就开始调用它的实例方法。
例如我们有一个类A,里面包含一个方法Add(),
public A a;//声明类A
//a=new A();//这里我们直接省去这一步(即在事件过程中缺少事件的订阅)
a.Add();//这里我们调用Add方法的时候就会产生异常
通过上面的例子大家肯定明白了路由事件的触发者和响应者之间没有显示的订阅关系,这句话。
二、路由事件的注册(例子来源深入浅出WPF)
第一步:创建一个继承至RoutedEventArgs类的路由事件参数类,以便传递给事件响应着一些参数信息;如果我们没有什么其他额外信息需要传递的话,我们可以直接使用RoutedEventArgs这个路由事件参数基类。
public class ReportTimeEventArgs:RoutedEventArgs
{
public DateTime ClickTime { get; set; }
public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
: base(routedEvent, source) { }
}
第二步:注册路由事件,并且添加普通事件包装器和事件激发方法。

/// <summary>
/// 创建一个报时按钮
/// </summary>
public class TimeButton:Button
{
//声明和注册路由事件
public static readonly RoutedEvent ReportTimeEvent=
EventManager.RegisterRoutedEvent("ReportTime",RoutingStrategy.Bubble,typeof(EventHandler<ReportTimeEventArgs>),typeof(TimeButton));
//普通事件包装器
public event RoutedEventHandler ReportTime
{
add{this.AddHandler(ReportTimeEvent,value);}
remove{this.RemoveHandler(ReportTimeEvent,value);}
}
//激发路由事件,借用Click事件的激发方法
protected override void OnClick()
{
base.OnClick();
ReportTimeEventArgs args=new ReportTimeEventArgs (ReportTimeEvent,this);//这个事件参数初始化的时候包含的参数提供了事件的来源
args.ClickTime=DateTime.Now;
this.RaiseEvent(args);//激发事件,这里和普通事件不同(普通事件激发是有事件包装器激发的)
}
}

Note:
1、路由事件注册方法RegisterRoutedEvent方法和注册依赖属性的方法很类似,他需要4个参数,普通事件包装器的名字、路由事件策略、事件处理类型、事件拥有者;
2、事件包装器,这里的目的主要是为了适应以前学习.net同学的方便这样我们就可以使用+=或者-=,我们其实可以直接调用AddHandler或者RemoveHandler方法来添加和删除事件;
3、激发事件方法调用的是RaiseEvent(),这个方法是在UIElement类中定义的,即所有UI元素都有这个方法。
三、路由事件的策略
1、Bubble(冒泡模式):事件从自己激发一直传递到根元素;
2、Tunnel(隧道模式):事件从根元素传递到自己;
3、Direct(直接模式):和传统事件一样。

<Window Name="wimdow1">
<Grid x:Name="grid1" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="grid2" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="grid3" local:TimeButton.ReportTime="ReportTimeHandler">
<StackPanel x:Name="stackPanel" local:TimeButton.ReportTime="ReportTimeHandler">
<ListBox x:Name="listBox"/>
<local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="报时" local:TimeButton.ReportTime="ReportTimeHandler" ></local:TimeButton>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>

上面代码我为每一个空间都设置了对TimeButton事件的侦听;所以当事件在上面传递的时候,都可以处理。下面是三种模式的输出结果:
Bubble(冒泡)
Tunnel(隧道)

Direct(直接)

四、路由事件中的几个注意字段
sender:这个是事件处理器中方法的触发源控件,他是当前控件而不是事件的最开始的发生源控件;
OriginalSource:这个是VisualTree树上的最开始发生源控件;
Source:这个是LogicTree树上的最开始发生源的控件。
五、附加事件
其实在上面我们就使用了附加事件,例如我们在给除TimeButton控件之外的空间都附加上了TimeButton控件的ReportTime事件;这样子的一个好处可以是我们统一处理一些控件的事件,例如我们一个stackpanel包含了多个button控件;这时候我们就可以将他们的click事件统一由stackpanel控件来处理,但是stackPanel又没有click事件,这时候我们就可以使用button.Click=""附加给stackpanel控件。其实这里也是WPF中路由事件的一种应用。我们不需要为每一个button控件都包含click事件的处理器,而可以统一由路由事件经过的visualtree上其他元素来处理。
WPF原理剖析——路由事件的更多相关文章
- WPF 学习笔记 路由事件
1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向 ...
- WPF学习之路由事件
原文:http://www.cnblogs.com/lxy131/archive/2010/08/10/1796754.html WPF中新添加了一种事件---路由事件 路由事件与一般事件的区别在于: ...
- WPF中的路由事件(转)
出处:https://www.cnblogs.com/JerryWang1991/archive/2013/03/29/2981103.html 最近因为工作需要学习WPF方面的知识,因为以前只关注的 ...
- WPF中自定义路由事件
public class MyButtonSimple: Button { // Create a custom routed event by first registering a RoutedE ...
- WPF 中的 路由事件
public class ReportTimeEventArgs:RoutedEventArgs { public ReportTimeEventArgs(RoutedEvent routedEven ...
- WPF手动触发路由事件
MouseButtonEventArgs args = , MouseButton.Left); args.RoutedEvent = UIElement.MouseLeftButtonDownEve ...
- WPF教程六:理解WPF中的隧道路由和冒泡路由事件
WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇开始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由. 事件最基本的用法 ...
- .NET: WPF 路由事件
(一)使用WPF内置路由事件 xaml: <Window x:Class="WpfApplication1.MainWindow" xmlns="http://sc ...
- 【WPF】路由事件
总结WPF中的路由事件,我将学到的内容分为四部分来逐渐掌握 第一部分:wpf中内置的路由事件 以Button的Click事件来说明内置路由事件的使用 XAML代码: <Window x:Clas ...
随机推荐
- 禅道项目管理软件-Linux上一键安装
一.安装 1.将安装包直接解压到/opt目录下 特别说明:不要解压到别的目录再拷贝到/opt/,因为这样会导致文件的所有者和读写权限改变,也不要解压后把整个目录777权限. 可以使用命令: tar - ...
- YsoSerial 工具常用Payload分析之URLDNS
本文假设你对Java基本数据结构.Java反序列化.高级特性(反射.动态代理)等有一定的了解. 背景 YsoSerial是一款反序列化利用的便捷工具,可以很方便的生成基于多种环境的反序列化EXP.ja ...
- Echarts入门踩坑记录
关于Echarts,官网上,是这样介绍的,"Echarts,一个使用JavaScript实现的开源可视化库",也就是说,在使用过程中,将其作为普通的JavaScript组件库使用即 ...
- 【爬虫系列】0. 无内鬼,破解前端JS参数签名
PS:这是一个系列,坐等我慢慢填坑. PS:不太会直接能跑的代码,抛砖引玉. PS:那些我也不太熟练的就不搞了,包括(破滑块.验证码..) PS: 反编译搞Apk会有很长的几个文章,稍后慢慢更. 最近 ...
- spring web.xml 标签<param-name>contextConfigLocation</param-name>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</lis ...
- GlassFish 任意文件读取漏洞
poc https://192.168.49.2:4848/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0 ...
- php预定义常量运用
当前url 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; 当前文件路径 dirname(__FILE__)header(" ...
- 使用递归计算1~n之间所有整数的和
5+getSum(4) 5+4+getSum(3) 5+4+3+getSum(2) 5+4+3+2+getSum(1) 5+4+3+2+1 function getSum(n){ if(n===1){ ...
- ECDSA高性能硬件实现——算法详解与模块划分
ECDSA全称椭圆曲线数字签名算法,它是基于素数域的椭圆曲线对信息进行加签与验签.其核心在于对信息的加签,及对加签的信息进行验签,那么下面介绍该算法流程. 假设Alice希望对消息m进行签名,并将消息 ...
- Flutter学习(7)——网络请求框架Dio简单使用
原文地址: Flutter学习(7)--网络请求框架Dio简单使用 | Stars-One的杂货小窝 Flutter系列学习之前都是在个人博客发布,感兴趣可以过去看看 网络请求一般APP都是需要的,在 ...