WPF 自定义路由事件

简介: 原文:WPF 自定义路由事件WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件。 1.WPF内置路由事件   WPF中的大多数事件都是路由事件,WPF有3中路由策略: 具体不多讲,单需要注意的是WPF路由事件是沿着VIsualTree传递的。
原文: WPF 自定义路由事件

WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件。

1.WPF内置路由事件  

WPF中的大多数事件都是路由事件,WPF有3中路由策略:

具体不多讲,单需要注意的是WPF路由事件是沿着VIsualTree传递的。VisualTree与LogicalTree的区别在于:LogicalTree的叶子节点是构成用户界面的控件(xaml紧密相关),而VisualTree要连控件中的细微结构也算上。VisualTree是LogicalTree的扩展。

reference: Understanding the Visual Tree and Logical Tree in WPF

下面给出一个使用WPF内置路由事件的例子:

<Window x:Class="WPFRoutedEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" >
    <Grid x:Name="Grid1" Margin="10" Background="AliceBlue"  MouseLeftButtonDown="Grid1_MouseLeftButtonDown">        
        <StackPanel Background="BurlyWood" Height="200" x:Name="StackPanel1" Button.Click="ButtonInStackPanel_Click"  MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown">
            <Button x:Name="Button1" Content="RoutedEvent" Click="Button1_Click" />
        </StackPanel>
    </Grid>
</Window>
View Code
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WPFRoutedEvent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //Grid订阅Button的Click事件
            Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click));
        }

        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Button  Clicked.");
            //
            //e.Handled = true;
        }

        private void ButtonInStackPanel_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("StackPanel Clicked.");
        }

        private void ButtonInGrid_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Grid Clicked.");
        }

        private void Grid1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Grid Mouse Left button down.");
        }

        private void StackPanel1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("StackPanel Mouse Left button down.");
        }
    }
}

Button的Click事件是一个路由事件,分别在StackPanel中和Grid中订阅这个事件并进行相应的处理,分别用xaml代码和C#代码如下:

Click="Button1_Click"
Button.Click="ButtonInStackPanel_Click"
Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click));

StackPanel的MouseLeftButtonDown也是一个路由事件,也可以叫“附加事件”。其实“附加事件”也是路由事件,只是个文字游戏,为什么还要另外起个名字呢?原来路由事件的宿主都是那些拥有可视化实体的界面元素;而附加事件则不具备显示在用户界面上的能力。

常见的附加事件有:

Binding类:SourceUpdated事件、TargetUpdated事件。

Mouse类:MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等。

Keyboard类:KeyDown事件、KeyUp事件等。

Grid和StackPanel中均如下订阅:

MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown"

程序运行如下:

 2.自定义路由事件    

 前面DebugLZQ写过一篇博文,内容是关于自定义CLR事件的,参考:.NET自定义事件小结下面来自定义一个WPF路由事件,各位博友可以比较下两者的异同。

创建自定义路由事件大体可以分为三个步骤:

(1)声明并注册路由事件

(2)为路由事件添加CLR事件包装

(3)创建可以激发路由事件的方法

下面我们自定义一个WPF路由事件,我们给事件携带个参数,为此需要创建一个RoutedEventArgs类的派生类。如下:

using System;
using System.Windows;

namespace MyRoutedEvent
{
    //事件参数
    class ReportTimeRoutedEventArgs:RoutedEventArgs
    {
        public ReportTimeRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }

        public DateTime ClickTime { get; set; }
    }
}

然后,创建一个Button类的派生类并按前面的步骤为其添加路由事件:

using System;
using System.Windows.Controls;
using System.Windows;

namespace MyRoutedEvent
{
    class TimeButton:Button
    {
        //声明和注册路由事件\
        public static readonly RoutedEvent ReportTimeRoutedEvent =
            EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeRoutedEventArgs>), typeof(TimeButton));
        //CLR事件包装
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeRoutedEvent, value); }
            remove { this.RemoveHandler(ReportTimeRoutedEvent, value); }
        }
        //激发路由事件,借用Click事件的激发方法

        protected override void OnClick()
        {
            base.OnClick();//保证Button原有功能正常使用,Click事件被激发

            ReportTimeRoutedEventArgs args = new ReportTimeRoutedEventArgs(ReportTimeRoutedEvent, this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);//UIElement及其派生类            
        }

    }
}

下面是程序界面的XAML代码,看下如何消费这个路由事件:

<Window x:Class="MyRoutedEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyRoutedEvent" 
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="grid1" local:TimeButton.ReportTime="TimeButton_ReportTime"><!---->
        <Grid x:Name="grid2">
            <Grid x:Name="grid3">
                <StackPanel x:Name="stackPanel1">
                    <ListBox x:Name="listBox1"/>
                    <local:TimeButton Width="200" Height="200" Background="Aquamarine" ReportTime="TimeButton_ReportTime" /><!---->
                </StackPanel>
            </Grid>
        </Grid>        
    </Grid>
</Window>

事件处理的后台代码如下:

using System.Windows;

namespace MyRoutedEvent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TimeButton_ReportTime(object sender, ReportTimeRoutedEventArgs e)//注意参数
        {
            listBox1.Items.Add(e.ClickTime.ToLongTimeString()+"DebugLZQ");
        }
    }
}

程序运行效果如下:

小结:UIElement类是路由事件和附加事件的分水岭,因为从UIElement类开始才具备了再界面上显示的能力,也因为RaiseEvent、AddHandler和RemoveHandler这些方法也定义在UIElement类中。附加事件也只能算是路由事件的一种用法而不是一个新的概念,其本质还是路由事件。 

目录
相关文章
|
4月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
4月前
|
C# 微服务 Windows
模块化革命:揭秘WPF与微服务架构的完美融合——从单一职责原则到事件聚合器模式,构建高度解耦与可扩展的应用程序
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中借鉴微服务架构思想,实现模块化设计。通过将WPF应用分解为独立的功能模块,并利用事件聚合器实现模块间解耦通信,可以有效提升开发效率和系统可维护性。文中还提供了具体示例代码,展示了如何使用事件聚合器进行模块间通信,以及如何利用依赖注入进一步提高模块解耦程度。此方法不仅有助于简化复杂度,还能使应用更加灵活易扩展。
112 0
|
4月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件
|
4月前
|
C#
WPF 自定义可拖动标题栏
WPF 自定义可拖动标题栏
56 0
|
4月前
|
开发框架 前端开发 C#
使用WPF开发自定义用户控件,以及实现相关自定义事件的处理
使用WPF开发自定义用户控件,以及实现相关自定义事件的处理
|
前端开发 C# 图形学
【WPF】WPF开发用户控件、用户控件属性依赖DependencyProperty实现双向绑定、以及自定义实现Command双向绑定功能演示
Wpf开发过程中,最经常使用的功能之一,就是用户控件(UserControl)了。用户控件可以用于开发用户自己的控件进行使用,甚至可以用于打造一套属于自己的UI框架。依赖属性(DependencyProperty)是为用户控件提供可支持双向绑定的必备技巧之一,同样用处也非常广泛。
962 0
【WPF】WPF开发用户控件、用户控件属性依赖DependencyProperty实现双向绑定、以及自定义实现Command双向绑定功能演示
|
前端开发 C# Windows
WPF鼠标、键盘、拖拽事件、用行为封装事件
本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。
362 0
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
|
Java C# 程序员
WPF程序中的弱事件模式
原文:WPF程序中的弱事件模式 在C#中,得益于强大的GC机制,使得我们开发程序变得非常简单,很多时候我们只需要管使用,而并不需要关心什么时候释放资源。但是,GC有的时并不是按照我们所期望的方式工作。 例如,我想实现一个在窗口的标题栏中实时显示当前的时间,一个比较常规的做法如下:     var...
1158 0
|
C#
WPF 控件自定义背景
<!--控件要设置尺寸的话,设置的尺寸必须比下面的图形的尺寸要小,不然显示不开--> <Label Content="直角测试" Width="90" Height="90" HorizontalContentAlignment="Center" Vert...
1021 0