附加事件概觀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.

PrerequisitesPrerequisites

本主題假設您已閱讀路由事件概觀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,更輕鬆地在任何指定的 UIElement 上處理,而不是在 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. 更常見的是,事件的目的是要在複合 (compositing) 中啟用報告狀態給父項目的複合項目,在此情況下,事件通常會在程式碼中引發,並依賴相關父類別中的類別處理。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 附加事件的「加入事件名稱」處理常式,包含以路由事件和處理常式做為引數呼叫 AddHandlerThe 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.

例如,下列程式碼會使用將附加事件宣告為路由事件的 WPF 附加事件策略,在 owner 類別 Aquarium上定義 NeedsCleaning 附加事件。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