WPF 為我們提供了許多不同的事件處理機制——它們是冒泡、隧道和直接的。這些都稱為路由事件
直接事件
直接在事件源上處理,這個有點像WinForms中的按鈕OnClick事件,直接在事件處理程序中處理業務
冒泡事件
當事件沒有被元素(比如一個文本框)處理并且事件“冒泡”到持有它的 UI 容器時,就會發生冒泡
例如,假設你有一個包含StackPanel的窗口,在該面板內你有一個Grid,在該Grid內您有一個文本框。如果文本框未處理事件,則它會冒泡到Grid級別,如果未在該級別處理,則事件會進一步冒泡(稱為可視化樹)到可能會或可能不會被處理的面板。這個過程一直持續到它被處理或事件脫離了最頂層的元素
隧道事件
隧道與冒泡相反,事件不是沿著可視樹向上傳播,而是沿著可視樹向下傳播到源元素。隧道事件都以Preview開頭,例如 PreviewDownKey和PreviewMouseButtonDown。可以在它們到達目標元素的途中捕獲它們并進行處理
事件對
大多數冒泡事件都與隧道事件配對。例如,PreviewKeyDown(隧道)鍵事件與Keydown 事件(冒泡)配對。當隧道事件沿著視覺樹向下工作并命中它起源的元素(比如一個按鈕)時,如果它沒有處理,那么該按鈕將啟動一個“Keydown”冒泡事件,該事件會在視覺樹上冒泡直到它自己被處理
處理事件
每種類型的事件處理程序都傳遞一個RoutedEventArgs對象。該對象有三個常用屬性,Handled屬性和Source屬性以及OriginalSource屬性。將Handled設置為 true,并且任何沿可視化樹的事件處理程序將不再處理該事件
前面我們的示例描述了一個指向文本框的隧道KeyDown事件。如果包含UI元素(窗口、面板或表格)捕獲并處理隧道事件(通過將 Handled 屬性設置為 true),則文本框將不會接收該事件,并且不會觸發任何匹配的冒泡事件
<Window x:Class="Example_11.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:Example_11"mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" Keyboard.PreviewKeyDown="Window_PreviewKeyDown"Keyboard.KeyDown="Window_KeyDown"><StackPanel Height="300" Width="400" Keyboard.PreviewKeyDown="StackPanel_PreviewKeyDown"Keyboard.KeyDown="StackPanel_KeyDown"><Grid Height="250" Width="300"Keyboard.PreviewKeyDown="Grid_PreviewKeyDown"Keyboard.KeyDown="Grid_KeyDown"><Grid.RowDefinitions><RowDefinition></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><TextBox Grid.Row="0" Width="200" Height="30"></TextBox><Button Grid.Row="1" Width="200" Height="50">Button</Button></Grid></StackPanel>
</Window>
using System.Windows;
using?System.Windows.Input;
namespace Example_11
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Window_PreviewKeyDown(object sender, KeyEventArgs e){MessageBox.Show("Window_PreviewKeyDown");//e.Handled = true;}private void StackPanel_PreviewKeyDown(object sender, KeyEventArgs e){MessageBox.Show("StackPanel_PreviewKeyDown");//e.Handled = true;}private void Grid_PreviewKeyDown(object sender, KeyEventArgs e){MessageBox.Show("Grid_PreviewKeyDown");//e.Handled = true;}private void Grid_KeyDown(object sender, KeyEventArgs e){MessageBox.Show("Grid_KeyDown");//e.Handled = true;}private void Window_KeyDown(object sender, KeyEventArgs e){MessageBox.Show("Window_KeyDown");//e.Handled = true;}private void StackPanel_KeyDown(object sender, KeyEventArgs e){MessageBox.Show("StackPanel_KeyDown");//e.Handled = true;}}
}
當我們向TextBox輸入值時,會看到事件的執行順序為:
Window_PreviewKeyDown
StackPanel_PreviewKeyDown
Grid_PreviewKeyDown
Grid_KeyDown
StackPanel_KeyDown?
Window_KeyDown
在本文中,我們看到 WPF 中的用戶界面事件可以是隧道、冒泡或直接事件。針對隧道和冒泡事件,我們可以捕捉、處理事件或讓事件繼續執行。我們可以通過捕獲隧道事件并將 Handled 屬性設置為 true 來阻止息到達控件