WPF路由事件
一、路由事件的核心概念
路由事件(Routed Event)是 WPF 引入的事件系统,与传统 CLR 事件的主要区别在于:它可以在元素树中 "传播",允许多个元素对同一个事件进行处理。
元素树:WPF 的 UI 控件通过嵌套形成树形结构(如Grid包含StackPanel,StackPanel包含Button),称为元素树。
事件源:触发事件的原始元素(如用户点击的Button)。
传播路径:事件从源元素出发,沿元素树向上或向下传播的路径。
二、路由事件的三种传播策略
路由事件有三种传播方式,决定了事件在元素树中的传播路径:
| 传播类型 | 说明 | 典型应用 |
|---|---|---|
| 冒泡(Bubble) | 从事件源向上传播至元素树的根节点(由内向外) | 普通交互事件(如Click、MouseDown) |
| 隧道(Tunnel) | 从元素树的根节点向下传播至事件源(由外向内) | 预览事件(如PreviewMouseDown、PreviewKeyDown),通常用于提前拦截事件 |
| 直接(Direct) | 仅在事件源上触发,不传播 | 类似传统 CLR 事件,如MouseEnter |
简单理解 冒泡:最里面的控件传播至外面 例如Grid包含StackPanel,StackPanel包含Button,Button点击-->StackPanel-->Grid
隧道:点击Button后 Grid-->StackPanel-->Button
三、实战:使用WPF内置路由事件
<Window x:Class="RoutedEventDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="路由事件演示" Height="300" Width="400" Button.Click="Window_Click"> <!-- 窗口处理Button的Click事件 --> <Grid Button.Click="Grid_Click"> <!-- Grid处理Button的Click事件 --> <StackPanel Margin="50" Button.Click="StackPanel_Click"> <!-- StackPanel处理Button的Click事件 --> <Button Content="点击我" Width="100" Height="30" Click="Button_Click"/> <!-- Button自身处理Click事件 --> </StackPanel> </Grid> </Window>
using System.Windows; namespace RoutedEventDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } // Button自身的Click事件处理 private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Button处理了Click事件"); // 若设置e.Handled = true,事件将停止传播(Grid和Window不会收到) // e.Handled = true; } // StackPanel处理Button的Click事件(冒泡到这里) private void StackPanel_Click(object sender, RoutedEventArgs e) { MessageBox.Show("StackPanel处理了Click事件"); } // Grid处理Button的Click事件(继续冒泡) private void Grid_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Grid处理了Click事件"); } // Window处理Button的Click事件(最终冒泡到根元素) private void Window_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Window处理了Click事件"); } } }
从上面例子可以看出每个树级别的元素都有一个Button.Click,他都可以使用这个事件,可以在后台代码不同或者相同的方法中处理。
四、自定义路由事件
1.自定义控件,有点像依赖属性
using System.Windows; using System.Windows.Controls; namespace 自定义路由事件 { // 自定义控件,作为路由事件的所有者 public class CustomButton : Button { // 声明并注册路由事件 public static readonly RoutedEvent CustomClickEvent = EventManager.RegisterRoutedEvent( "CustomClick",// 事件名称 RoutingStrategy.Bubble,// 传播策略(冒泡) typeof(RoutedEventHandler),// 事件处理程序类型 typeof(CustomButton));// 所有者类型 // 2. 创建CLR事件包装器(供XAML绑定) public event RoutedEventHandler CustomClick { add { AddHandler(CustomClickEvent, value); } remove { RemoveHandler(CustomClickEvent, value); } } // 重写基础按钮的点击事件 protected override void OnClick() { base.OnClick(); // 确保基础按钮的功能正常 // 3. 创建已注册和包装的本身的路由事件实例,并引发它 RoutedEventArgs args = new RoutedEventArgs(CustomClickEvent, this); RaiseEvent(args);// 引发自定义事件 } } }
2.界面代码
<Window x:Class="自定义路由事件.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:自定义路由事件" mc:Ignorable="d" Title="自定义路由事件示例" Height="300" Width="400"> <StackPanel local:CustomButton.CustomClick="StackPanel_CustomClick" Margin="10"> <Border BorderBrush="Black" BorderThickness="2" Padding="10" local:CustomButton.CustomClick="Border_CustomClick"> <local:CustomButton Content="点击我" CustomClick="CustomButton_CustomClick" Width="100" Height="30"/> </Border> <TextBlock x:Name="statusText" Margin="0,20,0,0"/> </StackPanel> </Window>
从上面代码可以看出 StackPanel ,Border, local:CustomButton 都绑定了自定义的路由事件
3.界面后台代码
using System.Windows; namespace 自定义路由事件 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } // 按钮自身的事件处理 private void CustomButton_CustomClick(object sender, RoutedEventArgs e) { statusText.Text = "按钮自身处理了CustomClick事件"; } // 边框的事件处理 private void Border_CustomClick(object sender, RoutedEventArgs e) { statusText.Text += "\n边框也处理了CustomClick事件"; // 如果想阻止事件继续冒泡,可以取消事件 // e.Handled = true; } // StackPanel的事件处理 private void StackPanel_CustomClick(object sender, RoutedEventArgs e) { statusText.Text += "\nStackPanel也处理了CustomClick事件"; } } }
从上到下控件一级一级的冒泡
浙公网安备 33010602011771号