WPF--路由事件
Posted X·3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF--路由事件相关的知识,希望对你有一定的参考价值。
WPF--路由事件
本主题描述Windows Presentation Foundation (WPF)中路由事件的概念。本主题定义路由事件术语,描述路由事件如何通过元素树来路由,概述如何处理路由事件,并介绍如何创建您自己的自定义路由事件。
先决条件
本主题假设您对如下内容有基本的了解:公共语言运行库(CLR)、面向对象的编程以及如何用树的概念来说明WPF元素之间的关系。为了按照本主题中的示例操作,您还应当了解可扩展应用程序标记语言(XAML)并知道如何编写非常基本的 WPF 应用程序或页。
什么是路由事件?
可以从功能或实现的角度来考虑路由事件。此处对这两种定义均进行了说明,因为用户当中有的认为前者更有用,而有的则认为后者更有用。
- 功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。
- 实现定义:路由事件是一个CLR事件,可以由RoutedEvent类的实例提供支持并由Windows Presentation Foundation (WPF)事件系统来处理。
典型的WPF应用程序中包含许多元素。无论这些元素是在代码中创建的还是在XAML中声明的,它们都由共同所在的元素树关联起来。根据事件的定义,事件路由可以按两种方向之一传播,但是通常会在元素树中从源元素向上“冒泡”,直到它到达元素树的根(通常是页面或窗口)。如果您以前用过 Dhtml 对象模型,则可能会熟悉这个冒泡概念。
元素树生成类似如下的内容:
在这个简化的元素树中,Click事件的源是某个Button元素,而所单击的Button是有机会处理该事件的第一个元素。但是,如果附加到Button的任何处理程序均未作用于该事件,则该事件将向上冒泡到元素树中的Button父级(StackPanel)。该事件可能会冒泡到Border,然后会到达元素树的页面根(未显示出来)。
换言之,此 Click 事件的事件路由为:Button-->StackPanel-->Border-->...
路由事件的顶级方案
下面简要概述了需运用路由事件的方案,以及为什么典型的CLR事件不适合这些方案:
控件的撰写和封装:WPF中的各个控件都有一个丰富的内容模型。例如,可以将图像放在Button的内部,这会有效地扩展按钮的可视化树。但是,所添加的图像不得中断命中测试行为(该行为会使按钮响应对图像内容的Click),即使用户所单击的像素在技术上属于该图像也是如此
单一处理程序附加点:在Windows窗体中,必须多次附加同一个处理程序,才能处理可能是从多个元素引发的事件。路由事件使您可以只附加该处理程序一次(像上例中那样),并在必要时使用处理程序逻辑来确定该事件源自何处。例如,这可以是前面显示的XAML的处理程序:
类处理:路由事件允许使用由类定义的静态处理程序。这个类处理程序能够抢在任何附加的实例处理程序之前来处理事件。
引用事件,而不反射:某些代码和标记技术需要能标识特定事件的方法。路由事件创建RoutedEvent字段作为标识符,以此提供不需要静态反射或运行时反射的可靠的事件标识技术。
路由事件的实现方式
路由事件是一个CLR事件,它由RoutedEvent类提供支持并用WPF事件系统注册。从注册中获取的RoutedEvent实例通常保留为某种类的public static readonly字段成员,该类进行了注册并因此“拥有”路由事件。与同名CLR事件(有时称为“包装”事件)的连接是通过重写CLR事件的add和remove实现来完成的。通常,add和remove保留为隐式默认值,该默认值使用特定于语言的相应事件语法来添加和移除该事件的处理程序。路由事件的支持和连接机制在概念上与以下机制相似:依赖项属性是一个CLR属性,该属性由DependencyProperty类提供支持并用WPF属性系统注册。
下面的示例演示自定义Tap路由事件的声明,其中包括注册和公开RoutedEvent标识符字段以及对Tap CLR事件进行add和remove实现。
路由事件处理程序和 XAML
若要使用XAML为某个事件添加处理程序,请将该事件的名称声明为用作事件侦听器的元素上的属性。该属性的值是所实现的处理程序方法的名称,该方法必须存在于代码隐藏文件的分部类中。
用来添加标准CLR事件处理程序的XAML语法与用来添加路由事件处理程序的语法相同,因为您实际上是在向下面具有路由事件实现的CLR事件包装中添加处理程序。
路由策略
路由事件使用以下三个路由策略之一:
冒泡:针对事件源调用事件处理程序。路由事件随后会路由到后续的父元素,直到到达元素树的根。大多数路由事件都使用冒泡路由策略。冒泡路由事件通常用来报告来自不同控件或其他UI元素的输入或状态变化。
直接:只有源元素本身才有机会调用处理程序以进行响应。这与Windows窗体用于事件的“路由”相似。但是,与标准CLR事件不同的是,直接路由事件支持类处理(类处理将在下一节中介绍)而且可以由EventSetter和EventTrigger使用。
隧道:最初将在元素树的根处调用事件处理程序。随后,路由事件将朝着路由事件的源节点元素(即引发路由事件的元素)方向,沿路由线路传播到后续的子元素。在合成控件的过程中通常会使用或处理隧道路由事件,这样,就可以有意地禁止显示复合部件中的事件,或者将其替换为特定于整个控件的事件。在 WPF 中提供的输入事件通常是以隧道/冒泡对实现的。隧道事件有时又称作 Preview 事件,这是由隧道/冒泡对所使用的命名约定决定的。
为什么使用路由事件?
作为应用程序开发人员,您不需要始终了解或关注正在处理的事件是否作为路由事件实现。路由事件具有特殊的行为,但是,如果您在引发该行为的元素上处理事件,则该行为通常会不可见。
如果您使用以下任一建议方案,路由事件的功能将得到充分发挥:在公用根处定义公用处理程序、合成自己的控件或者定义您自己的自定义控件类。
路由事件侦听器和路由事件源不必在其层次结构中共享公用事件。任何UIElement或ContentElement可以是任一路由事件的事件侦听器。因此,您可以使用在整个工作API集内可用的全套路由事件作为概念“接口”,应用程序中的不同元素凭借这个接口来交换事件信息。路由事件的这个“接口”概念特别适用于输入事件。
路由事件还可以用来通过元素树进行通信,因为事件的事件数据会永存到路由中的每个元素中。一个元素可以更改事件数据中的某项内容,该更改将对于路由中的下一个元素可用。
之所以将任何给定的WPF事件作为路由事件实现(而不是作为标准CLR事件实现),除了路由方面的原因,还有两个其他原因。如果您要实现自己的事件,则可能也需要考虑这两个因素:
某些WPF样式和模板功能(如EventSetter和EventTrigger)要求所引用的事件是路由事件。前面提到的事件标识符方案就是这样的。
路由事件支持类处理机制,类可以凭借该机制来指定静态方法,这些静态方法能够在任何已注册的实例程序访问路由事件之前,处理这些路由事件。这在控件设计中非常有用,因为您的类可以强制执行事件驱动的类行为,以防它们在处理实例上的事件时被意外禁止。
以上是关于WPF--路由事件的主要内容,如果未能解决你的问题,请参考以下文章