WPF-11 路由事件之一

什么是路由事件?

我们从两个维度来理解路由事件:

功能的角度来看,路由事件是一种事件类型,不仅仅可以在事件源上处理事件响应,还可以在元素树的多个侦听器上处理事件响应(事件侦听器是附加和调用事件处理程序的元素。事件源是最初引发事件的元素或对象)

实现的角度来看,路由事件是使用 CLR 事件“包装器”实现的,由 RoutedEvent 类的实例支持,并由 WPF 事件系统处理。.

根据路由事件的定义方式,当事件在源元素上引发时:

  1. 从源元素到根元素(通常是页面或窗口)通过元素树冒泡
  2. 从根元素到源元素通过元素树向下隧道
  3. 不遍历元素树,只出现在源元素上
<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">    <StackPanel Background="LightBlue" Orientation="Horizontal"                       Button.Click="StackPanel_Click">       <Button Name="YesButton">YES</Button>       <Button Name="NoButton">No</Button>       <Button Name="CancelButton">Cancel</Button>     </StackPanel></Border>

示例中的这三个Button都有自己Click事件,当触发一个button的Click事件时,Button元素的Click事件沿着树传播到根节点,Button和Border没有响应时间处理程序,但是StackPanel会响应。在这个例子中事件传播:Button -> StackPanel -> Border -> 父元素

如何实现路由事件?

我们可以通过WPF框架提供的接口来注册路由事件,由 RoutedEvent 类的实例支持。从注册中获得的 RoutedEvent 实例通常存储为注册它的类的公共静态只读成员。该类称为事件“所有者”类。通常,路由事件是对同名 CLR 事件的“包装器”。CLR 事件包装器包含添加和删除访问器,以便通过特定于语言的事件语法在 XAML 和代码隐藏中附加处理程序。add 和 remove 访问器覆盖其 CLR 实现并调用路由事件 AddHandler 和 RemoveHandler 方法。路由事件机制类似于依赖属性的机制。

以下示例注册 Tap 路由事件,存储返回的 RoutedEvent 实例,并实现 CLR 事件包装器。

// Register a custom routed event using the Bubble routing strategy.public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(    name: "Tap",    routingStrategy: RoutingStrategy.Bubble,    handlerType: typeof(RoutedEventHandler),    ownerType: typeof(CustomButton));   // Provide CLR accessors for adding and removing an event handler.public event RoutedEventHandler Tap{    add { AddHandler(TapEvent, value); }    remove { RemoveHandler(TapEvent, value); }}

路由策略

路由事件使用以下三种路由策略之一:

冒泡:最初,事件源上的事件处理程序被调用。路由事件然后路由到连续的父元素,依次调用它们的事件处理程序,直到它到达元素树根。大多数路由事件使用冒泡路由策略。冒泡路由事件通常用于报告来自复合控件或其他 UI 元素的输入或状态更改。

隧道:最初,调用元素树根处的事件处理程序。路由事件然后路由到连续的子元素,依次调用它们的事件处理程序,直到它到达事件源。遵循隧道路由的事件也称为预览事件。WPF 输入事件通常实现为预览和冒泡对。

直接:仅调用事件源上的事件处理程序。这种非路由策略类似于标准 CLR 事件的 Windows 窗体 UI 框架事件。与 CLR 事件不同,直接路由事件支持类处理并且可由 EventSetters 和 EventTriggers 使用。

为什么用路由事件

软件开发人员并需要知道你正在处理的事件是路由事件,路由事件本身具有自己的特性,如果你正在处理元素自身引发的事件,则该行为是不可用的。但是,如果你将事件附加到父元素或者子元素 ,这时路由事件就体现了它自己的特性。

路由事件支持沿事件路由的元素之间的事件信息交换,因为每个侦听器都可以访问相同的事件数据实例。如果一个元素更改了事件数据中的某些内容,则该更改对事件路由中的后续元素可见。 WPF 样式和模板功能(例如 EventSetters 和 EventTriggers)要求引用的事件是路由事件。