路由事件概觀Routed Events Overview

本主題說明 Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) 中路由事件的概念。This topic describes the concept of routed events in Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF). 本主題會定義路由事件的術語、說明如何透過元素的樹狀結構路由傳送路由事件、摘要說明如何處理路由事件,以及介紹如何建立您自己的自訂路由事件。The topic defines routed events terminology, describes how routed events are routed through a tree of elements, summarizes how you handle routed events, and introduces how to create your own custom routed events.

必要條件Prerequisites

本主題假設您具備 common language runtime (CLR) 和麵向物件程式設計的基本知識, 以及如何將元素之間WPFWPF的關聯性概念化為樹狀結構的概念。This topic assumes that you have basic knowledge of the common language runtime (CLR) and object-oriented programming, as well as the concept of how the relationships between WPFWPF elements can be conceptualized as a tree. 若要遵循本主題中的範例,您也應該了解 Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML),並知道如何撰寫非常基本的 WPFWPF 應用程式或頁面。In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML) and know how to write very basic WPFWPF applications or pages. 如需詳細資訊,請參閱逐步解說:我的第一個 WPF桌面應用程式和XAML 總覽 (WPF)For more information, see Walkthrough: My first WPF desktop application and XAML Overview (WPF).

什麼是路由事件?What Is a Routed Event?

您可以從功能或實作觀點來考量路由事件。You can think about routed events either from a functional or implementation perspective. 此處說明這兩個定義,讓使用者能夠選擇較實用的定義。Both definitions are presented here, because some people find one or the other definition more useful.

功能定義:路由事件是一種事件種類, 可以在專案樹狀結構中的多個接聽程式上叫用處理程式, 而不只是在引發事件的物件上叫用。Functional definition: A routed event is a type of event that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event.

執行定義:路由事件是由RoutedEvent類別的實例所支援的 CLR 事件, 並Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF)由事件系統處理。Implementation definition: A routed event is a CLR event that is backed by an instance of the RoutedEvent class and is processed by the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) event system.

一般的 WPFWPF 應用程式包含許多元素。A typical WPFWPF application contains many elements. 不論是在程式碼中建立或是在 XAMLXAML 中宣告,這些元素均存在於彼此間的元素樹狀結構關聯性中。Whether created in code or declared in XAMLXAML, these elements exist in an element tree relationship to each other. 事件路由的周遊方向取決於事件定義,但通常路由會從來源元素開始周遊,然後透過元素樹狀結構向上執行「事件反昇」,直到它到達元素樹狀結構根元素 (通常是頁面或視窗) 為止。The event route can travel in one of two directions depending on the event definition, but generally the route travels from the source element and then "bubbles" upward through the element tree until it reaches the element tree root (typically a page or a window). 如果您先前曾用過 DHTML 物件模型,可能很熟悉這個事件反昇概念。This bubbling concept might be familiar to you if you have worked with the DHTML object model previously.

請考慮下列簡單的元素樹狀結構:Consider the following simple element tree:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

這個元素樹狀結構會產生如下的畫面:This element tree produces something like the following:

[是]、[否] 和 [取消] 按鈕Yes, No, and Cancel buttons

在這個簡化的元素樹狀結構中, Click事件的來源是其中一個Button專案, 而Button按下的是第一個有機會處理事件的元素。In this simplified element tree, the source of a Click event is one of the Button elements, and whichever Button was clicked is the first element that has the opportunity to handle the event. 但是, 如果未附加至事件Button的處理常式, 則事件會向上反升Button至專案StackPanel樹狀結構中的父系, 也就是。But if no handler attached to the Button acts on the event, then the event will bubble upwards to the Button parent in the element tree, which is the StackPanel. 可能的話, 事件會冒泡Border到, 然後再到專案樹狀結構的頁面根目錄 (未顯示)。Potentially, the event bubbles to Border, and then beyond to the page root of the element tree (not shown).

換句話說, 此Click事件的事件路由為:In other words, the event route for this Click event is:

Button-->StackPanel-->Border-->...Button-->StackPanel-->Border-->...

路由事件的最上層案例Top-level Scenarios for Routed Events

以下是引發路由事件概念之案例的簡短摘要, 以及為什麼一般 CLR 事件無法滿足這些案例的原因:The following is a brief summary of the scenarios that motivated the routed event concept, and why a typical CLR event was not adequate for these scenarios:

控制項撰寫和封裝:WPFWPF的各種控制項都有豐富的內容模型。Control composition and encapsulation: Various controls in WPFWPF have a rich content model. 例如, 您可以將影像放在Button中, 以有效地擴充按鈕的視覺化樹狀結構。For example, you can place an image inside of a Button, which effectively extends the visual tree of the button. 不過, 新增的映射不能中斷導致按鈕回應Click其內容的點擊測試行為, 即使使用者按一下的是影像中技術上的圖元。However, the added image must not break the hit-testing behavior that causes a button to respond to a Click of its content, even if the user clicks on pixels that are technically part of the image.

單數處理常式附加點:Windows FormsWindows Forms中, 您必須多次附加相同的處理常式, 以處理可從多個元素引發的事件。Singular handler attachment points: In Windows FormsWindows Forms, you would have to attach the same handler multiple times to process events that could be raised from multiple elements. 路由事件可讓您只需附加該處理常式一次 (如上一個範例中所示),然後就能視需要使用處理常式邏輯判斷此事件來自何處。Routed events enable you to attach that handler only once, as was shown in the previous example, and use handler logic to determine where the event came from if necessary. 例如,這可能是先前所示之 XAMLXAML 的處理常式:For instance, this might be the handler for the previously shown XAMLXAML:

private void CommonClickHandler(object sender, RoutedEventArgs e)
{
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
  {
    case "YesButton":
      // do something here ...
      break;
    case "NoButton":
      // do something ...
      break;
    case "CancelButton":
      // do something ...
      break;
  }
  e.Handled=true;
}
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
  Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
  Select Case feSource.Name
    Case "YesButton"
      ' do something here ...
    Case "NoButton"
      ' do something ...
    Case "CancelButton"
      ' do something ...
  End Select
  e.Handled=True
End Sub

類別處理: 路由事件允許由類別定義的靜態處理常式。Class handling: Routed events permit a static handler that is defined by the class. 這個類別處理常式有機會在任何附加的執行個體處理常式之前處理事件。This class handler has the opportunity to handle an event before any attached instance handlers can.

參考不含反映的事件: 某些程式碼和標記技術需要一個可識別特定事件的方式。Referencing an event without reflection: Certain code and markup techniques require a way to identify a specific event. 路由事件會建立一個RoutedEvent欄位做為識別碼, 以提供健全的事件識別技術, 不需要靜態或執行時間反映。A routed event creates a RoutedEvent field as an identifier, which provides a robust event identification technique that does not require static or run-time reflection.

路由事件的實作方式How Routed Events Are Implemented

路由事件是一個 CLR 事件, 它是由RoutedEvent類別的實例所支援, 並已向WPFWPF事件系統註冊。A routed event is a CLR event that is backed by an instance of the RoutedEvent class and registered with the WPFWPF event system. RoutedEvent註冊取得的實例通常會保留public static readonly為類別的欄位成員, 註冊並因此「擁有」路由事件。The RoutedEvent instance obtained from registration is typically retained as a public static readonly field member of the class that registers and thus "owns" the routed event. 與相同名稱 clr 事件 (有時稱為「包裝函式」事件) 的連接是藉由覆寫 clr 事件addremove和實作為來完成。The connection to the identically named CLR event (which is sometimes termed the "wrapper" event) is accomplished by overriding the add and remove implementations for the CLR event. 一般而言,addremove 會保留為隱含的預設值,以使用適當的語言特定事件語法來加入和移除該事件的處理常式。Ordinarily, the add and remove are left as an implicit default that uses the appropriate language-specific event syntax for adding and removing handlers of that event. 路由事件的支援和連接機制在概念上類似于相依性屬性是由DependencyProperty類別支援並向WPFWPF屬性系統註冊的 CLR 屬性。The routed event backing and connection mechanism is conceptually similar to how a dependency property is a CLR property that is backed by the DependencyProperty class and registered with the WPFWPF property system.

下列範例顯示Tap自訂路由事件的宣告, 包括註冊和公開RoutedEvent [識別碼] 欄位, 以及Tap CLR 事件的addremove [執行]。The following example shows the declaration for a custom Tap routed event, including the registration and exposure of the RoutedEvent identifier field and the add and remove implementations for the Tap CLR event.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
        add { AddHandler(TapEvent, value); } 
        remove { RemoveHandler(TapEvent, value); }
}
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))

' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
    AddHandler(ByVal value As RoutedEventHandler)
        Me.AddHandler(TapEvent, value)
    End AddHandler

    RemoveHandler(ByVal value As RoutedEventHandler)
        Me.RemoveHandler(TapEvent, value)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Me.RaiseEvent(e)
    End RaiseEvent
End Event

路由事件處理常式和 XAMLRouted Event Handlers and XAML

若要使用 XAMLXAML 加入事件的處理常式,您可以宣告事件名稱做為屬於事件接聽程式之元素上的屬性。To add a handler for an event using XAMLXAML, you declare the event name as an attribute on the element that is an event listener. 屬性的值是您所實作之處理常式方法的名稱,此名稱必須存在於程式碼後置檔案的部分類別中。The value of the attribute is the name of your implemented handler method, which must exist in the partial class of the code-behind file.

<Button Click="b1SetColor">button</Button>

加入標準 CLR 事件處理常式的語法和加入路由事件處理常式相同,因為您實際上是將處理常式加入至CLR事件包裝函式,其底下會有路由事件執行。XAMLXAMLThe XAMLXAML syntax for adding standard CLR event handlers is the same for adding routed event handlers, because you are really adding handlers to the CLR event wrapper, which has a routed event implementation underneath. 如需在 XAMLXAML 中加入事件處理常式的詳細資訊,請參閱 XAML 概觀 (WPF)For more information about adding event handlers in XAMLXAML, see XAML Overview (WPF).

路由傳送策略Routing Strategies

路由事件會使用下列其中一個路由傳送策略:Routed events use one of three routing strategies:

  • 路由系統會叫用事件來源上的事件處理常式。Bubbling: Event handlers on the event source are invoked. 路由事件接著會路由傳送到後續的父元素,直到其到達元素樹狀結構的根元素為止。The routed event then routes to successive parent elements until reaching the element tree root. 大部分的路由事件會使用事件反昇路由傳送策略。Most routed events use the bubbling routing strategy. 事件反昇的路由事件通常是用來報告來自不同控制項或其他 UI 元素的輸入或狀態變更。Bubbling routed events are generally used to report input or state changes from distinct controls or other UI elements.

  • 直銷只有來源元素本身會有機會在回應中叫用處理程式。Direct: Only the source element itself is given the opportunity to invoke handlers in response. 這類似於 Windows FormsWindows Forms 針對事件所使用的「路由傳送」。This is analogous to the "routing" that Windows FormsWindows Forms uses for events. 不過, 與標準 CLR 事件不同的是, 直接路由事件支援類別處理 (下一節會說明類別處理), 而且可供EventSetterEventTrigger使用。However, unlike a standard CLR event, direct routed events support class handling (class handling is explained in an upcoming section) and can be used by EventSetter and EventTrigger.

  • 建立一開始, 會叫用元素樹狀結構根目錄的事件處理常式。Tunneling: Initially, event handlers at the element tree root are invoked. 路由事件接著會在路由中朝向屬於路由事件來源的節點元素 (會引發路由事件的元素) 移動,以透過後續的子元素周遊該路由。The routed event then travels a route through successive child elements along the route, towards the node element that is the routed event source (the element that raised the routed event). 通道路由事件時常會做為複合控制項的一部分來使用或處理,這類來自複合組件的事件可利用專用於該完整控制項的事件來刻意隱藏或取代。Tunneling routed events are often used or handled as part of the compositing for a control, such that events from composite parts can be deliberately suppressed or replaced by events that are specific to the complete control. WPFWPF 中提供的輸入事件通常會實作為成對的通道/事件反昇組合。Input events provided in WPFWPF often come implemented as a tunneling/bubbling pair. 由於用於配對的命名慣例,通道事件有時也稱為預覽事件。Tunneling events are also sometimes referred to as Preview events, because of a naming convention that is used for the pairs.

為什麼要使用路由事件?Why Use Routed Events?

身為應用程式開發人員,您不一定需要知道或在意要將您正在處理的事件實作為路由事件。As an application developer, you do not always need to know or care that the event you are handling is implemented as a routed event. 路由事件都有特殊的行為,但是,如果您正在引發事件的元素上處理該事件,則大多不會看見該行為。Routed events have special behavior, but that behavior is largely invisible if you are handling an event on the element where it is raised.

路由事件變成功能強大之處在於,您是否使用任何建議的案例︰在一般的根元素上定義一般的處理常式、組合自己的控制項,或定義自己的控制項類別。Where routed events become powerful is if you use any of the suggested scenarios: defining common handlers at a common root, compositing your own control, or defining your own custom control class.

路由事件接聽程式和路由事件來源不需要在其階層中共用一般事件。Routed event listeners and routed event sources do not need to share a common event in their hierarchy. Any UIElementContentElement可以是任何路由事件的事件接聽程式。Any UIElement or ContentElement can be an event listener for any routed event. 因此, 您可以在整個工作 API 集內使用完整的路由事件集, 做為概念「介面」, 讓應用程式中的不同元素可以交換事件資訊。Therefore, you can use the full set of routed events available throughout the working API set as a conceptual "interface" whereby disparate elements in the application can exchange event information. 路由事件的這個「介面」概念特別適用於輸入事件。This "interface" concept for routed events is particularly applicable for input events.

路由事件也可用來透過元素樹狀結構進行通訊,因為事件的事件資料會永久存在於路由的每個元素中。Routed events can also be used to communicate through the element tree, because the event data for the event is perpetuated to each element in the route. 一個元素可以變更事件資料中的某些內容,而該變更可供路由中的下一個元素使用。One element could change something in the event data, and that change would be available to the next element in the route.

除了路由的外觀以外, 還有其他兩個原因, 表示任何WPFWPF指定的事件可能會實作為路由事件, 而不是標準 CLR 事件。Other than the routing aspect, there are two other reasons that any given WPFWPF event might be implemented as a routed event instead of a standard CLR event. 如果您要實作自己的事件,也可以考慮這些準則:If you are implementing your own events, you might also consider these principles:

  • 某些WPFWPF樣式設定和範本化功能EventSetter ( EventTrigger例如和) 會要求參考的事件為路由事件。Certain WPFWPF styling and templating features such as EventSetter and EventTrigger require the referenced event to be a routed event. 這是先前所述的事件識別碼案例。This is the event identifier scenario mentioned earlier.

  • 路由事件支援類別處理機制,此類別可藉以指定靜態方法,有機會在任何已註冊的執行個體處理常式可存取路由事件之前處理它們。Routed events support a class handling mechanism whereby the class can specify static methods that have the opportunity to handle routed events before any registered instance handlers can access them. 這在控制項設計中非常有用,因為您的類別可以強制執行事件驅動的類別行為,您無法藉由處理執行個體上的事件來附帶隱藏這類行為。This is very useful in control design, because your class can enforce event-driven class behaviors that cannot be accidentally suppressed by handling an event on an instance.

上述每個考量都會在本主題的不同小節中加以討論。Each of the above considerations is discussed in a separate section of this topic.

加入和實作路由事件的事件處理常式Adding and Implementing an Event Handler for a Routed Event

若要在 XAMLXAML 中加入事件處理常式,您只需將事件名稱加入至元素做為屬性,並將屬性值設為實作適當委派的事件處理常式名稱,如下列範例所示。To add an event handler in XAMLXAML, you simply add the event name to an element as an attribute and set the attribute value as the name of the event handler that implements an appropriate delegate, as in the following example.

<Button Click="b1SetColor">button</Button>

b1SetColor這是已執行的處理常式名稱, 其中包含處理Click事件的程式碼。b1SetColor is the name of the implemented handler that contains the code that handles the Click event. b1SetColor必須具有與RoutedEventHandler委派相同的簽章, 也就是Click事件的事件處理常式委派。b1SetColor must have the same signature as the RoutedEventHandler delegate, which is the event handler delegate for the Click event. 所有路由事件處理常式委派的第一個參數會指定要加入事件處理常式的元素,而第二個參數會指定事件的資料。The first parameter of all routed event handler delegates specifies the element to which the event handler is added, and the second parameter specifies the data for the event.

void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event
}
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
  'logic to handle the Click event
End Sub

RoutedEventHandler是基本路由事件處理常式委派。RoutedEventHandler is the basic routed event handler delegate. 如果是針對特定控制項或案例特製化的路由事件,用於路由事件處理常式的委派也可能會變得更特製化,讓它們能夠傳輸特製化的事件資料。For routed events that are specialized for certain controls or scenarios, the delegates to use for the routed event handlers also might become more specialized, so that they can transmit specialized event data. 例如, 在一般輸入案例中, 您可能會處理DragEnter路由事件。For instance, in a common input scenario, you might handle a DragEnter routed event. 您的DragEventHandler處理常式應該會執行委派。Your handler should implement the DragEventHandler delegate. 藉由使用最特定的委派, 您可以DragEventArgs在處理常式中處理, 並Data讀取屬性, 其中包含拖曳作業的剪貼簿承載。By using the most specific delegate, you can process the DragEventArgs in the handler and read the Data property, which contains the clipboard payload of the drag operation.

如需如何使用 XAMLXAML 來將事件處理常式加入至元素的完整範例,請參閱處理路由事件For a complete example of how to add an event handler to an element using XAMLXAML, see Handle a Routed Event.

在使用程式碼建立的應用程式中加入路由事件的處理常式很簡單。Adding a handler for a routed event in an application that is created in code is straightforward. 路由事件處理常式一律可以透過 helper 方法AddHandler加入 (這與現有的支援所add呼叫的方法相同)。不過,現有的 WPFWPF 路由事件通常支援實作 addremove 邏輯,以允許透過語言特有的事件語法來加入路由事件的處理常式,此語法是比 Helper 方法更直覺的語法。Routed event handlers can always be added through a helper method AddHandler (which is the same method that the existing backing calls for add.) However, existing WPFWPF routed events generally have backing implementations of add and remove logic that allow the handlers for routed events to be added by a language-specific event syntax, which is more intuitive syntax than the helper method. 以下是 Helper 方法的使用方式範例:The following is an example usage of the helper method:

void MakeButton()
 {
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 }
 void Onb2Click(object sender, RoutedEventArgs e)
 {
     //logic to handle the Click event     
 }
Private Sub MakeButton()
     Dim b2 As New Button()
     b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
 Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
     'logic to handle the Click event     
 End Sub

下一個範例顯示C#運算子語法 (Visual Basic 因其對取值的處理而略有不同的運算子語法):The next example shows the C# operator syntax (Visual Basic has slightly different operator syntax because of its handling of dereferencing):

void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event     
}
Private Sub MakeButton2()
  Dim b2 As New Button()
  AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
  'logic to handle the Click event     
End Sub

如需如何在程式碼中加入事件處理常式的範例,請參閱使用程式碼加入事件處理常式For an example of how to add an event handler in code, see Add an Event Handler Using Code.

如果您使用 Visual Basic, 您也可以使用Handles關鍵字, 在處理常式宣告中加入處理常式。If you are using Visual Basic, you can also use the Handles keyword to add handlers as part of the handler declarations. 如需詳細資訊,請參閱 Visual Basic 和 WPF 事件處理For more information, see Visual Basic and WPF Event Handling.

已處理的概念The Concept of Handled

所有路由事件都會共用一個通用的事件資料基類RoutedEventArgsAll routed events share a common event data base class, RoutedEventArgs. RoutedEventArgs定義接受Handled布林值的屬性。RoutedEventArgs defines the Handled property, which takes a Boolean value. Handled屬性的用途是將的值Handled設定為true, 以啟用沿著路由的任何事件處理常式, 以將路由事件標示為已處理The purpose of the Handled property is to enable any event handler along the route to mark the routed event as handled, by setting the value of Handled to true. 透過處理常式在路由中的某一個元素上進行處理之後,就會再次向路由中的每一個接聽程式報告共用的事件資料。After being processed by the handler at one element along the route, the shared event data is again reported to each listener along the route.

的值Handled會影響路由事件在路由中進一步傳遞時的報告或處理方式。The value of Handled affects how a routed event is reported or processed as it travels further along the route. 如果Handledtrue在路由事件的事件資料中, 則通常不會再針對該特定事件實例叫用接聽其他元素上之路由事件的處理常式。If Handled is true in the event data for a routed event, then handlers that listen for that routed event on other elements are generally no longer invoked for that particular event instance. 此情況適用於 XAMLXAML 中附加的處理常式,以及適用於利用語言特定之事件處理常式附加語法 (例如 +=Handles) 所加入的處理常式。This is true both for handlers attached in XAMLXAML and for handlers added by language-specific event handler attachment syntaxes such as += or Handles. 在大部分常見的處理常式案例中, 將設定Handledtrue所處理的事件標示為將會針對通道路由或反升路由進行「停止」路由, 也適用于在路由中由類別處理常式在某個點上處理的任何事件。For most common handler scenarios, marking an event as handled by setting Handled to true will "stop" routing for either a tunneling route or a bubbling route, and also for any event that is handled at a point in the route by a class handler.

不過, 有一個「handledEventsToo」機制, 接聽程式仍然可以執行處理常式, 以回應事件資料Handled true中的路由事件。However, there is a "handledEventsToo" mechanism whereby listeners can still run handlers in response to routed events where Handled is true in the event data. 換句話說,事件路由無法藉由將事件資料標示為已處理來真正停止。In other words, the event route is not truly stopped by marking the event data as handled. 您只能在程式EventSetter代碼或中使用 handledEventsToo 機制:You can only use the handledEventsToo mechanism in code, or in an EventSetter:

除了Handled狀態在路由事件中產生的行為之外, 的Handled概念也會影響您應該如何設計應用程式並撰寫事件處理常式程式碼。In addition to the behavior that Handled state produces in routed events, the concept of Handled has implications for how you should design your application and write the event handler code. 您可以將Handled概念理解為路由事件所公開的簡單通訊協定。You can conceptualize Handled as being a simple protocol that is exposed by routed events. 您可以自行決定如何使用此通訊協定, 但要如何使用其值Handled的概念設計如下所示:Exactly how you use this protocol is up to you, but the conceptual design for how the value of Handled is intended to be used is as follows:

  • 如果將路由事件標示為已處理,則不需再次透過該路由中的其他元素來處理它。If a routed event is marked as handled, then it does not need to be handled again by other elements along that route.

  • 如果路由事件未標示為已處理, 則先前在路由中的其他接聽程式已選擇不註冊處理常式, 或已註冊的處理常式選擇不要操作事件資料, 並將設定Handled為。 trueIf a routed event is not marked as handled, then other listeners that were earlier along the route have chosen either not to register a handler, or the handlers that were registered chose not to manipulate the event data and set Handled to true. (或者,當然有可能目前的接聽程式為路由中的第一個點)。目前接聽程式上的處理常式現在有三個可能的做法:(Or, it is of course possible that the current listener is the first point in the route.) Handlers on the current listener now have three possible courses of action:

    • 不採取任何動作;事件會保持未處理狀態,而事件會路由傳送到下一個接聽程式。Take no action at all; the event remains unhandled, and the event routes to the next listener.

    • 執行程式碼以回應事件,但判定採取的動作後續不足以保證會將事件標示為已處理。Execute code in response to the event, but make the determination that the action taken was not substantial enough to warrant marking the event as handled. 事件會路由傳送到下一個接聽程式。The event routes to the next listener.

    • 執行程式碼以回應事件。Execute code in response to the event. 在傳遞至處理常式的事件資料中將事件標示為已處理,因為已將所採取的動作視為後續不足以保證會標示為已處理。Mark the event as handled in the event data passed to the handler, because the action taken was deemed substantial enough to warrant marking as handled. 事件仍會路由至下一個接聽程式, 但Handled在其事件資料中, 因此handledEventsToo只有接聽項有機會叫用= true進一步的處理常式。The event still routes to the next listener, but with Handled=true in its event data, so only handledEventsToo listeners have the opportunity to invoke further handlers.

這個概念設計會由先前所述的路由行為加強: 更難以 (雖然程式碼或樣式中仍然可行) 附加路由事件的處理常式, 即使已經設定Handled了路由的先前處理常式也是一樣。至trueThis conceptual design is reinforced by the routing behavior mentioned earlier: it is more difficult (although still possible in code or styles) to attach handlers for routed events that are invoked even if a previous handler along the route has already set Handled to true.

如需有關Handled的詳細資訊、路由事件的類別處理, 以及有關將路由事件標示為Handled時的建議事項, 請參閱將路由事件標示為已處理, 以及類別處理For more information about Handled, class handling of routed events, and recommendations about when it is appropriate to mark a routed event as Handled, see Marking Routed Events as Handled, and Class Handling.

在應用程式中,只在引發事件反昇路由事件的物件上處理該事件是相當常見的,而這並不完全與事件的路由特性有關。In applications, it is quite common to just handle a bubbling routed event on the object that raised it, and not be concerned with the event's routing characteristics at all. 不過,它仍是在事件資料中將路由事件標示為已處理的最佳做法,萬一元素樹狀結構中進一步向上的元素也具有已針對該相同路由事件附加的處理常式時,可避免發生未預期的副作用。However, it is still a good practice to mark the routed event as handled in the event data, to prevent unanticipated side effects just in case an element that is further up the element tree also has a handler attached for that same routed event.

類別處理常式Class Handlers

如果您要定義從DependencyObject某種方式衍生的類別, 您也可以針對屬於類別之已宣告或繼承事件成員的路由事件定義和附加類別處理常式。If you are defining a class that derives in some way from DependencyObject, you can also define and attach a class handler for a routed event that is a declared or inherited event member of your class. 每當路由事件到達其路由中的元素執行個體時,就可以在任何附加至該類別執行個體的執行個體接聽程式處理常式之前叫用類別處理常式。Class handlers are invoked before any instance listener handlers that are attached to an instance of that class, whenever a routed event reaches an element instance in its route.

某些 WPFWPF 控制項具有適用於特定路由事件的繼承類別處理。Some WPFWPF controls have inherent class handling for certain routed events. 這可能會造成表面上未曾引發過路由事件,但實際上卻已處理過類別的情況,而且,如果您使用某些技術,您的執行個體處理常式可能仍會處理該路由事件。This might give the outward appearance that the routed event is not ever raised, but in reality it is being class handled, and the routed event can potentially still be handled by your instance handlers if you use certain techniques. 此外,許多基底類別和控制項會公開可用於覆寫類別處理行為的虛擬方法。Also, many base classes and controls expose virtual methods that can be used to override class handling behavior. 如需如何因應不想要的類別處理,並在自訂類別中定義您自己之類別處理的詳細資訊,請參閱將路由事件標記為已處理以及類別處理For more information both on how to work around undesired class handling and on defining your own class handling in a custom class, see Marking Routed Events as Handled, and Class Handling.

在 WPF 中附加事件Attached Events in WPF

XAMLXAML 語言也會定義稱為「附加事件」的特定事件類型。The XAMLXAML language also defines a special type of event called an attached event. 附加事件可讓您將特殊事件的處理常式加入至任意元素。An attached event enables you to add a handler for a particular event to an arbitrary element. 處理事件的元素不需要定義或繼承附加事件,而物件不可能引發事件,且目的地處理執行個體也不需定義或「擁有」該事件做為類別成員。The element handling the event need not define or inherit the attached event, and neither the object potentially raising the event nor the destination handling instance must define or otherwise "own" that event as a class member.

WPFWPF 輸入系統會廣泛使用附加事件。The WPFWPF input system uses attached events extensively. 不過,幾乎這所有的附加事件都會轉送到基底元素。However, nearly all of these attached events are forwarded through base elements. 輸入事件接著會顯示為對等的非附加路由事件,其為基底元素類別的成員。The input events then appear as equivalent non-attached routed events that are members of the base element class. 例如, 基礎附加事件Mouse.MouseDown可以在任何指定UIElement的上使用MouseDown來處理, 而不是在UIElement或程式碼中XAMLXAML處理附加事件語法。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 XAMLXAML or code.

如需 WPFWPF 中附加事件的詳細資訊,請參閱附加事件概觀For more information about attached events in WPFWPF, see Attached Events Overview.

XAML 中的完整事件名稱Qualified Event Names in XAML

類似 typename.eventname 附加事件語法的另一種語法用法 (但嚴格來說,它不是附加事件的使用方式) 是,當您連接由子元素所引發之路由事件的處理常式時。Another syntax usage that resembles typename.eventname attached event syntax but is not strictly speaking an attached event usage is when you attach handlers for routed events that are raised by child elements. 您可將處理常式附加到共同父項,以利用事件路由,即使共同父項可能沒有相關的路由事件做為成員。You attach the handlers to a common parent, to take advantage of event routing, even though the common parent might not have the relevant routed event as a member. 再看一下這個範例:Consider this example again:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

在此, 會將處理常式加入其中的父項目接聽StackPanel項為。Here, the parent element listener where the handler is added is a StackPanel. 不過, 它會加入已宣告之路由事件的處理常式, 而且會由Button類別引發 (ButtonBase Button實際上是, 但可透過繼承)。However, it is adding a handler for a routed event that was declared and will be raised by the Button class (ButtonBase actually, but available to Button through inheritance). Button「擁有」事件, 但路由事件系統允許將任何路由事件的處理常式附加至任何UIElement可能附加 common language runtime (CLR) 事件之接聽程式的或ContentElement實例接聽程式。Button "owns" the event, but the routed event system permits handlers for any routed event to be attached to any UIElement or ContentElement instance listener that could otherwise attach listeners for a common language runtime (CLR) event. 這些完整事件屬性名稱的預設 xmlns 命名空間通常是預設的 WPFWPF xmlns 命名空間,但您也可以針對自訂路由事件指定有前置詞的命名空間。The default xmlns namespace for these qualified event attribute names is typically the default WPFWPF xmlns namespace, but you can also specify prefixed namespaces for custom routed events. 如需 xmlns 的詳細資訊,請參閱 WPF XAML 的 XAML 命名空間和命名空間對應For more information about xmlns, see XAML Namespaces and Namespace Mapping for WPF XAML.

WPF 輸入事件WPF Input Events

WPFWPF 平台中有一個路由事件的常用應用程式適用於輸入事件。One frequent application of routed events within the WPFWPF platform is for input events. WPFWPF 中,通道路由事件名稱前面依慣例會加上 "Preview" 這個字。In WPFWPF, tunneling routed events names are prefixed with the word "Preview" by convention. 輸入事件通常會成對出現,其中一個是事件反昇事件,另一個則是通道事件。Input events often come in pairs, with one being the bubbling event and the other being the tunneling event. 例如, KeyDown事件PreviewKeyDown和事件具有相同的簽章, 而前者是反升輸入事件, 後者則是通道輸入事件。For example, the KeyDown event and the PreviewKeyDown event have the same signature, with the former being the bubbling input event and the latter being the tunneling input event. 有時,輸入事件只會有事件反昇版本,或可能只有直接路由版本。Occasionally, input events only have a bubbling version, or perhaps only a direct routed version. 在文件中,路由事件主題會交互參考具備替代路由傳送策略的類似路由事件 (如果這類路由事件存在),而受管理參考頁面中的章節會釐清每個路由事件的路由傳送策略。In the documentation, routed event topics cross-reference similar routed events with alternative routing strategies if such routed events exist, and sections in the managed reference pages clarify the routing strategy of each routed event.

系統會實作成對出現的 WPFWPF 輸入事件,讓來自輸入的單一使用者動作 (例如按下滑鼠按鈕) 將依序引發配對中的這兩個路由事件。WPFWPF input events that come in pairs are implemented so that a single user action from input, such as a mouse button press, will raise both routed events of the pair in sequence. 首先,引發通道事件,並周遊其路由。First, the tunneling event is raised and travels its route. 接著,引發事件反昇事件,並周遊其路由。Then the bubbling event is raised and travels its route. 這兩個事件實際上會共用相同的事件資料實例, RaiseEvent因為引發反升事件的實類別中的方法呼叫會接聽來自通道事件的事件資料, 並在新引發的事件中重複使用它。The two events literally share the same event data instance, because the RaiseEvent method call in the implementing class that raises the bubbling event listens for the event data from the tunneling event and reuses it in the new raised event. 具有通道事件處理常式的接聽程式有機會優先將路由事件標示為已處理 (第一個是類別處理常式,然後是執行個體處理常式)。Listeners with handlers for the tunneling event have the first opportunity to mark the routed event handled (class handlers first, then instance handlers). 如果將通道路由中的元素標示為已處理,則已經處理的事件資料就會針對事件反昇事件進行傳送,而且將不會叫用基於對等事件反昇輸入事件附加的一般處理常式。If an element along the tunneling route marked the routed event as handled, the already-handled event data is sent on for the bubbling event, and typical handlers attached for the equivalent bubbling input events will not be invoked. 表面上來看,就如同未曾引發過已處理的事件反昇事件。To outward appearances it will be as if the handled bubbling event has not even been raised. 這個處理行為非常適用於控制項複合,您可能想要讓您的最終控制項 (而不是它的複合組件) 報告所有以點擊測試為基礎的輸入事件或以焦點為基礎的輸入事件。This handling behavior is useful for control compositing, where you might want all hit-test based input events or focus-based input events to be reported by your final control, rather than its composite parts. 最後一個控制項元素更接近複合中的根元素,因此有機會優先對通道事件進行類別處理,而且,或許可利用更專屬於控制項的事件,做為支援控制項類別的程式碼一部分來「取代」該路由事件。The final control element is closer to the root in the compositing, and therefore has the opportunity to class handle the tunneling event first and perhaps to "replace" that routed event with a more control-specific event, as part of the code that backs the control class.

如需輸入事件處理如何運作的圖表說明,請考量下列輸入事件範例。As an illustration of how input event processing works, consider the following input event example. 在下列樹狀結構圖例中leaf element #2 , 是PreviewMouseDownMouseDown事件的來源:In the following tree illustration, leaf element #2 is the source of both a PreviewMouseDown and then a MouseDown event:

事件路由圖表

事件處理的順序如下:The order of event processing is as follows:

  1. 根元素上的 PreviewMouseDown (通道)。PreviewMouseDown (tunnel) on root element.

  2. 中繼元素 #1 上的 PreviewMouseDown (通道)。PreviewMouseDown (tunnel) on intermediate element #1.

  3. 來源元素 #2 上的 PreviewMouseDown (通道)。PreviewMouseDown (tunnel) on source element #2.

  4. 來源元素 #2 上的 MouseDown (事件反昇)。MouseDown (bubble) on source element #2.

  5. 中繼元素 #1 上的 MouseDown (事件反昇)。MouseDown (bubble) on intermediate element #1.

  6. 根元素上的 MouseDown (事件反昇)。MouseDown (bubble) on root element.

路由事件處理常式委派提供對兩個物件的參考︰引發事件的物件,以及已叫用處理常式的物件。A routed event handler delegate provides references to two objects: the object that raised the event and the object where the handler was invoked. 已叫用處理常式的物件是 sender 參數所報告的物件。The object where the handler was invoked is the object reported by the sender parameter. 第一次引發事件的物件是由事件資料中Source的屬性所報告。The object where the event was first raised is reported by the Source property in the event data. 路由事件仍然可以由相同的物件引發和處理, 在這種情況下sender , Source和都相同 (在事件處理範例清單中, 步驟3和4的情況如下)。A routed event can still be raised and handled by the same object, in which case sender and Source are identical (this is the case with Steps 3 and 4 in the event processing example list).

因為通道和反升, 父元素會收到輸入事件, Source其中是其子專案的其中一個。Because of tunneling and bubbling, parent elements receive input events where the Source is one of their child elements. 當您瞭解來源元素為何時, 您可以藉由存取Source屬性來識別來源元素。When it is important to know what the source element is, you can identify the source element by accessing the Source property.

通常, 一旦標示Handled輸入事件之後, 就不會叫用進一步的處理常式。Usually, once the input event is marked Handled, further handlers are not invoked. 一般而言,您應該在叫用處理常式來處理代表輸入事件意義之應用程式特定的邏輯處理之後,儘速將輸入事件標示為已處理。Typically, you should mark input events as handled as soon as a handler is invoked that addresses your application-specific logical handling of the meaning of the input event.

此一般語句關於Handled狀態的例外狀況, 是為了刻意忽略Handled事件資料狀態的輸入事件處理常式, 仍然會沿著任一路由叫用。The exception to this general statement about Handled state is that input event handlers that are registered to deliberately ignore Handled state of the event data would still be invoked along either route. 如需詳細資訊,請參閱預覽事件將路由事件標記為已處理以及類別處理For more information, see Preview Events or Marking Routed Events as Handled, and Class Handling.

通道事件與事件反昇事件之間共用的事件資料模型,以及後續引發的第一個通道事件,然後是事件反昇事件,不是一般適用於所有路由事件的概念。The shared event data model between tunneling and bubbling events, and the sequential raising of first tunneling then bubbling events, is not a concept that is generally true for all routed events. 該行為特別是透過 WPFWPF 輸入裝置如何選擇引發並連接輸入事件配對的方式來實作。That behavior is specifically implemented by how WPFWPF input devices choose to raise and connect the input event pairs. 實作您自己的輸入事件是一個進階案例,但您也可以選擇針對自己的輸入事件遵循該模型。Implementing your own input events is an advanced scenario, but you might choose to follow that model for your own input events also.

某些類別會選擇對特定輸入事件進行類別處理,一般的用意是重新定義特定使用者驅動的輸入事件在該控制項內所代表的意義,然後引發新的事件。Certain classes choose to class-handle certain input events, usually with the intent of redefining what a particular user-driven input event means within that control and raising a new event. 如需詳細資訊,請參閱將路由事件標記為已處理以及類別處理For more information, see Marking Routed Events as Handled, and Class Handling.

如需輸入以及在一般應用程式案例中輸入如何與事件互動的詳細資訊,請參閱輸入概觀For more information on input and how input and events interact in typical application scenarios, see Input Overview.

EventSetters 和 EventTriggersEventSetters and EventTriggers

在樣式中, 您可以XAMLXAML EventSetter使用, 在標記中包含一些預先宣告的事件處理語法。In styles, you can include some pre-declared XAMLXAML event handling syntax in the markup by using an EventSetter. 套用樣式時,會將參考的處理常式加入至樣式執行個體。When the style is applied, the referenced handler is added to the styled instance. EventSetter您只能針對路由事件宣告。You can declare an EventSetter only for a routed event. 下列為範例。The following is an example. 請注意,此處參考的 b1SetColor 方法位於程式碼後置檔案中。Note that the b1SetColor method referenced here is in a code-behind file.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.
  </Button>
</StackPanel>

這裡所得到的優點是, 樣式可能包含許多其他資訊, 可套用至應用程式中的任何按鈕, 而讓EventSetter成為該樣式的一部分, 即使在標記層級也能重複使用程式碼。The advantage gained here is that the style is likely to contain a great deal of other information that could apply to any button in your application, and having the EventSetter be part of that style promotes code reuse even at the markup level. 此外, EventSetter處理常式的抽象方法名稱會從一般應用程式和頁面標記進一步執行。Also, an EventSetter abstracts method names for handlers one step further away from the general application and page markup.

另一個結合的路由事件和動畫功能WPFWPF的特製化語法EventTrigger是。Another specialized syntax that combines the routed event and animation features of WPFWPF is an EventTrigger. 如同, 只有路由事件可以用於EventTriggerEventSetterAs with EventSetter, only routed events may be used for an EventTrigger. 通常會將宣告為樣式的一部分, EventTrigger但也可以在頁面層級專案上宣告為Triggers集合的一部分, 或在中ControlTemplate宣告。 EventTriggerTypically, an EventTrigger is declared as part of a style, but an EventTrigger can also be declared on page-level elements as part of the Triggers collection, or in a ControlTemplate. 可讓您Storyboard指定在路由事件到達EventTrigger其路由中宣告該事件之專案的時執行的。 EventTriggerAn EventTrigger enables you to specify a Storyboard that runs whenever a routed event reaches an element in its route that declares an EventTrigger for that event. 除了處理事件並EventTrigger使其啟動現有的分鏡腳本之外, 的優點是EventTrigger , 可讓您更有效地控制腳本和其執行時間行為。The advantage of an EventTrigger over just handling the event and causing it to start an existing storyboard is that an EventTrigger provides better control over the storyboard and its run-time behavior. 如需詳細資訊,請參閱在分鏡腳本開始後使用事件觸發程式進行控制For more information, see Use Event Triggers to Control a Storyboard After It Starts.

深入了解路由事件More About Routed Events

本主題主要是從描述基本概念並提供如何及何時回應路由事件的觀點來討論路由事件,而路由事件已經存在於各種基底元素和控制項中。This topic mainly discusses routed events from the perspective of describing the basic concepts and offering guidance on how and when to respond to the routed events that are already present in the various base elements and controls. 不過,您可以在自訂類別上建立自己的路由事件以及所有必要的支援,例如特製化的事件資料類別和委派。However, you can create your own routed event on your custom class along with all the necessary support, such as specialized event data classes and delegates. 路由事件擁有者可以是任何類別, 但路由事件必須由引發並由UIElementContentElement衍生類別處理, 才能發揮效用。The routed event owner can be any class, but routed events must be raised by and handled by UIElement or ContentElement derived classes in order to be useful. 如需自訂事件的詳細資訊,請參閱建立自訂路由事件For more information about custom events, see Create a Custom Routed Event.

另請參閱See also