与传统的桌面开发相比,在事件模型上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的诱因之一:
从上一个例子我们可以看到,WPF事件机制默认提供的EventArgs为RoutedEventArgs
public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);
与EventArgs相比,Routed Event提供了4个新的属性.
RoutedEvent:可以获取到时间的类型,可以在一个Handler处理多个不同类型事件时用上。
其它三个都与事件路由相关:
Handled,指定sender是否对该事件继续路由,接下来的例子会展示它的作用。
Source,该事件的触发源。
OriginalSource,通常与Source为同一个值。
以事件路由模式来分类,WPF提供了3种事件路由模式:
对于一个事件,如果不清楚它的路由模式,可以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将不会触发,大家可以试试。