WPF-路由事件
逻辑树与可视树
逻辑树
描述WPF界面元素的实际结构,XAML中所有的UI元素组成
如:
其中Window,Grid,DockPanel就是逻辑树
可视树
- 界面上可见的元素构成
- Visual或者Visual3D类中派生出来的类
可视树与逻辑树的遍历
LogicalTreeHelper:遍历逻辑树VisualTreeHelper:遍历可视树
示例
新建一个项目,在视图文件中定义两个TreeView控件来展示逻辑树和可视树:
逻辑树
可视树
然后我们新建一个类WPFTreeHelper来实现两个静态方法,获取可视树和逻辑树:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace RoutedEventDemo
{
class WPFTreeHelper
{
static string GetTypeDescription(object obj)
{
return obj.GetType().FullName;
}
// 获取逻辑树
public static TreeViewItem GetLogicTree(DependencyObject dobj)
{
if (dobj == null)
{
return null;
}
var treeItem = new TreeViewItem { Header = GetTypeDescription(dobj), IsExpanded = true };
foreach (var child in LogicalTreeHelper.GetChildren(dobj))
{
var item = GetLogicTree(child as DependencyObject); //递归调用
if (item != null)
{
treeItem.Items.Add(item);
}
}
return treeItem;
}
// 获取可视树
public static TreeViewItem GetVisualTree(DependencyObject dobj)
{
if (dobj == null)
{
return null;
}
var treeItem = new TreeViewItem { Header = GetTypeDescription(dobj), IsExpanded = true };
for(int i=0; i
有了这两个静态方法就可以获取可视树和逻辑树了。然后我们在Button的点击事件中将获取到的逻辑树与可视树传入两个TreeView控件中展示:
namespace RoutedEventDemo
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.tvLogicTree.Items.Add(WPFTreeHelper.GetLogicTree(this));
this.tvVisualTree.Items.Add(WPFTreeHelper.GetVisualTree(this));
}
}
}
显示结果:

可以看到逻辑树就和我们在xaml文件中看到的一样,可视树则更丰富一些,把所有可见的元素都显示了出来。
路由事件
什么是路由事件
- 对元素树中多个侦听器调用处理的事件
- 路由事件是一个CLR事件
冒泡事件和隧道事件
- 冒泡事件:从源向它的父级元素传播
- 隧道事件:从源向它的子级元素传播
路由事件的定义
- 声明路由事件变量并注册
- 通过标准的.NET事件进行包装
- 产生传递事件
示例
新建一个用户控件RoutedEventControl:
在控件的cs文件自定义路由事件,并且在按钮点击时传递路由事件:
namespace RoutedEventDemo
{
///
/// RoutedEventControl.xaml 的交互逻辑
///
public partial class RoutedEventControl : UserControl
{
public RoutedEventControl()
{
InitializeComponent();
}
// 声明和注册路由事件
// RoutingStrategy.Bubble即冒泡事件
// 冒泡事件命名规则:在名称后面加上'Event'
public static readonly RoutedEvent MyClickEvent = EventManager.RegisterRoutedEvent("MyClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventControl));
// 包装路由事件
public event RoutedEventHandler MyClick
{
add { AddHandler(MyClickEvent, value); }
remove { RemoveHandler(MyClickEvent, value); }
}
// 产生传递事件
private void Button_Click(object sender, RoutedEventArgs e)
{
RoutedEventArgs arg = new RoutedEventArgs();
arg.RoutedEvent = MyClickEvent; // RoutedEvent的赋值要在Source的赋值前面,否则会报错
arg.Source = this;
RaiseEvent(arg);
}
}
}
在MainWindow中引入用户控件RoutedEventControl:
MyClick就是我们定义的路由事件,我们再去MainWindow.xaml.cs中实现这个事件的处理方法:
private void RoutedEventControl_MyClick(object sender, RoutedEventArgs e)
{
var source = e.Source;
MessageBox.Show("Hello MyClickEvent " + source);
}
完成后,点击按钮的显示结果:

同样的,我们也可以注册隧道事件:
// 注册隧道事件
// 隧道事件命名规则:在名称前面加上'Preview'
public static readonly RoutedEvent PreviewMyClickEvent = EventManager.RegisterRoutedEvent("PreviewMyClick", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(RoutedEventControl));
public event RoutedEventHandler PreviewMyClick
{
add { AddHandler(PreviewMyClickEvent, value); }
remove { RemoveHandler(PreviewMyClickEvent, value); }
}
附加事件
什么是附加事件
- 特殊的WPF路由事件
- 用于非定义该事件的类
附加事件的定义
- 声明附加事件变量并注册
- 通过静态方法添加或移除事件
- 引发附加事件
示例
新建一个用户控件AttachedEventControl:
在cs代码中定义我们的附加事件:
namespace RoutedEventDemo
{
///
/// AttachedEventControl.xaml 的交互逻辑
///
public partial class AttachedEventControl : UserControl
{
public AttachedEventControl()
{
InitializeComponent();
}
// 附加事件的声明和注册
public static readonly RoutedEvent MyAttachedEvent = EventManager.RegisterRoutedEvent("MyAttached", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AttachedEventControl));
// 静态添加方法
public static void AddMyAttachedEventHandler(DependencyObject dp, RoutedEventHandler handler)
{
UIElement element = dp as UIElement;
if (element != null)
{
element.AddHandler(MyAttachedEvent, handler);
}
}
// 静态移除方法
public static void RemoveMyAttachedEventHandler(DependencyObject dp, RoutedEventHandler handler)
{
UIElement element = dp as UIElement;
if (element != null)
{
element.RemoveHandler(MyAttachedEvent, handler);
}
}
// 引发附加事件
// 这里是重写了鼠标左键点击事件
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonDown(e);
RoutedEventArgs arg = new RoutedEventArgs(MyAttachedEvent, this);
RaiseEvent(arg);
}
}
}
在MainWindow中引入用户控件AttachedEventControl:
在MainWindow.xaml.cs文件中添加我们定义的附加事件:
namespace RoutedEventDemo
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// 添加自定义的附加事件
AttachedEventControl.AddMyAttachedEventHandler(this, new RoutedEventHandler(AttachedEventHandler));
}
// 附加事件处理代码
private void AttachedEventHandler(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello, MyAttachedEvent");
}
}
}
实现效果:
