目录

WPF-事件系统底层原理.

一、整体架构总览

WPF事件系统由 三层 组成:

┌─────────────────────────────────────────────┐
│              XAML 层 (声明式绑定)              │
│         <Button Click="Handler"/>            │
├─────────────────────────────────────────────┤
│           RoutedEvent 层 (路由传播)           │
│    EventManager / EventRoute / RaiseEvent    │
├─────────────────────────────────────────────┤
│             CLR Event 层 (委托机制)           │
│          delegate / multicast delegate       │
└─────────────────────────────────────────────┘

二、RoutedEvent 的注册机制

2.1 EventManager.RegisterRoutedEvent

每个路由事件都通过 EventManager 注册到一个 全局静态表 中。

public static readonly RoutedEvent ClickEvent = 
    EventManager.RegisterRoutedEvent(
		"Click",						// 事件名
		RoutingStrategy.Bubble,			// 路由策略
		typeof(RoutedEventHandler),		// 委托类型
		typeof(ButtonBase));			// 拥有者类型
    

2.2内部数据结构

EventManager 内部维护:

GlobalEventManager
├── _routedEvents: List<RoutedEvent>       // 所有已注册的路由事件
├── _eventMap: Dictionary<int, RoutedEvent> // GlobalIndex → RoutedEvent
└── _classHandlers: Hashtable              // 类级别处理器映射
       ├── Key: (Type, RoutedEvent)
       └── Value: RoutedEventHandlerInfoList

每个 RoutedEvent 对象包含:

public sealed class RoutedEvent
{
    public string Name { get; }
    public RoutingStrategy RoutingStrategy { get; }
    public Type HandlerType { get; }
    public Type OwnerType { get; }
    internal int GlobalIndex { get; }		// 全局唯一索引,用于快速查找
}

“关键点: GlobalIndex 是一个递增整数,用于在数组中 O(1) 定位事件。”

三、事件处理器的存储机制

3.1 两种级别的处理器

┌─────────────────────────────────────────────┐
│           Class Handler (类级别)             │
│   通过 RegisterClassHandler 注册             │
│   存储在 EventManager 全局表中               │
│   优先执行,所有实例共享                      │
├─────────────────────────────────────────────┤
│         Instance Handler (实例级别)          │
│   通过 AddHandler / += 注册                  │
│   存储在每个 UIElement 实例的 EventHandlersStore │
│   后执行                                     │
└─────────────────────────────────────────────┘

3.2 类级别处理器注册

// 在静态构造函数中注册
static MyButton()
{
    EventManager.RegisterClassHandler(
    	typeof(MyButton),
    	ButtonBase.ClickEvent,
    	new RoutedEventHandler(OnClickClassHandler),
    	handledEventsToo: false);		// 是否处理已标记 Handled 的事件
}

private static void OnClickClassHandler(object sender, RoutedEventArgs e)
{
    // 类级别处理,优先于实例处理器
}

3.3 实例级别处理器存储

每个 UIElement 内部有一个 EventHandlersStore :

// UIElement 内部简化:
public class UIElement : Visual
{
    private EventHandlersStore _eventHandlersStore;
    
    public void AddHandler(RoutedEvent routedEvent, Delegate handler, bool handledEventsToo)
    {
        if (_eventHandlersStore == null)
            _eventHandlersStore = new EventHandlersStore();
            
        _eventHandlersStore.AddRoutedEventHandler(routedEvent, handler, handledEventsToo);
    }
}

EventHandlersStore 内部结构:

EventHandlersStore
└── _entries: FrugalMap (轻量字典)
        Key   = RoutedEvent.GlobalIndex (int)
        Value = RoutedEventHandlerInfo[]
        
RoutedEventHandlerInfo
├── Handler: Delegate          // 实际处理器
└── InvokeHandledEventsToo: bool // 是否在 Handled=true 时仍执行

为什么用 FrugalMap? 大部分控件只订阅少量事件,FrugalMap 对1-6 个元素有机制优化(避免Dictionary 的哈希开销)。”

四、事件路由传播机制

4.1 RaiseEvent 入口

// UIElement.RaiseEvent 简化逻辑
public void RaiseEvent(RoutedEventArgs e)
{
    e.Source = this;
    
    // 1. 构建事件路由
    EventRoute route = new EventRoute(e.RoutedEvent);
    
    // 2. 沿可视树构建路由路径
    BuildRoute(route);
    
    // 3. 按路由策略调用处理器
    route.InvokeHandlers(e);
    
    e.Source = e.OriginalSource;
}

4.2 BuildRoute - 构建路由路径

// 简化的路由构建过程
internal void BuildRoute(EventRoute route)
{
    // 从当前元素向上遍历可视树
    UIElement current = this;
    
    while (current != null)
    {
        // 将每个节点的处理器信息加入路由表
        current.AddToEventRoute(route);
        
        // 向上走到父元素
        current = VisualTreeHelper.GetParent(current) as UIElement;
    }
}

4.3 EventRoute 内部结构

internal sealed class EventRoute
{
    private RoutedEvent _routedEvent;
    private List<RouteItem> _routeItemList;
    
    internal struct RouteItem
    {
        internal object Target; 	// 目标要素
        internal RoutedEventHandlerInfo Handler;	// 处理器信息
    }
}

4.4三种路由策略的执行顺序

假设可视树:
    Window
      └── Grid
            └── StackPanel
                  └── Button (事件源)

Bubble(冒泡) - 从下往上

执行顺序:
  1. Button      的 ClassHandler → InstanceHandler
  2. StackPanel  的 ClassHandler → InstanceHandler
  3. Grid        的 ClassHandler → InstanceHandler
  4. Window      的 ClassHandler → InstanceHandler

Tunnel(隧道) - 从上往下

执行顺序:
  1. Window      的 ClassHandler → InstanceHandler
  2. Grid        的 ClassHandler → InstanceHandler
  3. StackPanel  的 ClassHandler → InstanceHandler
  4. Button      的 ClassHandler → InstanceHandler

Direct( 直达) - 只在源

执行顺序:
  1. Button      的 ClassHandler → InstanceHandler

4.5 调用顺序的完整规则

对于路由上的每一个节点:

┌──────────────────────────────────┐
│ 1. Class Handlers (类级别)       │ ← 优先!无法被实例移除
│    按继承链从基类到派生类执行      │
│    UIElement → Control → Button  │
├──────────────────────────────────┤
│ 2. Instance Handlers (实例级别)  │ ← 按 AddHandler 注册顺序执行
└──────────────────────────────────┘

然后移动到路由上的下一个节点...

五、Handled 机制

5.1基本行为

private void Button_Click(object sender, RoutedEventArgs e)
{
    e.Handled = true; // 标记已处理
}
Button (Handled = true) -> StackPanel (跳过) -> Grid (跳过) -> Window (跳过)

5.2 穿透 Handled 的方法

// 方法一: AddHandler 指定 handledEventsToo = true
grid.AddHandler(
	ButtonBase.ClickEvent,
	new RoutedEventHandler(Grid_Click),
	handledEventsToo: true);	//  <- 即使 Handled = true 也执行

// 方法二: 类级别注册时指定
EventManager.RegisterClassHandler(
	typeof(MyGrid),
	ButtonBase.ClickEvent,
	new RoutedEventHandler(OnClick),
	handledEventsToo: true);

5.3 内部判断逻辑

// EventRoute.InvokeHandlers 简化逻辑
foreach (RouteItem item in _routeItemList)
{
    if (!e.Handled || item.Handler.InvokeHandledEventsToo)
    {
        item.Handler.InvokeHandler(item.Target, e);
    }
}

六、Tunnel + Bubble 配对机制

6.1 Preview 事件

WPF 中大部分事件成对出现:

Tunnel(Preview) Bubble
PreviewMouseDown MouseDown
PreviewKeyDown KeyDown
PreviewTextInput TextInput

6.2执行顺序

用户点击 Button:

                    Tunnel 阶段 (Preview)
                    ─────────────────────
Step 1: Window.PreviewMouseDown     ↓
Step 2: Grid.PreviewMouseDown       ↓
Step 3: StackPanel.PreviewMouseDown ↓
Step 4: Button.PreviewMouseDown     ↓

                    Bubble 阶段
                    ─────────────────────
Step 5: Button.MouseDown            ↑
Step 6: StackPanel.MouseDown        ↑
Step 7: Grid.MouseDown              ↑
Step 8: Window.MouseDown            ↑

6.3 底层实现

// 在输入系统中 (MouseDevice / KeyboardDevice)
// 简化的鼠标按下处理:

internal void ReportMouseDown(MouseButton button)
{
    // 1. 先触发 Tunnel 事件
    var previewArgs = new MouseButtonEventArgs(...);
    previewArgs.RoutedEvent = Mouse.PreviewMouseDownEvent;
    target.RaiseEvent(previewArgs);
    
    // 2. 再触发 Bubble 事件
    var args = new MouseButtonEventArgs(...);
    args.RoutedEvent = Mouse.MouseDownEvent;
    
    // 如果 Preview 阶段 Handled,Bubble 也跳过
    if (!previewArgs.Handled)
    {
        target.RaiseEvent(args);
    }
}

七、附加事件(Attached Event)

7.1概念

允许一个类定义事件,但由 其它类 触发或处理。

// Mouse 类定义了 MouseDown, 但任何 UIElement 都能使用
public static class Mouse
{
    public static readonly RoutedEvent MouseDownEvent = 
        EventManager.RegisterRoutedEvent(
    		"MouseDown",
    		RoutingStrategy.Bubble,
    		typeof(MouseButtonEventHandler),
    		typeof(Mouse));		// 拥有者是 Mouse 类, 不是 UIElement
    
    // 附加的 Add/Remove 方法
    public static void AddMouseDownHandler(DependencyObject d, MouseButtonEventHandler handler)
    {
        if (d is UIElement uie)
        {
            uie.AddHandler(MouseDownEvent, handler);
        }
    }
    
    public static void RemoveMouseDownHandler(DependencyObject d, MouseButtonEventHandler handler)
    {
        if (d is UIElement uie)
        {
            uie.RemoveHandler(MouseDownEvent, handler);
        }
    }
}

7.2 XAML 中使用

<Grid Mouse.MouseDown="Grid_MouseDown">
    <!-- 子元素 -->
</Grid>

八、CLR 事件包装器

8.1 标准模式

RoutedEvent 通常会提供一个 CLR 事件包装器:

public class ButtonBase : ContentControl
{
    // 1. RoutedEvent 注册
    public static readonly RoutedEvent ClickEvent = 
        EventManger.RegisterRoutedEvent("Click", ...);
    
    // 2. CLR 事件包装器(XAML 解析器需要)
    public event RoutedEventHandler Click
    {
        add 	{ AddHandler(ClickEvent, value); }
        remove  { RemoveHandler(ClickEvent, value); }
    }
    
    // 触发事件
    protected virtual void OnClick()
    {
        RaiseEvent(new RoutedEventArgs(ClickEvent, this));
    }
}

8.2 为什么需要CLR包装器?

┌──────────────────────────────────────────────┐
│  XAML 解析器 (XamlParser)                     │
│  遇到 <Button Click="Handler"/>              │
│                                              │
│  1. 反射查找 "Click" CLR 事件                 │
│  2. 从 CLR 事件的 add 访问器                  │
│     内部调用 AddHandler(ClickEvent, handler)  │
│  3. 最终注册到 RoutedEvent 系统               │
└──────────────────────────────────────────────┘

九、完整事件触发流程图

用户鼠标点击屏幕
┌──────────────────┐
│   Win32 消息循环   │  WM_LBUTTONDOWN
│   (HwndSource)    │
└────────┬─────────┘
┌──────────────────┐
│   InputManager    │  WPF 输入管理器
│  (Dispatcher 线程) │
└────────┬─────────┘
┌──────────────────┐
│   MouseDevice     │  确定命中测试目标
│   HitTest         │  → 找到 Button
└────────┬─────────┘
         ├──→ 构建 PreviewMouseDownEvent 的 EventRoute
         │        收集路由上所有节点的 Handler
         │        Window → Grid → StackPanel → Button
         ├──→ Tunnel: 从 Window 到 Button 依次调用
         │        每个节点: ClassHandler → InstanceHandler
         │        检查 e.Handled
         ├──→ 构建 MouseDownEvent 的 EventRoute
         ├──→ Bubble: 从 Button 到 Window 依次调用
         │        每个节点: ClassHandler → InstanceHandler
         │        检查 e.Handled
         ├──→ ButtonBase 内部逻辑判断 Click
         ├──→ 构建 ClickEvent 的 EventRoute
         └──→ Bubble: 从 Button 到 Window 依次调用
                 ClickEvent 的所有处理器

十、性能优化设计

优化点 实现方式
GlobalIndex 每个 RoutedEvent 分配唯一整数索引,数组 O(1) 查找
FrugalMap 小集合优化,避免 Dictionary 开销
EventRoute 复用 EventRoute 对象可被回收复用
ClassHandler共享 类处理器存全局表,不重复存储
延迟创建 EventHandlerStore 没有订阅事件的元素不创建 Store

十一、总结

                    WPF RoutedEvent 系统
                    
  ┌──────────── 注册层 ────────────┐
  │  EventManager (全局注册表)      │
  │  RoutedEvent (事件标识)         │
  │  GlobalIndex (快速索引)         │
  └───────────────────────────────┘
  ┌──────────── 存储层 ────────────┐
  │  ClassHandler (全局共享)        │
  │  EventHandlersStore (实例私有)  │
  │  FrugalMap (轻量存储)           │
  └───────────────────────────────┘
  ┌──────────── 路由层 ────────────┐
  │  EventRoute (路由路径)          │
  │  BuildRoute (沿可视树构建)      │
  │  Tunnel / Bubble / Direct      │
  └───────────────────────────────┘
  ┌──────────── 调用层 ────────────┐
  │  InvokeHandlers                │
  │  ClassHandler 优先于 Instance   │
  │  Handled 机制控制传播           │
  └───────────────────────────────┘