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>

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");
        }
    }
}

假如我如果想在后台代码中消费定义的路由事件,该如何做呢?

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

            this.grid1.AddHandler(TimeButton.OnReportTimeRoutedEvent, new RoutedEventHandler(MainWindow_MEvent));
            this.grid1.AddHandler(TimeButton.OnReportTimeRoutedEvent, new RoutedEventHandler(TimeButton_ReportTime1));//监听OnReportTimeRoutedEvent的路由事件

        }

        private void TimeButton_ReportTime(object sender, ReportTimeRoutedEventArgs e)
        {
            listBox1.Items.Add(e.ClickTime.ToLongTimeString() + "DebugLZQ");

        }
        private void TimeButton_ReportTime1(object sender, RoutedEventArgs e)
        {
            ReportTimeRoutedEventArgs ss = e as ReportTimeRoutedEventArgs;//这里使用了一个转换,父类转换程子类的引用,只有转换了,才能被AddHandler调用。

            listBox1.Items.Add(ss.ClickTime.ToLongTimeString() + "DebugLZQ");

        }
        void MainWindow_MEvent(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("dddd");
        }
    }

程序运行效果如下:

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

WPF自定义路由事件(二),布布扣,bubuko.com

时间: 2024-10-17 17:11:27

WPF自定义路由事件(二)的相关文章

WPF自定义路由事件

一 概要 本文通过实例演示WPF自定义路由事件的使用,进而探讨了路由事件与普通的CLR事件的区别(注:"普通的CLR事件"这个说法可能不太专业,但是,我暂时也找不到什么更好的称呼,就这么着吧,呵呵.)(扩展阅读:例说.NET事件的使用). 二 实例演示与说明 1 新建DetailReportEventArgs类,该类派生自RoutedEventArgs类,RoutedEventArgs类包含与路由事件相关的状态信息和事件数据.DetailReportEventArgs类中定义了属性Ev

WPF:自定义路由事件的实现

路由事件通过EventManager,RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数:通过RaiseEvent方法来触发事件:通过传统的CLR事件来封装后供用户使用. 如何实现自定义路由事件,可以参考MSDN官网上的文档:如何:创建自定义路由事件 下面的这个demo参考自<葵花宝典--WPF自学手册>. 1.MainWindow.xaml 1 <Window x:Class="WpfApplic

WPF的路由事件、冒泡事件、隧道事件(预览事件)

原文:WPF的路由事件.冒泡事件.隧道事件(预览事件) 本文摘要: 1:什么是路由事件: 2:中断事件路由: 3:自定义路由事件: 4:为什么需要自定义路由事件: 5:什么是冒泡事件和预览事件(隧道事件): 1:什么是路由事件 WPF中的事件为路由事件,所谓路由事件,MSDN定义如下: 功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件. 实现定义:路由事件是一个 CLR 事件,可以由 RoutedEvent 类的实例提供支持并由 Window

迟到的 WPF 学习 —— 路由事件

1. 理解路由事件:WPF 通过事件路由(event routing)概念增强了传统的事件执行的能力和范围,允许源自某个元素的事件由另一个元素引发,例如,事件路由允许工具栏上的一个按钮点击的事件在被代码处理之前上传到工具栏,再由工具栏上传到所属窗体 2. 定义.注册和包装路由事件:和依赖性属性类似,它由只读的静态字段表示,在一个静态构造函数中注册,并通过一个标准的 .Net 事件定义进行包装.如 Button 的 Click 事件,该事件继承自抽象的 ButtonBase 基类 public a

自定义路由事件

自定义路由事件大体上可分为三个步骤: 1.声明并注册路由事件: 2.为路由事件添加CLR事件包装: 3.创建可以激发路由事件的方法. 主要的示例代码如下: public class TimeButton : Button { /// <summary> /// 声明并注册路由事件. /// </summary> public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent(

WPF路由事件二:路由事件的三种策略

一.什么是路由事件 路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件.路由事件是一个CLR事件. 路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而持久的方式在每个元素上触发,而不需要任何定制的代码(如果用传统的方式实现一个操作,执行整个事件的调用则需要执行代码将事件串联起来). 路由事件的路由策略: 所谓的路由策略就是指:路由事件实现遍历元素的方式. 路由事件一般使用以下三

WPF中路由事件的传播

路由事件(RoutedEvent)是WPF中新增的事件,使用起来与传统的事件差别不大, 但传播方式是完全不同的. 路由事件的传播方式 通过RoutingStrategy来定义传播的方式 public enum RoutingStrategy { Tunnel = 0, //隧道,由顶层元素向内传播,事件一般以Preview开头 Bubble = 1, //冒泡,与隧道相反,向外传播 Direct = 2, //直接,与传统的事件相似 } WPF中的路由事件用的最多的就是Tunnel和Bubble

WPF Demo18 路由事件

using System.Windows; namespace 路由事件2 { public class Student { ////声明并定义路由事件 //public static readonly RoutedEvent NameChangedEvent = // EventManager.RegisterRoutedEvent("NameChanged", // RoutingStrategy.Bubble, // typeof(RoutedEventHandler), //

学习WPF——了解路由事件

入门 我们先来看一个例子 前台代码: 后台代码: 点击按钮的运行效果第一个弹出窗口 第二个弹出窗口: 第三个弹出窗口: 说明 当点击按钮之后,先触发按钮的click事件,再上查找,发现stackpanel也注册了该事件,那么接着触发StackPanel的Button.Click事件,依次再触发Grid的Button.Click事件,这就是最基本的事件路由,事件路由的策略是右内向外的 如果不希望在XAML中注册路由事件,那么也可以通过编码的方式注册路由事件如下所示 如果想终止事件的向上传递,可以使