附加事件概述Attached Events Overview

可扩展应用程序标记语言 (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 中,附加事件存在于存在服务级别抽象的某些功能区域,例如对于静态Mouse类或Validation类启用的事件。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上面使用MouseDown,而不是在 XAML 或代码中处理附加UIElement的事件语法,可以更轻松地处理基础附加事件。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类可能将其转换为其他路由事件 。 SelectionChangedFor 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. 方法必须为publicstatic,没有返回值。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. 方法必须为publicstatic,没有返回值。The method must be public and static, with no return value.

在元素上声明附加的事件处理程序属性时,__添加事件名称处理程序__访问器方法有助于 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``Aquarium上的附加事件,使用 WPF 附加的事件策略将附加事件声明为路由事件。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.

但是,如果您正在基于基于 附加事件的 WPF 模型定义RoutedEvent自定义附加事件,则可以使用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