
前言
1、什么是路由事件?
路由事件 是一种能在 元素树(Visual Tree) 中沿着传播方向(向上/向下)被多个元素处理的事件。
传统的 .NET 事件:事件只会在触发它的控件上被处理。
WPF 路由事件:事件可以从一个元素"路由"到其他元素。
本质:解决了 UI 树中深层嵌套控件的通信与事件处理问题。
2、三种路由策略

3、事件参数与传播控制
所有路由事件都使用 RoutedEventArgs(或其派生类):
csharp
public class RoutedEventArgs : EventArgs
{
public RoutedEvent RoutedEvent { get; set; } // 当前事件
public object Source { get; set; } // 逻辑树上的源
public object OriginalSource { get; set; } // 视觉树上的真实源
public bool Handled { get; set; } // 是否已处理
}
重要方法:e.Handled = true 可以阻止事件继续传播。
4、系统常用路由事件(你遇到过/提到的)

4.1、冒泡事件
4.1.1(系统自带的冒泡事件)

冒泡事件的传播方向是:从触发事件的元素开始,沿着 UI 树向上(向根元素)逐级传递,就像气泡从水底往水面上升一样,事件从深层控件一层层往上"冒"到顶层窗口,比如一个StackPanel中放了Buttton,从Button传到StackPanel就是冒泡事件,冒泡事件一般在统一处理多个子控件的相同操作时需要使用冒泡事件。ButtonBase类中的Click是一个冒泡事件,当点击按钮"保存"、"删除" 、"编辑"时,触发冒泡事件,这时候事件被传递到上一层StackPanel,又因为StackPanel监听了
ButtonBase.Click事件,所以就能看到StackPanel_Click方法被触发。
csharp
<StackPanel Grid.Column=" 2" ButtonBase.Click="StackPanel_Click">
<Button Content="保存" Tag="Save"/>
<Button Content="删除" Tag="Delete"/>
<Button Content="编辑" Tag="Edit"/>
</StackPanel>
csharp
private void StackPanel_Click(object sender, RoutedEventArgs e)
{
}
4.1.2(自定义的冒泡事件)
1)注册路由事件
路由事件也是定义在控件自身的类当中,使用EventManager.RegisterRoutedEvent方法注册路由事件,该方法第一个参数是"事件名称";第二个参数是路由策略,路由策略分为冒泡、隧道、直接三种;第3个参数是委托类型,这个委托类型不是固定的,RoutedEventHandler是标准的写法;第4个参数是事件所在的类。
2)添加CLR事件包装器
事件包装器与属性包装器类似,它是为路由事件提供一个标准的 .NET 事件访问接口,让开发者能够用熟悉的方式使用事件。
3)触发自定义路由事件
重写OnClick方法,首先调用base.OnClick()让标准的Click事件进行触发,接下来调用 RaiseEvent(new RoutedEventArgs(MyClickEvent, this));从而让自定义的路由事件MyClick触发。
csharp
public class MyButton : Button
{
// 1. 注册路由事件(静态只读)
public static readonly RoutedEvent MyClickEvent =
EventManager.RegisterRoutedEvent(
name: "MyClick", // 事件名称
routingStrategy: RoutingStrategy.Bubble, // 路由策略
handlerType: typeof(RoutedEventHandler), // 委托类型
ownerType: typeof(MyButton) // 所属类型
);
// 2. 添加CLR事件包装器(供代码使用 += / -=)
public event RoutedEventHandler MyClick
{
add { AddHandler(MyClickEvent, value); }
remove { RemoveHandler(MyClickEvent, value); }
}
// 3. 触发事件的受保护方法(供派生类调用)
protected override void OnClick()
{
base.OnClick(); // 先触发原生 Click 事件
RaiseEvent(new RoutedEventArgs(MyClickEvent, this));
}
}
xaml界面代码
下面的代码中
csharp
<StackPanel Grid.Column=" 0" Orientation="Horizontal" local:MyButton.MyClick="StackPanel_MyClick" Background="Red" >
<local:MyButton Content="点我" Width="100" Height="30"/>
</StackPanel >
private void StackPanel_MyClick(object sender, RoutedEventArgs e)
{
}
当点击按钮"点我"时,会触发MyClick路由事件,由于StackPanel监听了MyClick事件,所以StackPanel_MyClick方法就会被执行。

4.2、隧道事件
4.3.1 (系统自带的隧道事件)
PreviewMouseDown是系统自带的隧道事件,它在鼠标按钮被按下时发生,从根元素向下传递到实际被点击的元素。
下面的代码中StackPanel和"按钮2"都监听了PreviewMouseDown事件,当点击"按钮1"时,由于隧道事件从根元素开始传递,所以StackPanel的StackPanel_PreviewMouseDown方法先执行;当点击"按钮2"时,由于隧道事件从根元素开始传递,所以StackPanel的StackPanel_PreviewMouseDown方法先执行,然后"按钮2"的Button_PreviewMouseDown方法再执行,这样就能看出隧道事件都是先从根元素开始触发的。
csharp
<StackPanel Grid.Column=" 3" PreviewMouseDown="StackPanel_PreviewMouseDown">
<Button Content="按钮1"/>
<Button Content="按钮2" PreviewMouseDown="Button_PreviewMouseDown"/>
</StackPanel>
csharp
private void StackPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("隧道StackPanel_PreviewMouseDown");
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("隧道Button_PreviewMouseDown");
}