理解路由事件
路由事件是一種可以針對元素樹中的多個偵聽器而不是僅僅針對引發(fā)該事件的對象調用處理程序的事件唆途,也就是說蹬挤,觸發(fā)事件源的父級或子級如果都有對該事件的監(jiān)聽阴孟,則都能觸發(fā)事件
路由事件與一般事件的區(qū)別在于:路由事件是一種用于元素樹的事件彤避,當路由事件觸發(fā)后荐类,它可以向上或向下遍歷可視樹和邏輯樹肃叶,他用一種簡單而持久的方式在每個元素上觸發(fā)蹂随,而不需要任何定制的代碼(如果用傳統的方式實現一個操作,執(zhí)行整個事件的調用則需要執(zhí)行代碼將事件串聯起來)
路由事件的路由策略
所謂的路由策略就是指:路由事件實現遍歷元素的方式
路由事件一般使用以下三種路由策略:
- 冒泡:由事件源向上傳遞一直到根元素
- 直接:只有事件源才有機會響應事件
- 隧道:從元素樹的根部調用事件處理程序并依次向下深入直到事件源
一般情況下因惭,WPF提供的輸入事件都是以隧道/冒泡對實現的岳锁。隧道事件常常被稱為Preview事件
冒泡
程序設計思路是,將多個Grid嵌套起來蹦魔,構成父子結構激率,在最底層支Grid上定義一個按鈕咳燕,綁定一個單擊事件,并且按鈕的所有低級元素都綁定該事件乒躺,它們使用同一個事件處理程序招盲;再定義一個ListBox,當事件處理程序執(zhí)行的時候嘉冒,打印出事件激發(fā)者的名字曹货,這樣便能看到事件執(zhí)行的順序
下圖是界面設計:
下面是XMAL代碼,按鈕和每一個Grid都綁定了Button.Click="Btn_Click"
<Window x:Class="WPF_CODE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<!--最外層的Grid-->
<Grid Button.Click="Btn_Click" Name="Grid_1" Background="#FF43AEAE">
<!--定義兩行-->
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--第二層Grid-->
<Grid Button.Click="Btn_Click" Name="Grid_2" Margin="10" Background="#FF8D888D" Grid.Row="0">
<!--定義兩列-->
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--第三層左側Grid-->
<Grid Button.Click="Btn_Click" Name="Grid_3_Left" Grid.Column="0" Background="#FF3D8F3A" Margin="10">
<!--添加一個按鈕-->
<Button Button.Click="Btn_Click" Name="ButtonLeft" Width="80" Height="50" Content="Hello"/>
</Grid>
<!--第三層右側Grid-->
<Grid Button.Click="Btn_Click" Name="Grid_3_Right" Grid.Column="1" Background="#FFC95E3E" Margin="10">
<!--添加一個按鈕-->
<Button Button.Click="Btn_Click" Name="ButtonRight" Width="80" Height="50" Margin="10" Content="World"></Button>
</Grid>
</Grid>
<!--定義一個ListBox健爬,用于輸出結果-->
<ListBox Name="Print_List" Grid.Row="1"/>
</Grid>
</Window>
下面是后端代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF_CODE
{
/// <summary>
/// MainWindow.xaml 的交互邏輯
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// 事件處理程序
private void Btn_Click(object sender, RoutedEventArgs e)
{
//sender是指由誰激發(fā)了這個事件處理程序控乾,它可以獲取到觸發(fā)對象
//通過(sender as FrameworkElement).Name轉換,將觸發(fā)控件的名稱拿出來娜遵,
string message = "觸發(fā)者:"+(sender as FrameworkElement).Name.ToString();
this.Print_List.Items.Add(message);
//e包含了與事件相關的一些參數蜕衡,e.Handled如果設置為True,則表示冒泡不再繼續(xù)
//e.Handled = true;
}
}
}
運行程序设拟,點擊左側按鈕慨仿,結果如下:
點擊右側按鈕,結果如下:
可以看出纳胧,事件首先在源元素上觸發(fā)镰吆,然后從每一個元素向上沿著樹傳遞,直到到達根元素為止(或者直到處理程序把事件標記為已處理為止)跑慕,從而調用這些元素中的路由事件
如果將事件處理程序里的e.Handled = true;
代碼放開万皿,效果如下
//e包含了與事件相關的一些參數,e.Handled如果設置為True核行,則表示冒泡不再繼續(xù)
e.Handled = true;
即事件處理程序只要被觸發(fā)一次牢硅,并不會發(fā)生冒泡
隧道
隧道的執(zhí)行順序與冒泡正好相反,直接看例子芝雪,然后再解釋减余,將上便中所有的Click事件改為PreviewMouseDown事件,而綁定的事件處理程序不變
XAML代碼如下:
<Window x:Class="WPF_CODE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<!--最外層的Grid-->
<Grid PreviewMouseDown="Btn_Click" Name="Grid_1" Background="#FF43AEAE">
<!--定義兩行-->
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--第二層Grid-->
<Grid PreviewMouseDown="Btn_Click" Name="Grid_2" Margin="10" Background="#FF8D888D" Grid.Row="0">
<!--定義兩列-->
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--第三層左側Grid-->
<Grid PreviewMouseDown="Btn_Click" Name="Grid_3_Left" Grid.Column="0" Background="#FF3D8F3A" Margin="10">
<!--添加一個按鈕-->
<Button PreviewMouseDown="Btn_Click" Name="ButtonLeft" Width="80" Height="50" Content="Hello"/>
</Grid>
<!--第三層右側Grid-->
<Grid PreviewMouseDown="Btn_Click" Name="Grid_3_Right" Grid.Column="1" Background="#FFC95E3E" Margin="10">
<!--添加一個按鈕-->
<Button PreviewMouseDown="Btn_Click" Name="ButtonRight" Width="80" Height="50" Margin="10" Content="World"></Button>
</Grid>
</Grid>
<!--定義一個ListBox惩系,用于輸出結果-->
<ListBox Name="Print_List" Grid.Row="1"/>
</Grid>
</Window>
運行程序位岔,查看效果:
可以看出,隧道是指事件首先是從根元素上被觸發(fā)堡牡,然后從每一個元素向下沿著樹傳遞抒抬,直到到達根元素為止(或者直到到達處理程序把事件標記為已處理為止),他的執(zhí)行方式正好與冒泡策略相反
所有的隧道事件都以Preview
開頭
后臺代碼記得將e.Handled=true
注釋掉
直接策略
事件僅僅在源元素上觸發(fā)晤柄,這個與普通的.Net事件的行為相同擦剑,不同的是這樣的事件仍然會參與一些路由事件的特定機制,如事件觸發(fā)器等;該事件唯一可能的處理程序是與其掛接的委托