附加事件概述Attached Events Overview

Extensible Application Markup Language (XAML) 定义称为附加事件的语言组件和事件类型。Extensible Application Markup Language (XAML) defines a language component and type of event called an attached event. 通过附加事件的概念,你能够向任意元素(而不是实际定义或继承事件的元素)添加特定事件的处理程序。The concept of an attached event enables you to add a handler for a particular event to an arbitrary element rather than to an element that actually defines or inherits the event. 在这种情况下,对象既不会引发事件,目标处理实例也不会定义或“拥有”事件。In this case, neither the object potentially raising the event nor the destination handling instance defines or otherwise "owns" the event.

系统必备Prerequisites

本主题假定你已阅读路由事件概述XAML 概述 (WPF)This topic assumes that you have read Routed Events Overview and XAML Overview (WPF).

附加事件语法Attached Event Syntax

附加事件具有 XAML 语法和编码模式, 支持代码必须使用它才能支持附加的事件使用。Attached events have a XAML syntax and a coding pattern that must be used by the backing code in order to support the attached event usage.

在 XAML 语法中, 附加事件不只是通过其事件名称指定, 而是由其所属类型和事件名称指定, 用点 (.) 分隔。In XAML syntax, the attached event is specified not just by its event name, but by its owning type plus the event name, separated by a dot (.). 因为事件名称是使用具有其所属类型的名称限定的,所以附加事件语法允许将任何附加事件附加到可以实例化的任何元素。Because the event name is qualified with the name of its owning type, the attached event syntax allows any attached event to be attached to any element that can be instantiated.

例如, 下面是用于为自定义NeedsCleaning附加事件附加处理程序的 XAML 语法:For example, the following is the XAML syntax for attaching a handler for a custom NeedsCleaning attached event:

<aqua:Aquarium Name="theAquarium" Height="600" Width="800" aqua:AquariumFilter.NeedsCleaning="WashMe"/>

请注意 aqua: 前缀;该前缀在本例中是必需的,因为附加事件是来自自定义映射 xmlns 的自定义事件。Note the aqua: prefix; the prefix is necessary in this case because the attached event is a custom event that comes from a custom mapped xmlns.

WPF 如何实现附加事件How WPF Implements Attached Events

在 WPF 中, 附加事件由RoutedEvent字段支持, 并在引发后通过树进行路由。In WPF, attached events are backed by a RoutedEvent field and are routed through the tree after they are raised. 通常,附加事件的源(引发该事件的对象)是系统或服务源,所以运行引发该事件的代码的对象并不是元素树的直接组成部分。Typically, the source of the attached event (the object that raises the event) is a system or service source, and the object that runs the code that raises the event is therefore not a direct part of the element tree.

附加事件的方案Scenarios for Attached Events

在 WPF 中, 附加事件存在于某些功能区域, 其中存在服务级别抽象, 例如, 对于由静态MouseValidation或类启用的事件。In WPF, attached events are present in certain feature areas where there is service-level abstraction, such as for the events enabled by the static Mouse class or the Validation class. 与该服务交互或使用该服务的类可以在附加事件语法中使用该事件,也可以选择将附加事件作为路由事件来呈现,这是类如何集成服务功能的一部分。Classes that interact with or use the service can either use the event in the attached event syntax, or they can choose to surface the attached event as a routed event that is part of how the class integrates the capabilities of the service.

虽然 WPF 定义了大量附加事件, 但使用或直接处理附加事件的方案非常有限。Although WPF defines a number of attached events, the scenarios where you will either use or handle the attached event directly are very limited. 通常情况下, 附加事件为体系结构目的提供服务, 但随后会转发到非附加 (使用 CLR 事件 "包装") 路由事件。Generally, the attached event serves an architecture purpose, but is then forwarded to a non-attached (backed with a CLR event "wrapper") routed event.

例如Mouse.MouseDown , 可以UIElement通过使用MouseDownUIElement更轻松地处理基础附加事件, 而不是在 XAML 或代码中处理附加事件语法。For instance, the underlying attached event Mouse.MouseDown can more easily be handled on any given UIElement by using MouseDown on that UIElement rather than dealing with attached event syntax either in XAML or code. 附加事件用于体系结构,因为它允许进一部扩展输入设备。The attached event serves a purpose in the architecture because it allows for future expansion of input devices. 假设的设备只需引发Mouse.MouseDown才能模拟鼠标输入, 而无需从Mouse派生。The hypothetical device would only need to raise Mouse.MouseDown in order to simulate mouse input, and would not need to derive from Mouse to do so. 但是, 此方案涉及事件的代码处理, 附加事件的 XAML 处理与此方案无关。However, this scenario involves code handling of the events, and XAML handling of the attached event is not relevant to this scenario.

在 WPF 中处理附加事件Handling an Attached Event in WPF

处理附加事件的过程以及将要编写的处理程序代码与路由事件的基本相同。The process for handling an attached event, and the handler code that you will write, is basically the same as for a routed event.

通常, WPF 附加事件与 WPF 路由事件并不完全相同。In general, a WPF attached event is not very different from a WPF routed event. 不同之处在于, 事件的来源以及类作为成员公开的方式 (这也会影响 XAML 处理程序语法)。The differences are how the event is sourced and how it is exposed by a class as a member (which also affects the XAML handler syntax).

但正如前文所述, 现有 WPF 附加事件并不是特别适用于 WPF 中的处理。However, as noted earlier, the existing WPF attached events are not particularly intended for handling in WPF. 更多情况下,该事件用于在复合时,使复合元素能够向父元素报告状态,在这种情况下,事件通常用代码引发,同时依赖于相关父类中的类处理。More often, the purpose of the event is to enable a composited element to report a state to a parent element in compositing, in which case the event is usually raised in code and also relies on class handling in the relevant parent class. 例如, 中的Selector项应引发附加Selected事件, 该类Selector随后由类进行处理, 并可能由Selector类转换为其他路由事件, SelectionChanged.For instance, items within a Selector are expected to raise the attached Selected event, which is then class handled by the Selector class and then potentially converted by the Selector class into a different routed event, SelectionChanged. 有关路由事件和类处理的详细信息,请参阅将路由事件标记为“已处理”和“类处理”For more information on routed events and class handling, see Marking Routed Events as Handled, and Class Handling.

将自己的附加事件定义为路由事件Defining Your Own Attached Events as Routed Events

如果是从公共 WPF 基类派生的, 则可以通过在类中包含某些模式方法, 并使用基类中已经存在的实用工具方法来实现自己的附加事件。If you are deriving from common WPF base classes, you can implement your own attached events by including certain pattern methods in your class and by using utility methods that are already present on the base classes.

模式如下:The pattern is as follows:

  • 方法添加包含两个参数的 事件名称处理程序A method AddEventNameHandler with two parameters. 第一个参数是要向其添加事件处理程序的实例。The first parameter is the instance to which the event handler is added. 第二个参数是要添加的事件处理程序。The second parameter is the event handler to add. 方法必须是public和, static并且没有返回值。The method must be public and static, with no return value.

  • 使用两个参数的方法__删除事件名称处理程序__。A method RemoveEventNameHandler with two parameters. 第一个参数是从中删除事件处理程序的实例。The first parameter is the instance from which the event handler is removed. 第二个参数是要删除的事件处理程序。The second parameter is the event handler to remove. 方法必须是public和, static并且没有返回值。The method must be public and static, with no return value.

当在元素上声明附加事件处理程序属性时, __Add名称程序处理程序__方法可简化 XAML 处理。The AddEventNameHandler accessor method facilitates XAML processing when attached event handler attributes are declared on an element. "添加事件处理程序" 和 "__删除事件名称" 处理程序__方法还允许代码访问附加事件的事件处理程序存储区。The AddEventNameHandler and RemoveEventNameHandler methods also enable code access to the event handler store for the attached event.

此常规模式尚未精确地用于框架中的实际实现, 因为任何给定的 XAML 读取器实现可能都有不同的方案用于标识支持语言和体系结构中的基础事件。This general pattern is not yet precise enough for practical implementation in a framework, because any given XAML reader implementation might have different schemes for identifying underlying events in the supporting language and architecture. 这是 WPF 将附加事件作为路由事件实现的原因之一;要用于事件的标识符 (RoutedEvent) 已由 WPF 事件系统定义。This is one of the reasons that WPF implements attached events as routed events; the identifier to use for an event (RoutedEvent) is already defined by the WPF event system. 此外, 路由事件是对附加事件的 XAML 语言级概念的自然实现扩展。Also, routing an event is a natural implementation extension on the XAML language-level concept of an attached event.

WPF 附加事件的 "__添加事件名称" 处理程序__实现包括AddHandler使用路由事件和处理程序作为参数调用。The AddEventNameHandler implementation for a WPF attached event consists of calling the AddHandler with the routed event and handler as arguments.

通常, 此实现策略和路由事件系统会将附加事件的处理限制为UIElement派生类或ContentElement派生类, 因为只有这些类具有AddHandler实现。This implementation strategy and the routed event system in general restrict handling for attached events to either UIElement derived classes or ContentElement derived classes, because only those classes have AddHandler implementations.

例如, 下面的代码使用将附加NeedsCleaning事件声明为路由事件的AquariumWPF 附加事件策略来定义所有者类的附加事件。For example, the following code defines the NeedsCleaning attached event on the owner class Aquarium, using the WPF attached event strategy of declaring the attached event as a routed event.

public static readonly RoutedEvent NeedsCleaningEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));
public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
Public Shared ReadOnly NeedsCleaningEvent As RoutedEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AquariumFilter))
Public Shared Sub AddNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub
Public Shared Sub RemoveNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub

请注意, 用于建立附加事件标识符字段RegisterRoutedEvent的方法实际上与用于注册非附加路由事件的方法相同。Note that the method used to establish the attached event identifier field, RegisterRoutedEvent, is actually the same method that is used to register a non-attached routed event. 附加事件和路由事件都已注册到集中式内部存储。Attached events and routed events all are registered to a centralized internal store. 此事件存储实现能够考虑到路由事件概述中介绍的“事件即界面”概念。This event store implementation enables the "events as an interface" conceptual consideration that is discussed in Routed Events Overview.

引发 WPF 附加事件Raising a WPF Attached Event

通常不需要从代码中引发现有的 WPF 定义的附加事件。You do not typically need to raise existing WPF-defined attached events from your code. 这些事件遵循常规 "服务" 概念模型, 服务类 (如InputManager ) 负责引发事件。These events follow the general "service" conceptual model, and service classes such as InputManager are responsible for raising the events.

但是, 如果RoutedEvent要基于在上建立附加事件的 WPF 模型定义自定义附加事件, 则可以使用RaiseEvent从任何UIElementContentElement引发附加事件。However, if you are defining a custom attached event based on the WPF model of basing attached events on RoutedEvent, you can use RaiseEvent to raise an attached event from any UIElement or ContentElement. 如果引发路由事件 (附加或不), 则要求您将元素树中的特定元素声明为事件源;该源将报告为RaiseEvent调用方。Raising a routed event (attached or not) requires that you declare a particular element in the element tree as the event source; that source is reported as the RaiseEvent caller. 服务负责确定将哪个元素报告为元素树中的事件源Determining which element is reported as the source in the tree is your service's responsibility

请参阅See also