WPF基础之路由事件四

WPF 中的附加事件 
XAML 语言还定义了一个名为“附加事件”的特殊类型的事件。使用附加事件,可以将特定事件的处理程序添加到任意元素中。正在处理该事件的元素不必定义或继承附加事件,可能引发这个特定事件的对象和用来处理实例的目标也都不必将该事件定义为类成员或将其作为类成员来“拥有”。

WPF 输入系统广泛地使用附加事件。但是,几乎所有的附加事件都是通过基本元素转发的。输入事件随后会显示为等效的、作为基本元素类成员的非附加路由事件。例如,通过针对该 UIElement 使用 MouseDown(而不是在 XAML 或代码中处理附加事件语法),可以针对任何给定的 UIElement 更方便地处理基础附加事件 Mouse..::.MouseDown。

XAML 中的限定事件名称 
为子元素所引发的路由事件附加处理程序是另一个语法用法,它与类型名称.事件名称 附加事件语法相似,但它并非严格意义上的附加事件用法。可以向公用父级附加处理程序以利用事件路由,即使公用父级可能不将相关的路由事件作为其成员也是如此。请再次考虑下面的示例:

 

在这里,在其中添加处理程序的父元素侦听器是 StackPanel。但是,它正在为已经声明而且将由 Button 类(实际上是 ButtonBase,但是可以由 Button 通过继承来使用)引发的路由事件添加处理程序。Button“拥有”该事件,但是路由事件系统允许将任何路由事件的处理程序附加到任何 UIElement 或 ContentElement 实例侦听器,该侦听器可能会以其他方式为公共语言运行库 (CLR) 事件附加侦听器。对于这些限定的事件属性名来说,默认的 xmlns 命名空间通常是默认的 WPF xmlns 命名空间,但是您还可以为自定义路由事件指定带有前缀的命名空间。

WPF 输入事件 
路由事件在 WPF 平台中的常见用法之一是用于事件输入。在 WPF 中,按照约定,隧道路由事件的名称以单词“Preview”开头。输入事件通常成对出现,一个是冒泡事件,另一个是隧道事件。例如,KeyDown 事件和 PreviewKeyDown 事件具有相同的签名,前者是冒泡输入事件,后者是隧道输入事件。偶尔,输入事件只有冒泡版本,或者有可能只有直接路由版本。在本文档中,当存在具有替换路由策略的类似路由事件时,路由事件主题交叉引用它们,而且托管引用页面中的各个节阐释每个路由事件的路由策略。

实现成对出现的 WPF 输入事件的目的在于,使来自输入的单个用户操作(如按鼠标按钮)按顺序引发该对中的两个路由事件。首先引发隧道事件并沿路由传播,然后引发冒泡事件并沿其路由传播。顾名思义,这两个事件会共享同一个事件数据实例,因为用来引发冒泡事件的实现类中的 RaiseEvent 方法调用会侦听隧道事件中的事件数据并在新引发的事件中重用它。具有隧道事件处理程序的侦听器首先获得将路由事件标记为“已处理”的机会(先是类处理程序,后是实例处理程序)。如果隧道路由中的某个元素将路由事件标记为“已处理”,则会针对冒泡事件发送已经处理的事件数据,而且将不调用为等效的冒泡输入事件附加的典型处理程序。已处理的冒泡事件看起来好像尚未引发过。此处理行为对于控件合成非常有用,因为此时您可能希望所有基于命中测试的输入事件或者所有基于焦点的输入事件都由最终的控件(而不是它的复合部件)报告。作为可支持控件类的代码的一部分,最后一个控件元素靠近合成链中的根,因此将有机会首先对隧道事件进行类处理,或许还有机会将该路由事件“替换”为更特定于控件的事件。

为了说明输入事件处理的工作方式,请考虑下面的输入事件示例。在下面的树插图中,leaf element #2 是先后发生的 PreviewMouseDown 事件和 MouseDown 事件的源。

输入事件的冒泡和隧道
 
事件的处理顺序如下所示:

针对根元素处理 PreviewMouseDown(隧道)。

针对中间元素 1 处理 PreviewMouseDown(隧道)。

针对源元素 2 处理 PreviewMouseDown(隧道)。

针对源元素 2 处理 MouseDown(冒泡)。

针对中间元素 1 处理 MouseDown(冒泡)。

针对根元素处理 MouseDown(冒泡)。

路由事件处理程序委托提供对以下两个对象的引用:引发该事件的对象以及在其中调用处理程序的对象。在其中调用处理程序的对象是由 sender 参数报告的对象。首先在其中引发事件的对象是由事件数据中的 Source 属性报告的。路由事件仍可以由同一个对象引发和处理,在这种情况下,sender 和 Source 是相同的(事件处理示例列表中的步骤 3 和 4 就是这样的情况)。

由于存在隧道和冒泡,因此父元素接收 Source 作为其子元素之一的输入事件。当有必要知道源元素是哪个元素时,可以通过访问 Source 属性来标识源元素。

通常,一旦将输入事件标记为 Handled,就将不进一步调用处理程序。通常,一旦调用了用来对输入事件的含义进行特定于应用程序的逻辑处理的处理程序,就应当将输入事件标记为“已处理”。

对于这个有关 Handled 状态的通用声明有一个例外,那就是,注册为有意忽略事件数据 Handled 状态的输入事件处理程序仍将在其路由中被调用。

通常,隧道事件和冒泡事件之间的共享事件数据模型以及先引发隧道事件后引发冒泡事件等概念并非对于所有的路由事件都适用。该行为的实现取决于 WPF 输入设备选择引发和连接输入事件对的具体方式。实现自己的输入事件是一个高级方案,但是您也可以选择针对自己的输入事件遵循该模型。

一些类选择对某些输入事件进行类处理,其目的通常是重新定义用户驱动的特定输入事件在该控件中的含义并引发新事件。

原文地址:https://www.cnblogs.com/jljxxf/p/2494755.html