與傳統的桌面開發相比,在事件模型上WPF引入了Routed Events,從開發者的角度上,我們獲得了兩個便利:
1.可以實現事件路由,即向XAML結構中的父元素路由或者是向子元素路由。
2. RoutedEventArgs作為默認的事件Args為我們提供了更多的信息。
事件應用示例
建立工程“RoutedEvent”,初始的代碼修改Grid Layout為StackPanel,添加了一個Button,如下圖:
編寫Click Event Handler:
private void ButtonA_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Debug.WriteLine("Get it"); }
我們有兩種方式為該按鈕添加Click Event Handler:
XAML形式:
<Button Name="ButtonA" Click="ButtonA_Click">ButtonA</Button>
C#形式:
ButtonA.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonA_Click)); ButtonA.Click += ButtonA_Click; //這樣亦可,但只能用於該控件支持的事件
結果:
注意:一個事件,同一個handler添加多次的結果如下圖,是引發Bug的誘因之一:
RoutedEventArgs
從上一個例子我們可以看到,WPF事件機制默認提供的EventArgs為RoutedEventArgs
public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);
與EventArgs相比,Routed Event提供了4個新的屬性.
RoutedEvent:可以獲取到時間的類型,可以在一個Handler處理多個不同類型事件時用上。
其它三個都與事件路由相關:
Handled,指定sender是否對該事件繼續路由,接下來的例子會展示它的作用。
Source,該事件的觸發源。
OriginalSource,通常與Source為同一個值。
事件路由
以事件路由模式來分類,WPF提供了3種事件路由模式:
- Direct (不路由)
- Bubbling events(向上路由)
- Tunneling events(向下路由)
對於一個事件,如果不清楚它的路由模式,可以MSDN查一下:比如:MSDN上關於Click的說明:
直接以例子來說明事件路由的作用:我們修改一下XAML代碼,如下:
<StackPanel> <Button Name="ButtonA">ButtonA</Button> <Button Name="ButtonB">ButtonB</Button> <Button Name="ButtonC">ButtonC</Button> <Button Name="ButtonD">ButtonD</Button> <Button Name="ButtonE">ButtonE</Button> <Button Name="ButtonF">ButtonF</Button> <Button Name="ButtonG">ButtonG</Button> <Button Name="ButtonH">ButtonH</Button> </StackPanel>
現在界面上有了8個Button,如果需要為8個按鈕都做事件處理,那要怎么做呢?通過事件路由我們可以很優雅的解決:
由於Click為向上路由的事件,我們隨便找它的一個父元素,比如stackPanel,填加一句代碼就好了:
<StackPanel Button.Click="Button_Click">
以下為Handler代碼:
private void Button_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Debug.WriteLine("Get Info From {0}", e.Source); }
結果:
為頂層Window添加同樣的代碼會得到相同的結果,因為事件將一直路由到頂層。
最后,回顧一下剛才RoutedEventArgs中的Handled.
接下來的例子說明了如何通過設置Handled阻礙事件路由:
XAML代碼:
<Window x:Class="RoutedEvent.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" Button.Click="Button_Click"> <StackPanel Name="StackPanelA"> <Button Name="ButtonA">ButtonA</Button> <Button Name="ButtonB">ButtonB</Button> <Button Name="ButtonC">ButtonC</Button> <Button Name="ButtonD">ButtonD</Button> <Button Name="ButtonE">ButtonE</Button> <Button Name="ButtonF">ButtonF</Button> <Button Name="ButtonG">ButtonG</Button> <Button Name="ButtonH">ButtonH</Button> </StackPanel> </Window>
C#代碼:
public MainWindow() { InitializeComponent(); StackPanelA.AddHandler(Button.ClickEvent, new RoutedEventHandler(StackPanel_Click), true); } private void Button_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Debug.WriteLine("Get Info From {0}", e.Source); } private void StackPanel_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Debug.WriteLine("I will block the routing", e.Source); e.Handled = true; }
結果:事件路由將到了StackPanel即被阻礙,Window的對於Click 的Handler將不會觸發,大家可以試試。