当前位置: 首页 > news >正文

WPF路由事件:冒泡、隧道与直接全解析

路由事件的三类传播方式

  • 冒泡事件(Bubbling)
    事件从源元素向上传递到根元素(如Button.Click)。

    <StackPanel Button.Click="StackPanel_Click"><Button Content="Click Me"/>
    </StackPanel>
    

    private void StackPanel_Click(object sender, RoutedEventArgs e) {// 处理按钮点击事件
    }
    

  • 隧道事件(Tunneling)
    事件从根元素向下传递到源元素(命名以Preview开头,如PreviewMouseDown)。

    <StackPanel PreviewMouseDown="StackPanel_PreviewMouseDown"><Button Content="Click Me"/>
    </StackPanel>
    

    private void StackPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e) {// 在事件到达按钮前拦截
    }
    

  • 直接事件(Direct)
    仅触发在源元素上,不传播(如MouseEnter)。

  • 添加/移除事件处理器

    button.AddHandler(Button.ClickEvent, new RoutedEventHandler(HandleClick));
    button.RemoveHandler(Button.ClickEvent, new RoutedEventHandler(HandleClick));
    

  • 终止事件传播
    在事件处理器中设置e.Handled = true,阻止事件继续冒泡或隧道。

    private void HandleClick(object sender, RoutedEventArgs e) {e.Handled = true; // 停止传播
    }
    

  • 输入事件
    MouseDown(冒泡)、PreviewKeyDown(隧道)
  • 控件事件
    Button.Click(冒泡)、TextBox.TextChanged(直接)
  • 全局快捷键
    在窗口级处理PreviewKeyDown事件,拦截特定按键组合。
  • 控件组合交互
    父容器监听子控件的冒泡事件(如ListBox中按钮的点击)。
  • INotifyPropertyChanged实现:
    public class Model : INotifyPropertyChanged {private string _name;public string Name {get => _name;set { _name = value; OnPropertyChanged(nameof(Name)); }}public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

命令与MVVM

  • ICommand接口实现:
    public class RelayCommand : ICommand {private readonly Action _execute;public RelayCommand(Action execute) => _execute = execute;public bool CanExecute(object parameter) => true;public void Execute(object parameter) => _execute();public event EventHandler CanExecuteChanged;
    }
    

  • MVVM优势:解耦视图与逻辑,利于单元测试,支持设计时数据(d:DataContext)。

样式与模板

  • 控件模板示例:
    <ControlTemplate TargetType="Button"><Border Background="{TemplateBinding Background}" CornerRadius="5"><ContentPresenter HorizontalAlignment="Center"/></Border>
    </ControlTemplate>
    

  • 触发器类型:PropertyTriggerDataTriggerEventTrigger

性能优化

  • 虚拟化技术:VirtualizingStackPanel用于ListBox等控件,延迟加载可视项。
  • 依赖属性机制:静态注册节省内存,支持值继承、动画等。
    public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive", typeof(bool), typeof(MyControl));public bool IsActive {get => (bool)GetValue(IsActiveProperty);set => SetValue(IsActiveProperty, value);
    }
    

高级主题

  • 路由事件:Bubbling(冒泡)、Tunneling(隧道)、Direct(直接)。
  • 跨线程访问UI:Dispatcher.InvokeDispatcher.BeginInvoke
  • 自定义绘图:继承FrameworkElement,重写OnRender方法使用DrawingContext

调试技巧

  • 使用Snoop或WPF Inspector工具实时查看视觉树。
  • 绑定失败时查看Output窗口的绑定错误日志。
  • PresentationTraceSources.TraceLevel=High诊断绑定问题。
http://www.lryc.cn/news/579202.html

相关文章:

  • 嵌入式软件面经(四)Q:请说明在 ILP32、LP64 与 LLP64 三种数据模型下,常见基本类型及指针的 sizeof 值差异,并简要解释其原因
  • 软件开发早期阶段,使用存储过程的优势探讨:敏捷开发下的利器
  • 【C++】--入门
  • 欧拉角、四元数和旋转矩阵的变换关系以及无人机的坐标变换
  • 如何在Excel中每隔几行取一行
  • sqlmap学习笔记ing(3.[MoeCTF 2022]Sqlmap_boy,cookie的作用)
  • LeetCode Hot 100 滑动窗口 【Java和Golang解法】
  • 鸿蒙开发技巧---去除字符串两端的空格
  • AI大模型如何重塑软件开发流程?从自动化革命到人机共生范式
  • 怎样理解:source ~/.bash_profile
  • 深入Flink核心概念:解锁大数据流处理的奥秘
  • SAP WM LT10 TO创建增强
  • Android Auto即将带来变革
  • Agno(一)
  • 机器学习在智能制造业中的应用:质量检测与设备故障预测
  • Vue + RuoYi 前后端分离入门手册
  • Ubuntu云服务器上部署发布Vite项目
  • Redis基础(1):NoSQL认识
  • 端到端自动驾驶系统关键技术
  • 开发自动驾驶系统所需工具
  • 2025美国券商交易系统综合开发及解决方案报告:低延迟、全球化与代币化技术赋能机构业务新生态
  • “山河”应急指挥决策AI智能体 - 全生命周期构建实施说明
  • 轻松上手:使用Nginx实现高效负载均衡
  • python中的pydantic是什么?
  • 逆向入门(25、26)程序逆向篇-KeygenMe,MexeliteCRK1
  • Linux基本命令篇 —— alias命令
  • MIT协议开源CRM系统:打造高效客户关系管理解决方案
  • 【Vue入门学习笔记】项目的创建与基础认识
  • 存储过程封装:复杂业务逻辑的性能优化
  • 基于 TOF 图像高频信息恢复 RGB 图像的原理、应用与实现