路由事件概觀
更新:2007 年 11 月
本主題說明 Windows Presentation Foundation (WPF) 中的路由事件概念。本主題會定義路由事件的術語、說明路由事件是如何透過項目樹狀結構路由傳送的、摘要說明如何處理路由事件,以及簡介說明如何建立自己的自訂路由事件。
這個主題包含下列章節。
- 必要條件
- 什麼是路由事件
- 路由策略
- 為何使用路由事件
- 加入和實作路由事件的事件處理常式
- 類別處理常式
- 在 WPF 中的附加事件
- XAML 中的完整事件名稱
- WPF 輸入事件
- EventSetters 和 EventTriggers
- 更多關於路由事件的資訊
- 相關主題
必要條件
本主題假設您具有 Common Language Runtime (CLR) 和物件導向程式設計的基本知識,以及 WPF 項目間的關係如何能夠以樹狀結構的方式概念化的觀念。為了能夠理解本主題中的範例,您也應該了解可延伸標記語言 (XAML),並知道如何撰寫很基本的 WPF 應用程式或頁面。如需詳細資訊,請參閱 Windows Presentation Foundation 使用者入門和 XAML 概觀。
什麼是路由事件
您可以從功能面或是從實作面來想像路由事件。這裡同時陳述這兩種定義,因為不同人對於哪種定義較為實用的看法各有不同。
功能定義:路由事件這種事件類型可以在項目樹狀結構的多個接聽項上叫用處理常式,而非僅能在引發事件的物件上叫用。
實作定義:路由事件是一種 CLR 事件,受到 RoutedEvent 類別的執行個體所支援,並且由 Windows Presentation Foundation (WPF) 事件系統所處理。
一般的 WPF 應用程式包含許多項目。不論是在程式碼中建立的,或是在 XAML 中宣告的,這些項目在項目樹狀結構中都彼此關聯著。依據事件的定義,事件路由可以向兩個方向中的一個前進,但一般而言,路由會從來源項目出發前進,然後透過項目樹狀結構反昇 (Bubble) 向上,直到抵達項目樹狀結構的根項目 (通常是頁面或視窗)。如果您過去曾經使用 DHTML 物件模型,那麼對這個反昇概念應該不陌生。
請考慮下列簡單項目樹狀結構:
<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>
這個項目樹狀結構會產生類似下列的情形:
在這個簡化的項目樹狀結構中,Click 事件的來源是其中一個 Button 項目,不論按下哪個 Button,該項目就有機會成為處理事件的第一個項目。但如果 Button 所附加的處理常式都不會對該事件有反應,則事件會反昇至項目樹狀結構中的 Button 父項目,也就是 StackPanel。也有可能事件會反昇至 Border,然後超出項目樹狀結構的頁面根項目 (未顯示) 範圍。
換句話說,這個 Click 事件的事件路由如下:
Button-->StackPanel-->Border-->...
路由事件的最上層案例
以下內容將簡短摘要說明促成路由事件概念的案例,以及為何一般的 CLR 事件無法滿足這些案例。
控制項複合和封裝:WPF 中的各種控制項都具有豐富的內容模型。舉例來說,將影像置放在 Button 內,即可以有效擴充按鈕的視覺化樹狀結構。然而,加入的影像不能破壞造成按鈕回應其內容的 Click 點擊測試 (Hit Testing) 行為,就算使用者按下的像素在技術上是影像的一部分。
單一處理常式附加點:Windows Form 中,對於可以從多個項目引發的事件,您必須多次附加相同的處理常式才能處理這些事件。路由事件讓您可以只附加一次該處理常式,再視需要使用處理常式邏輯判斷事件的來源,如先前範例所示。例如,這可以做為先前顯示的 XAML 的處理常式:
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;
}
類別處理:路由事件允許類別所定義的靜態處理常式。這個類別處理常式有機會在任何附加的執行個體處理常式進行前,先處理事件。
不需反映即可參考事件:有些程式碼和標記技術需要有識別特定事件的方式。路由事件會將 RoutedEvent 欄位建立為識別項,提供強固的事件識別技術,而不需要靜態或執行階段的反映。
路由事件是如何實作的
路由事件是一種 CLR 事件,受到 RoutedEvent 類別的執行個體所支援,並且由 WPF 事件系統所註冊。從註冊取得的 RoutedEvent 執行個體,通常會保留成註冊該路由事件進而「擁有」該路由事件的類別的 publicstaticreadonly 欄位成員。對同名 CLR 事件 (有時稱為「包裝函式」事件) 的連接,是藉由覆寫 CLR 事件的 add 和 remove 實作而達成的。一般而言,add 和 remove 是保留為隱含預設值,會使用適當的特定語言事件語法來加入和移除該事件的處理常式。路由事件的支援和連接機制,在概念上與這種情況類似:相依性屬性 (Property) 是一種 CLR 屬性,受到 DependencyProperty 類別所支援,並向 WPF 屬性系統註冊。
下列範例顯示自訂 Tap 路由事件的宣告,包括 RoutedEvent 識別項欄位的註冊和公開,以及 TapCLR 事件的 add 和 remove 實作。
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
路由事件處理常式和 XAML
若要使用 XAML 加入事件的處理常式,您要在做為事件接聽項的項目上,將事件名稱宣告為屬性 (Attribute)。屬性的值則為實作處理常式方法的名稱,且程式碼後置 (Code-Behind) 檔案的部分類別中必須要有該方法。
<Button Click="b1SetColor">button</Button>
用於加入標準 CLR 事件處理常式的 XAML 語法,與加入路由事件處理常式的語法相同,因為您實際上就是將處理常式加入到其下具有路由事件實作的 CLR 事件包裝函式中。如需在 XAML 中加入事件處理常式的詳細資訊,請參閱 XAML 概觀。
路由策略
路由事件使用下列三種路由策略中的一種:
反昇:會叫用事件來源上的事件處理常式。路由事件接著會路由至相鄰的父項目,直到抵達項目樹狀結構的根項目。大部分的路由事件會使用反昇路由策略。反昇路由事件通常用於回報不同控制項或其他 UI 項目的輸入或狀態變更。
直接:只有來源項目本身有機會叫用回應的處理常式。這就好比 Windows Form 針對事件使用的「路由」。但是,跟標準 CLR 事件不同的是,直接路由事件支援類別處理 (在後續的章節將說明類別處理),並可以讓 EventSetter 和 EventTrigger 所使用。
通道:最初會叫用在項目樹狀結構的根項目上的事件處理常式。路由事件接著會在路由過程中前進到鄰近的子項目,朝向身為路由事件來源的節點項目 (即引發路由事件的項目)。通道路由事件通常會視為複合控制項的一部分來使用或處理,這樣複合部分的控制項就可以特意隱藏起來,或是使用完整控制項的專屬事件來取代。WPF 中提供的輸入事件的實作,通常會以通道/反昇配對組的方式進行。由於用於配對組的命名慣例,通道事件有時候也稱為「預覽」事件。
為何使用路由事件
身為應用程式開發人員,您不一定有必要知道或是在意您處理中的事件是否是以路由事件實作的。路由事件具有特殊的行為,但當您處理的事件就在引發事件的項目上時,您不大會感受到這個行為。
當您使用下列任何一個建議案例時,路由事件就相當好用:在通用根項目定義通用處理常式、複合自己的控制項,或者是定義自己的自訂控制項類別。
路由事件接聽項和路由事件來源不需要共用階層架構中的通用事件。任何 UIElement 或 ContentElement 都可以是任何路由事件的事件接聽項。因此,您可以使用整個 API 工作組中的可用完整路由事件組做為概念上的「介面」,讓應用程式中的不同項目可以交換事件資訊。這個路由事件的「介面」概念,對於輸入事件而言特別適用。
路由事件也可以用於在項目樹狀結構間進行通訊,因為事件的事件資料對路由中的每個項目而言都是永久有效的。當某個項目變更事件資料中的內容時,該項變更就可以供路由中的下一個項目使用。
除了路由的觀點外,還有另外兩個原因讓任何指定的 WPF 事件應該以路由事件實作,而非以標準 CLR 事件實作。當您在實作自己的事件時,也應該考量這些準則:
某些 WPF 樣式和樣板化功能,例如 EventSetter 和 EventTrigger 要求參考事件必須是路由事件。這是稍早提過的事件識別項案例。
路由事件支援的類別處理機制,讓類別可以指定靜態方法,有機會在註冊的執行個體處理常式進行存取前,先處理路由事件。這對於控制項設計非常有用,因為類別可以強制執行事件驅動 (Event-Driven) 的類別行為,讓行為不會因處理執行個體上的事件而意外隱藏。
上述每個考量都將於本主題的小節中分別討論。
加入和實作路由事件的事件處理常式
若要在 XAML 中加入事件處理常式,您只需要將事件名稱加入為項目的屬性 (Attribute),並將屬性值設為會實作適當委派的事件處理常式名稱,如以下範例所示。
<Button Click="b1SetColor">button</Button>
b1SetColor 是實作的處理常式名稱,所包含的程式碼會處理 Click 事件。b1SetColor 必須與 RoutedEventHandler 委派具有相同的簽章,該委派是 Click 事件的事件處理常式委派。所有路由事件處理常式委派的第一個參數,會指定要加入事件處理常式的項目,而第二個參數則指定事件的資料。
void b1SetColor(object sender, RoutedEventArgs args)
{
//logic to handle the Click event
...
}
RoutedEventHandler 是基本的事件處理常式委派。對於針對某些控制項或案例而特製化的路由事件,用於路由事件處理常式的委派也應該成為特製化的,這樣才能傳輸特製化的事件資料。例如,在一般的輸入案例中,您可能會處理 DragEnter 路由事件。而您的處理常式應該要實作 DragEventHandler 委派。藉由使用這些最為專用的委派,您可以在處理常式中處理 DragEventArgs,並讀取 Data 屬性 (Property),該屬性包含拖曳作業的剪貼簿承載內容。
如需如何使用 XAML 將事件處理常式加入到項目的完整範例,請參閱 HOW TO:處理路由事件。
在使用程式碼建立的應用程式中加入路由事件的處理常式,是相當直接的。路由事件處理常式都可以透過 Helper 方法 AddHandler (這是現有支援呼叫 add 的相同方法) 加入。然而,現有的 WPF 路由事件通常都有 add 和 remove 邏輯的支援實作,讓路由事件的處理常式可以藉由特定語言的事件語法來加入,這跟 Helper 方法比較是更為直覺化的語法。下列是 Helper 方法的使用範例:
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
}
下一個範例顯示 C# 運算子語法 (Visual Basic 因為會處理取值 (Dereferencing) 而使用不同的運算子語法):
void MakeButton2()
{
Button b2 = new Button();
b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
//logic to handle the Click event
}
如需如何在程式碼中加入事件處理常式的範例,請參閱 HOW TO:使用程式碼加入事件處理常式。
如果是使用 Visual Basic,您也可以使用 Handles 關鍵字,將處理常式加入為處理常式宣告的一部分。如需詳細資訊,請參閱 Visual Basic 和 WPF 事件處理。
已處理的概念
所有的路由事件都共用通用的事件資料基底類別:RoutedEventArgs。RoutedEventArgs 會定義採用布林值的 Handled 屬性 (Property)。Handled 屬性的目的在於讓路由過程的每個事件處理常式,可以將路由事件標記為「已處理」(Handled),方法是藉由將 Handled 值設定為 true。當路由過程中的一個項目上的處理常式處理過共用的事件資料後,會再度將資料回報給路由過程的每個接聽項。
Handled 的值會影響到路由事件在路由過程中前進時的回報或處理方式。如果路由事件的事件資料中的 Handled 為 true,則在其他項目上接聽該路由事件的處理常式,通常都不會再針對該特定事件執行個體進行叫用。這對於這兩種情況皆成立:XAML 中附加的處理常式,以及由特定語言事件處理常式附加語法 (例如 += 或 Handles) 所加入的處理常式。對於大部分的一般處理常式案例,藉由將 Handled 設定為 true 來將事件標記為已處理,將會「停止」通道路由或反昇路由的路由傳送,同時包括由類別處理常式於路由點中處理的任何事件的路由傳送。
然而,還是有一個 "handledEventsToo" 機制,可以讓接聽項繼續執行處理常式,以回應事件資料中 Handled 為 true 的路由事件。換句話說,路由事件不會因為將事件資料標記為已處理而真正停止下來。您只可以在程式碼中或是在 EventSetter 中使用 handledEventsToo 機制:
在程式碼中請改為呼叫 WPF 方法 AddHandler(RoutedEvent, Delegate, Boolean) 來加入處理常式,而不使用適用於一般 CLR 事件的特定語言事件語法。指定 handledEventsToo 的值為 true。
在 EventSetter 中,將 HandledEventsToo 屬性 (Attribute) 設定為 true。
除了在路由事件產生 Handled 狀態的行為外,Handled 的概念也隱含意味著您應該如何設計應用程式和撰寫事件處理常式程式碼。您可以將 Handled 的概念,想像為路由事件所公開的簡單通訊協定。至於實際上要如何使用這個通訊協定取決於您自己,不過 Handled 值的設計概念是用於下列情形:
如果路由事件標記為已處理,那麼路由過程的其他項目則不需要再度處理該事件。
如果路由事件未標記為已處理,那麼路由過程中稍早遇到的其他接聽項,不是選擇不要註冊處理常式,不然就是註冊的處理常式選擇不要操作事件資料並將 Handled 設定為 true (或者,當然也有可能是目前的接聽項是路由中的第一點)。目前接聽項的處理常式現在會採取的動作有三個可能:
不進行任何動作:事件維持在未處理,而事件會路由到下一個接聽項。
執行程式碼回應事件,但進行的決定是採取的動作不足以保證讓事件標記為已處理。事件會路由到下一個接聽項。
執行程式碼回應事件。在傳遞給處理常式的事件資料中,將事件標記為已處理,因為採取的動作被認定足夠保證標記為已處理。事件仍然會路由到下一個接聽項,但事件資料中 Handled=true,所有只有 handledEventsToo 接聽項有機會叫用進一步的處理常式。
這個概念設計是藉由前述的路由行為來鞏固的:對於叫用的路由事件,它比較難 (雖然還是有可能以程式碼或樣式的方式達成) 附加處理常式,即使路由過程中稍早的處理常式已將 Handled 設定為 true。
如需 Handled、路由事件的類別處理的詳細資訊,以及適合將路由事件標記為 Handled 的時機建議,請參閱將路由事件標記為已處理以及類別處理。
在應用程式中,常常都只有對引發事件的物件處理反昇路由事件,而完全不考慮事件的路由特性。然而,在事件資料中將路由事件標記為已處理仍然是很好的作法,可以防止未預期的副作用 (Side Effect),以免在項目樹狀結構中前進時所遇到的其他項目,也具有為該相同路由事件附加的處理常式。
類別處理常式
如果定義的類別是以某種方式衍生自 DependencyObject,針對類別的宣告或繼承事件成員的路由事件,您也可以定義和附加其類別處理常式。類別處理常式的叫用時間,是在附加到該類別的執行個體的任何執行個體接聽項處理常式之前,只要是路由事件抵達路由的項目執行個體的時候。
某些 WPF 控制項對於部分路由事件,本身就具有類別處理作用。這可能會造成表面上從未引發路由事件,但實際上卻已經過類別處理,而當您使用某些技術時,您的執行個體處理常式還是可能會處理該路由事件。此外,許多基底類別和控制項會公開可用於覆寫類別處理行為的虛擬方法。如需如何解決不想要的類別處理以及在自訂類別中定義自己的類別處理的詳細資訊,請參閱將路由事件標記為已處理以及類別處理。
在 WPF 中的附加事件
XAML 語言也會定義一種特別的事件類別,稱為「附加事件」(Attached Event)。附加事件可以讓您將特定事件的處理常式加入到任意項目中。處理事件的項目不需要定義或繼承附加事件,而且可能引發事件的物件或處理執行個體的目的端,也不需要定義或用其他方式「擁有」該事件做為成員。
WPF 輸入系統使用附加事件的範圍很廣。然而,幾乎所有的這些附加事件都是透過基底項目轉送的。接著,輸入事件會以對等的非附加路由事件呈現,其中該路由事件為基底項目類別的成員。例如,在任何指定的 UIElement 上,可以更輕鬆處理基礎附加事件 Mouse.MouseDown,方法是藉由在該 UIElement 上使用 MouseDown,而不是在 XAML 或程式碼中處理附加事件語法。
如需 WPF 中的附加事件的詳細資訊,請參閱附加事件概觀。
XAML 中的完整事件名稱
有另一種語法使用方式類似於 typename.eventname 附加事件的語法,但沒有嚴格指出附加事件使用方式,這種語法使用方式的使用時機是為子項目所引發路由事件來附加處理常式的時候。您會將處理常式附加到通用父項目,以利用事件路由的優勢,即使通用父項目可能並沒有相關的路由事件做為成員。請再度考慮這個範例:
<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。然而,加入的處理常式是要給由 Button 類別 (實際上是 ButtonBase,但可以透過繼承供 Button 使用) 所宣告和來引發的路由事件使用。Button「擁有」該事件,但路由事件系統卻允許將任何路由事件的處理常式附加到任何 UIElement 或 ContentElement 執行個體接聽項,而卻另外附加有 Common Language Runtime (CLR) 事件的接聽項。這些完整事件屬性 (Attribute) 名稱的預設 xmlns 命名空間通常是預設 WPF xmlns 命名空間,但您也可以為自訂的路由事件指定前置命名空間。如需 xmlns 的詳細資訊,請參閱 XAML 命名空間和命名空間對應。
WPF 輸入事件
在 WPF 平台內,常見的一個路由事件應用是輸入事件。在 WPF 中,通道路由事件名稱習慣上是以 "Preview" 這個字做為前置字元。輸入事件通常都成對出現,其中一個是反昇事件,而另一個是通道事件。舉例來說,KeyDown 事件和 PreviewKeyDown 事件具有相同的簽章,而前者是反昇輸入事件,後者則是通道輸入事件。有時候,輸入事件只有反昇版本,也或許只有直接路由版本。在說明文件中,路由事件主題間會交互參照相似的路由事件,當有這類路由事件存在時就提供替代路由策略,而 Managed 參考頁面中的章節會釐清每個路由事件的路由策略。
成對出現的 WPF 輸入事件的實作方式,是當發生單一使用者輸入動作 (例如壓下滑鼠按鈕) 時,會依序引發這兩個成對路由事件。首先會引發通道事件,並在其路由中前進。接著會引發反昇事件,並在其路由中前進。這兩個事件會逐一共用相同的事件資料執行個體,因為引發反昇事件的實作類別中的 RaiseEvent 方法呼叫會接聽來自通道事件的事件資料,並在新的引發事件中重複使用該資料。具有通道事件處理常式的接聽項,是第一個有機會將路由事件標記為已處理的 (類別處理常式優先,再來是執行個體處理常式)。如果通道路由過程中的項目已將路由事件標記為已處理,這些已經過處理的事件資料會傳送給反昇事件,且不會叫用為對等反昇輸入事件而附加的一般處理常式。表面上看起來,甚至就好像沒有引發過這個已處理的反昇事件一樣。這個處理行為對於以下情況的控制項複合 (Compositing) 非常有用:當您希望最後的控制項能夠回報所有的點擊測試輸入事件或焦點輸入事件,而非其複合部分。最後的控制項項目在複合中更接近根項目,因而有機會先以類別處理通道事件,或許可以將該路由事件「取代」為更適合控制項的事件,做為支援控制項類別的程式碼部分。
為了說明輸入事件處理是如何進行的,請考慮下列輸入事件範例。在下列樹狀結構圖例中,leaf element #2 依序是 PreviewMouseDown 和 MouseDown 事件的來源。
輸入事件反昇和通道
事件處理的順序如下:
根項目上的 PreviewMouseDown (通道)。
中繼項目 #1 上的 PreviewMouseDown (通道)。
來源項目 #2 上的 PreviewMouseDown (通道)。
來源項目 #2 上的 MouseDown (反昇)。
中繼項目 #1 上的 MouseDown (反昇)。
根項目上的 MouseDown (反昇)。
路由事件處理常式委派會提供兩個物件的參考:引發事件的物件以及叫用處理常式的物件。叫用處理常式的物件即是 sender 參數所回報的物件。首先引發事件的物件則是由事件資料中的 Source 屬性 (Property) 所回報。路由事件還是可以由相同物件來引發和處理,這時 sender 和 Source 就會相同 (這是事件處理範例清單中步驟 3 和 4 的情況)。
由於通道和反昇的原因,父項目接收到的輸入事件中的 Source,是他們的一個子項目。如果很需要知道來源項目為何者,您可以藉由存取 Source 屬性以識別來源項目。
通常,一旦將輸入事件標記為 Handled,就不會叫用進一步的處理常式。一般而言,只要叫用的處理常式能夠解決針對輸入事件的應用程式特定邏輯處理,您就應該將輸入事件標記為已處理。
關於 Handled 狀態的這項一般陳述的例外情況是,特意註冊為要忽略事件資料 Handled 狀態的輸入事件處理常式,還是應該要在任一個路由過程中被叫用時。如需詳細資訊,請參閱預覽事件或將路由事件標記為已處理以及類別處理。
通道和反昇事件間的共用事件資料模型,以及先通道再反昇事件的循序引發方式,並不是對所有路由事件皆成立的概念。該行為的具體實作方式是 WPF 輸入裝置如何選擇引發和連接輸入事件配對組。實作您自己的輸入事件是進階的案例,但您也可以選擇將該模型用於自己的輸入事件。
有些類別選擇以類別處理某些輸入事件,通常是希望重新定義在該控制項內特殊使用者導向的輸入事件所代表的意義為何,以及希望引發新事件。如需詳細資訊,請參閱將路由事件標記為已處理以及類別處理。
如需輸入和一般應用案例中輸入和事件的互動方式的詳細資訊,請參閱輸入概觀。
EventSetters 和 EventTriggers
在樣式中,藉由使用 EventSetter,您可以在標記中包含某些預先宣告的 XAML 事件處理語法。當套用樣式時,就會將參考的處理常式加入到樣式執行個體中。您只能針對路由事件宣告 EventSetter。以下即為一個範例。請注意,這裡參考的 b1SetColor 方法是程式碼後置檔案。
<StackPanel
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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 成為該樣式的一部分,能夠促使程式碼於標記層級重複使用事件。此外,EventSetter 讓處理常式的方法名稱相對於一般應用程式和頁面標記而言更加抽象化。
另一個結合 WPF 的路由事件和動畫功能的特製化語法是 EventTrigger。跟 EventSetter 一樣,只有路由事件可以用於 EventTrigger。一般而言,EventTrigger 是宣告為樣式的一部分,但 EventTrigger 也可以在頁面層級項目上宣告,以做為 Triggers 集合的一部分,或是在 ControlTemplate 中宣告。EventTrigger 可以讓您指定 Storyboard,當路由事件抵達針對該事件宣告 EventTrigger 的路由項目時,即會執行該腳本。相對於僅僅是處理事件和讓它啟動現有 Storyboard 而言,EventTrigger 的好處是 EventTrigger 提供更佳的 Storoyboard 控制以及其執行階段行為。如需詳細資訊,請參閱 HOW TO:在腳本開始後使用事件觸發程式進行控制。
更多關於路由事件的資訊
本主題主要是從這些觀點來討論路由事件:描述基本概念,以及對於各種基底項目和控制項中已經有的路由事件的回應方式和時機。然而,您可以藉著所有必要的支援 (例如特製化的事件資料類別和委派) 來在自訂類別中建立您自己的路由事件。路由事件的擁有者可以是任何類別,但路由事件必須由 UIElement 或 ContentElement 衍生類別所引發和處理,才會好用。如需自訂事件的詳細資訊,請參閱 HOW TO:建立自訂路由事件。