路由事件是具有更强传播能力的事件,它们可在元素树中向上冒泡和向下隧道传播,并且沿着传播路径被事件处理程序处理。
理解路由事件当有意义的事情发生时,由对象发送的用于通知代码的消息。
事件路由允许源自某个元素的事件由另一个元素引发。
比如·来自工具栏按钮的单击事件可在代码处理之前上传到工具栏,然后上传到包含工具栏的窗口。
定义,注册和封装路由事件依赖项属性是使用DependencyProperty.Register方法注册的,路由事件是使用EventManager.RegisterRoutedEvent()方法注册的
注册事件时,需要指定事件的名称,路由类型,定义事件程序程序语法的委托,和拥有的事件类
通常,路由事件通过普通的.Net事件进行封装,从而使所有的.Net语言都能访问它们,事件封装器可使用AddHandler()和RemoveHandler()方法添加和删除已注册的调用程序
例如Button类提供了大家熟悉的Click事件
共享路由事件与依赖项属性一样,可在类之间共享路由事件的定义。比如 UIElement(是普通WPF元素的起点)和Content(该类是所有内容的起点)。两个基类都用了MouseUp事件。
引发路由事件
使用RaiseEvent()方法引发事件
RaiseEvent方法负责为每个已经通过AddHandler方法注册的调用程序引发事件,因为AddHandler方法是公有的,所以调用程序可访问该方法---它们能够通过直接调用AddHandler方法注册自己,也可以使用事件封装器。无论使用哪种办法,当调用RaiseEvent方法是都会通知它们。
事件处理程序的第一个参数sender参数都提供引发该事件的对象引用。第二个参数是EventArgs对象,该对象与其他所有可能很重要的附加细节绑定在一起。MouseUp事件提供了一个MouseEventAgrs对象,用于指示当事件发生时按下哪些鼠标键
在WPF中,如果事件不需要传递任何额外细节,可使用RoutedEventArgs类,该类包含了有关如何传递事件的一些细节,如果事件确实需要传递额外的信息,那么需要使用更特殊的继承自RoutedEventArgs的对象。
处理路由事件可用多种方法关联事件处理程序。最常用的方法是为XAML标记添加事件特性。
也可以使用代码连接事件
上面的代码创建了一个针对该事件具有正确签名的委托对象,并将该委托指向img_MouseUp方法。然后将该委托添加到img.MouseUp事件的已注册的事件程序程序列表中
也可以自行通过UIElement.AddHandler方法直接连接事件
使用这种办法,始终需要创建合适的委托类型,而不能隐式地创建委托对象,这是因为UIElement.AddHandler方法支持所有的WPF事件,并且它不知道你想使用的委托类型
或者使用:
断开事件处理程序
事件路由比如:
对每个元素的MouseDown和MouseUp事件关联同一个事件处理程序,这样会使得标记变得杂乱无章难以维护,WPF使用路由事件模型提供了良好的解决方案
- 直接路由事件,源于一个元素,不传递给其他元素。比如:MouseEnter事件(鼠标移动到元素上发生)是直接路由事件
- 冒泡路由事件,该事件被单击的元素引发,接下来被该元素的父元素引发,直到元素树的顶端
- 隧道路由事件,隧道路由事件给到达恰当的控件之前为预览事件提供了机会。比如PreviewKeyDown事件可截获是否按下了某个键。首先在窗口级别,然后是具体的容器,直到到达当按下键时具有焦点的元素
当使用EventManager.RegisterEvent方法注册路由事件时,需要传递一个RoutingStrategy枚举值,该值由于指示希望应用于事件的事件行为。
RoutedEventArgs类有些情况下,可能希望确定事件最初发生的位置。可从RoutedEventArgs类的属性获取这一信息以及其他细节
处理挂起的事件有一个方法可接受被标记为处理过的事件。AddHandler方法提供了一个重载版本,可以用
附加事件按钮有Click事件,而其他基类没有定义这个控件
假设在StackPanel面板中封装了一堆按钮,并希望在一个事件处理程序中处理所有这些按钮的单击事件。以下代码是不可用的,因为StackPanel没有Click事件。
<StackPanel Click="Dosomething" Margin="5"> <Button Name="cmd1">Command 1</Button> <Button Name="cmd2">Command 2</Button> <Button Name="cmd3">Command 3</Button> </StackPanel>
解决办法是:
<StackPanel Button.Click="Dosomething" Margin="5"> <Button Name="cmd1">Command 1</Button> <Button Name="cmd2">Command 2</Button> <Button Name="cmd3">Command 3</Button> </StackPanel>
可在代码中关联附加事件,但需要使用UIElement.AddHandler方法
为了确定是谁引发
或者是对每个按钮设置Tag
隧道路由事件
隧道路由事件易于识别,都以单词Preview开头,而且WPF通常成对地定义冒泡路由事件和隧道路由事件。如果把隧道路由事件标记已经处理,就不会发生冒泡路由事件
如果需要执行一些预处理(根据键盘上特定的键执行动作或过滤掉特定的鼠标动作),隧道事件还是很有用的