為了降低由事件訂閱帶來的耦合度,和代碼量,WPF推出了路由事件機制,路由事件與直接事件的區別在于,直接事件激發時,發送者直接將訊息通過事件訂閱者交給事件回應者,事件回應者對事件的發生做出回應,路由事件的訂閱者和回應者之間沒有直接顯式的訂閱關系,事件的擁有者只負責激發事件,事件由誰回應它并不知道,事件回應者通過事件偵聽器進行偵聽,本文以一些簡單的小例子,簡述路由事件的基本使用,僅供學習分享使用,如有不足之處,還請指正,
什么是路由事件?
根據MSDN定義:
- 功能定義:路由事件是一種可以針對元素樹中的多個偵聽器(而不是僅針對引發該事件的物件)呼叫處理程式的事件,
- 實作定義:路由事件是由 類的實體支持的 CLR 事件, RoutedEvent 由事件 Windows Presentation Foundation (WPF) 系統處理,
典型的 WPF 應用程式中包含許多元素, 無論這些元素是在代碼中創建還是在 XAML 中宣告,它們存在于彼此關聯的元素樹關系中,
路由策略
路由事件使用以下三種路由策略之一:
-
Bubbling【冒泡】: 呼叫事件源上的事件處理程式, 路由事件隨后會路由到后續的父級元素,直到到達元素樹的根, 大多數路由事件都使用冒泡路由策略, 冒泡路由事件通常用于報告來自不同控制元件或其他 UI 元素的輸入或狀態變化,
-
Direct【直接】: 只有源元素本身才有機會呼叫處理程式以進行回應, 這類似于表單用于事件的Windows路由", 但是,與標準 CLR 事件不同,直接路由事件支持類處理 (類處理在即將發布的) 節中進行了說明,并且 和 可以使用 EventSetter或 EventTrigger ,
-
Tunneling【隧道】: 最初將呼叫元素樹的根處的事件處理程式, 隨后,路由事件將朝著路由事件的源節點元素(即引發路由事件的元素)方向,沿路由線路傳播到后續的子元素, 合成控制元件的程序中通常會使用或處理隧道路由事件,通過這種方式,可以有意地禁止復合部件中的事件,或者將其替換為特定于整個控制元件的事件,
冒泡策略
事件的冒泡策略,就像水里的泡泡一樣,從底往上,逐級觸發,路由事件隨后會路由到后續的父級元素,直到到達元素樹的根, 大多數路由事件都使用冒泡路由策略, 冒泡路由事件通常用于報告來自不同控制元件或其他 UI 元素的輸入或狀態變化,

路由事件會像泡泡一樣,逐層回應,示例如下所示:

示例原始碼
1 <Window x:Class="WpfApp1.OneWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp1" 7 mc:Ignorable="d" 8 x:Name="w1" 9 Title="OneWindow" Height="350" Width="500" Button.Click="btn1_Click"> 10 <Grid x:Name="gdOuter" Button.Click="btn1_Click"> 11 <StackPanel x:Name="sp1" Button.Click="btn1_Click"> 12 <Grid x:Name="gd1" Button.Click="btn1_Click"> 13 <Button x:Name="btn1" Content="點我" Click="btn1_Click" Margin="5" Padding="5" FontSize="18"></Button> 14 </Grid> 15 <RichTextBox x:Name="txtInfo" Margin="5" MinHeight="100" ></RichTextBox> 16 </StackPanel> 17 </Grid> 18 </Window>
隧道策略
隧道策略事件,與冒泡策略剛好相反, 最初將呼叫元素樹的根處的事件處理程式, 隨后,路由事件將朝著路由事件的源節點元素(即引發路由事件的元素)方向,沿路由線路傳播到后續的子元素,
所有的隧道策略模式事件,都是以Preview開頭,隧道事件有時又稱作預覽事件,這是由該對所使用的命名約定決定的,
隧道策略由頂至下,逐層下探,直至最好一個元素,如下所示:

示例原始碼
1 <Window x:Class="WpfApp1.TwoWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp1" 7 mc:Ignorable="d" 8 Name="w1" 9 Title="TwoWindow" Height="350" Width="500" Button.PreviewMouseDown="btn1_PreviewMouseDown"> 10 <Grid> 11 <Grid x:Name="gdOuter" Button.PreviewMouseDown="btn1_PreviewMouseDown"> 12 <StackPanel x:Name="sp1" Button.PreviewMouseDown="btn1_PreviewMouseDown"> 13 <Grid x:Name="gd1" Button.PreviewMouseDown="btn1_PreviewMouseDown"> 14 <Button x:Name="btn1" Content="點我" PreviewMouseDown="btn1_PreviewMouseDown" Margin="5" Padding="5" FontSize="18"></Button> 15 </Grid> 16 <RichTextBox x:Name="txtInfo" Margin="5" MinHeight="100" ></RichTextBox> 17 </StackPanel> 18 </Grid> 19 </Grid> 20 </Window>
注意:在 WPF 中提供的輸入事件通常是以隧道/浮升對實作的,
事件阻止
在實際應用中,如果不想事件采用冒泡或隧道策略,向上或向下執行,則需要設定e.Handled=true即可,如下所示:

示例原始碼:
1 private void btn1_Click(object sender, RoutedEventArgs e) 2 { 3 this.txtInfo.AppendText(string.Format("當前回應事件物件:{0},回應事件原始物件:{1}\r\n", (sender as FrameworkElement).Name, e.OriginalSource)); 4 e.Handled = true; 5 }
后臺添加路由事件
路由事件既可以通過XAML的方式,進行設定,也可以通過后臺代碼的方式進行設定,如下所示:

后臺設定路由事件,如下所示:
1 /// <summary> 2 /// ThreeWindow.xaml 的互動邏輯 3 /// </summary> 4 public partial class ThreeWindow : Window 5 { 6 public ThreeWindow() 7 { 8 InitializeComponent(); 9 this.btn1.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn1_Click)); 10 this.gd1.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn1_Click)); 11 this.sp1.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn1_Click)); 12 this.gdOuter.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn1_Click)); 13 this.w1.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn1_Click)); 14 } 15 16 17 private void btn1_Click(object sender, RoutedEventArgs e) 18 { 19 this.txtInfo.AppendText(string.Format("當前回應事件物件:{0},回應事件原始物件:{1}\r\n", (sender as FrameworkElement).Name, e.OriginalSource)); 20 //e.Handled = true; 21 } 22 }
自定義路由事件
創建自定義路由事件,步驟如下:
- 宣告并注冊路由事件,
- 為路由事件添加CLR事件包裝器,
- 創建可以激發事件的方法,
具體操作步驟如下:
首先創建的自定義控制元件,繼承自Button按鈕,如下所示:
1 namespace WpfApp1 2 { 3 /// <summary> 4 /// 自定義路由事件 5 /// </summary> 6 public class TimeButton:Button 7 { 8 /// <summary> 9 /// 宣告和注冊路由事件 10 /// </summary> 11 public static readonly RoutedEvent TimeEvent = EventManager.RegisterRoutedEvent("Time", RoutingStrategy.Bubble, typeof(EventHandler<TimeEventArgs>), typeof(TimeButton)); 12 13 /// <summary> 14 /// 事件包裝器 15 /// </summary> 16 public event RoutedEventHandler Time{ 17 add { this.AddHandler(TimeEvent, value); } 18 remove 19 { 20 this.RemoveHandler(TimeEvent, value); 21 } 22 } 23 24 /// <summary> 25 /// 重寫方法,激發事件 26 /// </summary> 27 protected override void OnClick() 28 { 29 base.OnClick(); 30 TimeEventArgs e = new TimeEventArgs(TimeEvent,this); 31 e.ClickTime = DateTime.Now; 32 this.RaiseEvent(e); 33 } 34 35 } 36 37 public class TimeEventArgs : RoutedEventArgs { 38 public TimeEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { 39 40 } 41 42 public DateTime ClickTime { get; set; } 43 } 44 }
在表單中,創建自定義按鈕實體,如下所示:
1 <Window x:Class="WpfApp1.A1Window" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp1" 7 mc:Ignorable="d" 8 Title="A1Window" Height="450" Width="800"> 9 <Grid x:Name="gd1" local:TimeButton.Time="timeButton1_Time"> 10 <StackPanel x:Name="sp1" local:TimeButton.Time="timeButton1_Time"> 11 <local:TimeButton x:Name="timeButton1" Content="報時" Time="timeButton1_Time"></local:TimeButton> 12 <RichTextBox x:Name="txtInfo" Margin="5" MinHeight="100" ></RichTextBox> 13 </StackPanel> 14 </Grid> 15 </Window>
然后實作事件函式,如下所示:
1 private void timeButton1_Time(object sender, TimeEventArgs e) 2 { 3 this.txtInfo.AppendText(string.Format("當前回應事件物件:{0},回應事件時間為:{1}\r\n", (sender as FrameworkElement).Name, e.ClickTime.ToString("yyyy-MM-dd hh:mm:ss.fff"))); 4 }
自定義路由事件,示例截圖如下:

以上就是WPF路由事件的簡單用法,旨在拋磚引玉,共同學習,一起進步,
備注
題李凝幽居
【作者】賈島 【朝代】唐閑居少鄰并,草徑入荒園,鳥宿池邊樹,僧敲月下門,
過橋分野色,移石動云根,暫去還來此,幽期不負言,

作者:Alan.hsiang
出處:http://www.cnblogs.com/hsiang/
本文著作權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段宣告,且在文章頁面明顯位置給出原文連接,謝謝,
關注個人公眾號,定時同步更新技術及職場文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/343859.html
標籤:.NET技术
