1、什麼是路由事件windows
路由事件是一種能夠針對元素樹中的多個偵聽器而不是僅僅針對引起該事件的對象調用處理程序的事件。路由事件是一個CLR事件。spa
路由事件與通常事件的區別在於:路由事件是一種用於元素樹的事件,當路由事件觸發後,它能夠向上或向下遍歷可視樹和邏輯樹,他用一種簡單而持久的方式在每一個元素上觸發,而不須要任何定製的代碼(若是用傳統的方式實現一個操做,執行整個事件的調用則須要執行代碼將事件串聯起來)。.net
路由事件的路由策略:3d
所謂的路由策略就是指:路由事件實現遍歷元素的方式。code
路由事件通常使用如下三種路由策略:1) 冒泡:由事件源向上傳遞一直到根元素。2) 直接:只有事件源纔有機會響應事件。3) 隧道:從元素樹的根部調用事件處理程序並依次向下深刻直到事件源。通常狀況下,WPF提供的輸入事件都是以隧道/冒泡對實現的。隧道事件經常被稱爲Preview事件。xml
一、冒泡對象
XAML代碼以下:blog
1 <Window x:Class="WpfRouteEventByBubble.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="190" Width="246" WindowStartupLocation="CenterScreen"> 5 <Grid x:Name="GridRoot" Background="Lime"> 6 <Grid x:Name="GridA" Margin="10" Background="Blue"> 7 <Grid.ColumnDefinitions> 8 <ColumnDefinition></ColumnDefinition> 9 <ColumnDefinition></ColumnDefinition> 10 </Grid.ColumnDefinitions> 11 <Canvas x:Name="CanvasLeft" Grid.Column="0" Background="Red" Margin="10"> 12 <Button x:Name="ButtonLeft" Width="65" Height="100" Margin="10" Content="Left"></Button> 13 </Canvas> 14 <Canvas x:Name="CanvasRight" Grid.Column="1" Background="Yellow" Margin="10"> 15 <Button x:Name="ButtonRight" Width="65" Height="100" Margin="10" Content="Right"></Button> 16 </Canvas> 17 </Grid> 18 </Grid> 19 </Window>
運行效果以下所示:事件
當單擊Left按鈕的時候,Button.Click事件被觸發,而且沿着ButtonLeft→CanvasLeft→GridA→GridRoot→Window這條路線向上傳遞,當單擊Right按鈕就會沿着ButtonRight→CanvasRight→GridA→GridRoot→Window這條路線向上傳遞,這裏尚未添加監聽器,因此是沒有反應的。路由
如何加入監聽器,咱們能夠再XAML中添加,XAML代碼以下:
1 <Window x:Class="WpfRouteEventByBubble.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="190" Width="246" WindowStartupLocation="CenterScreen"> 5 <Grid x:Name="GridRoot" Background="Lime" Button.Click="Button_Click"> 6 <Grid x:Name="GridA" Margin="10" Background="Blue" Button.Click="Button_Click"> 7 <Grid.ColumnDefinitions> 8 <ColumnDefinition></ColumnDefinition> 9 <ColumnDefinition></ColumnDefinition> 10 </Grid.ColumnDefinitions> 11 <Canvas x:Name="CanvasLeft" Grid.Column="0" Background="Red" Margin="10" Button.Click="Button_Click"> 12 <Button x:Name="ButtonLeft" Width="65" Height="100" Margin="10" Content="Left" Button.Click="Button_Click"></Button> 13 </Canvas> 14 <Canvas x:Name="CanvasRight" Grid.Column="1" Background="Yellow" Margin="10" Button.Click="Button_Click"> 15 <Button x:Name="ButtonRight" Width="65" Height="100" Margin="10" Content="Right" Button.Click="Button_Click"></Button> 16 </Canvas> 17 </Grid> 18 </Grid> 19 </Window>
咱們在XAML代碼中添加了Button.Click="Button_Click"這個事件處理器,就是監聽器,而且事件處理交由Button_Click負責,後臺Button_Click代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 16 namespace WpfRouteEventByBubble 17 { 18 /// <summary> 19 /// MainWindow.xaml 的交互邏輯 20 /// </summary> 21 public partial class MainWindow : Window 22 { 23 public MainWindow() 24 { 25 InitializeComponent(); 26 } 27 28 private void Button_Click(object sender, RoutedEventArgs e) 29 { 30 MessageBox.Show("我到達了:" + (sender as FrameworkElement).Name); 31 } 32 } 33 }
咱們分析一下,那兩個參數究竟是什麼呢?
咱們會發現,當點擊button按鈕時,ButtonLeft、CanvasLeft、GridA、GridRoot中的事件都會觸發,這就是冒泡路由策略的功能所在,事件首先在源元素上觸發,而後從每個元素向上沿着樹傳遞,直到到達根元素爲止(或者直處處理程序把事件標記爲已處理爲止),從而調用這些元素中的路由事件。
若是把Button_Click事件修改成:
1 private void Button_Click(object sender, RoutedEventArgs e) 2 { 3 MessageBox.Show("我到達了:" + (sender as FrameworkElement).Name); 4 e.Handled = true;//讓事件中止冒泡 5 }
則以上事件就不會沿着ButtonLeft→CanvasLeft→GridA→GridRoot→Window這條路線傳遞下去,只會執行ButtonLeft的事件。
2、管道
事件首先是從根元素上被觸發,而後從每個元素向下沿着樹傳遞,直到到達根元素爲止(或者直到到達處理程序把事件標記爲已處理爲止),他的執行方式正好與冒泡策略相反。
XAML代碼以下;
1 <Window x:Class="Wpf路由事件管道策略.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen" PreviewMouseDown="Window_PreviewMouseDown"> 5 <Grid x:Name="grid" PreviewMouseDown="grid_PreviewMouseDown"> 6 <Button Height="30" Width="100" Content="點擊我" PreviewMouseDown="Button_PreviewMouseDown"></Button> 7 </Grid> 8 </Window>
後臺代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 16 namespace Wpf路由事件管道策略 17 { 18 /// <summary> 19 /// MainWindow.xaml 的交互邏輯 20 /// </summary> 21 public partial class MainWindow : Window 22 { 23 public MainWindow() 24 { 25 InitializeComponent(); 26 } 27 28 private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e) 29 { 30 MessageBox.Show("windows被點擊"); 31 } 32 33 private void grid_PreviewMouseDown(object sender, MouseButtonEventArgs e) 34 { 35 MessageBox.Show("grid被點擊"); 36 } 37 38 private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) 39 { 40 MessageBox.Show("button被點擊"); 41 } 42 } 43 }
程序運行效果:
特別值得注意的是:管道事件按照慣例,他們的名字中都有一個preview前綴,通常來講管道事件都有他的配對的冒泡事件,例如:PreviewMouseDown和MouseDown就是配對事件,若是同時存在的話,那麼就會先執行管道事件而後才執行配對的冒泡事件。固然e.Handled=true,依然可以阻斷事件。
3、直接策略
事件僅僅在源元素上觸發,這個與普通的.Net事件的行爲相同,不一樣的是這樣的事件仍然會參與一些路由事件的特定機制,如事件觸發器等。
該事件惟一可能的處理程序是與其掛接的委託。
路由事件的事件處理程序的簽名(即方法的參數):
他與通用的.net事件處理程序的模式一致,也有兩個參數:第一個爲:System.Object對象,名爲sender,第二個參數(通常名爲e)是一個派生於System.EventArgs的類。sender參數就是該處理程序被添加的元素,參數e是RoutedEventArgs的一個實例提供了4個有用的屬性:
Source---邏輯樹中開始觸發該事件的的元素。
originalSource--可視樹中一開始觸發該事件的元素。
handled---布爾值,設置爲true表示事件已處理,在這裏中止。
RoutedEvent---真正的路由事件對象,(如Button.ClickEvent)當一個事件處理程序同時用於多個路由事件時,它能夠有效地識別被出發的事件。