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

①声明并注册路由事件。

②为路由事件添加CLR事件包装。

③创建可以激发路由事件的方法。

以ButtonBase类中代码为例展示这3个步骤:

public abstract class ButtonBase:ContentControl,ICommandSource

{

//声明并注册路由事件。

public static readonly RoutedEvent ClickEvent=EventManager.RegisterRoutedEvent("Click",RoutedStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));

//为路由事件添加CLR事件包装器。

pubic event RoutedEventHandler Click

{

add{this.AddHandler(ClickEvent,Value);}

remove{this.REmoveHandler(ClickEvent,Value);}

}

//激发路由事件的方法,此方法在用户单击鼠标时会被Windows系统调用

protected virtual void OnClick()

{

RoutedEventArgs newEvent=new RoutedEventArgs(ButtonBase.ClickEvent,this);

this.RaiseEvent(new Event);

}

}

理解EventManager.RegisterRoutedEvent方法的四个参数:

第一个参数:

参数为String类型,被称为路由事件的名称。应与RoutedEvent变量的前缀和CLR事件包装器的名称一致。

第二个参数:

①Buddle,冒泡式:路由事件有事件的激发着出发向它的上级容器一层一层路由,直至最外层容器(Window或者Page)。因为是有树的底端向顶端移动,所以这种策略被形象的命名为“冒泡式”。

②Tunnel,隧道式:事件的路由方向正好与Bubble策略相反。

③Direct,直达式:模仿CLR直接事件,直接将事件消息送达事件处理器。

第三个参数:

用于指定事件处理器类型。事件处理器的返回值类型和参数列表必须与此参数指定的委托保持一致,不然会导致在编译时抛出异常。

第四个参数:

用于指定路由事件的宿主(拥有者)是哪儿个类型。

自己动手创建一个路由事件,这个事件的用途是报告事件发生的时间。

“兵马未动,粮草先行”。为了让事件消息能携带按钮被单击时的时间。先创建一个RoutedEventArgs类的派生类。

//用于承载时间消息的事件参数

class ReportTimeEventArgs:RoutedEventArgs

{

public ReportTimeEventArgs(RoutedEvent routedEvent,object source)

:base(routedEvent,source){}

public DateTime ClickTime{get;set;}

}

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

class TimeButton:Button

{

//声明和注册路由事件

public static Readonly RoutedEvent ReportTimeEvent=EventManager.RegisterManager

("ReportTime",RoutingStrategy.Bubbl,typeof(EventHandler<ReportTimeArgs>),typeof(TimeButton));

//为路由事件添加CLR事件包装器

public event RoutedEventHanler ReportTime

{

add{AddHandler(ReportTimeEvent,value);}

remove{RemoveHandler(ReportTimeEvent,value);}

}

//激发路由事件,借用Click事件的激发事件

protected override void OnClick()

{

base.OnClick();

ReportTimeEventArgs args=new ReportTimeEventArgs(ReportEvent,this);

args.ClickTime=DateTime.Now;

this.RaiseEvent(args);

}

}

//下面是程序界面XAML代码

<Window x:Class="Wpf8.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Wpf8"
        local:TimeButton.ReportTime="ReportTimeHandler"
        Title="Window1" Height="300" Width="300">
    <Grid x:Name="grid_1" local:TimeButton.ReportTime="ReportTimeHandler">
        <Grid x:Name="grid_2" local:TimeButton.ReportTime="ReportTimeHandler">
            <Grid x:Name="grid_3" local:TimeButton.ReportTime="ReportTimeHandler">
                <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="ReportTimeHandler">
                    <ListBox x:Name="listBox"></ListBox>
                    <local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="报时" local:TimeButton.ReportTime="ReportTimeHandler"></local:TimeButton>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>

在UI界面上,以Window为根,套了三层Grid和一层StackPanel。最里面放置了一个ListBox和一个TimeButton。从最内层的TimeButton到最外层的Window都侦听着TimeButton.ReportTimeEvent这个路由事件。并用ReportTimeHandler方法来响应这个事件。ReportTimeHandler的代码如下:

//ReportTimeEvent路由事假处理器

private void ReportTimeHandler(object sender,ReportTimeEventArgs e)

{

FrameworkElement element=sender as FrameworkElement;

string timeStr=e.ClickTime.ToLongTimeString();

string content=string.Format("{0}到达{1}",timeStr,element.Name);

this.listBox.Items.Add(content);

}

运行程序,单击按钮。

RoutedEvenArgs的Source与OriginalSource

路由事件的消息包含在RouteEventArgs实例中。RoutedEventArgs有两个属性Source和OriginalSource。这两个属性都表示路由事件传递的起点。只不过Source表示的是LogicalTree上的消息源头,而OriginalSource则表示VisualTree上的源头。

示例:

创建一个用户控件:

<UserControl x:Class="Wpf8.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Border BorderBrush="Orange" BorderThickness="3" CornerRadius="5">
        <Button x:Name="innerButton" Width="80" Height="80" Content="OK"></Button>
    </Border>
</UserControl>

在主窗体中添加用户控件。

<Window x:Class="Wpf8.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Wpf8"
        Title="Window2" Height="180" Width="300">
    <Grid>
        <local:MyUserControl x:Name="myUserControl" Margin="10"/>
    </Grid>
</Window>

后台代码:

public partial class Window2 : Window
    {
        public Window2()
        {
            InitializeComponent();
            //为主窗体添加对Button.Click事件的侦听。
            this.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.Button_Click));
        }
        //路由事件处理器。
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string strOriginalSource = string.Format("VisualTree start point:{0},type is {1}",
                (e.OriginalSource as FrameworkElement).Name,e.OriginalSource.GetType().Name);
            string strSource = string.Format("LogicalTree start point:{0},type is {1}",
                (e.Source as FrameworkElement).Name,e.Source.GetType().Name);
            MessageBox.Show(strOriginalSource+"\r\n"+strSource);
        }
    }

Button.Click路由事件是从MyUserControl的innerButton发出来的,在主窗体中,myUserControl是LogicalTree的末端结点,所以e.source就是myUserControl;而窗体的VisualTree则包含了myUserControl的内部结构。所以使用e.OriginalSource可以获得innerButton.

Wpf自定义路由事件的更多相关文章

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

    路由事件通过EventManager,RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数:通过RaiseEvent方法来 ...

  2. WPF自定义路由事件(二)

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

  3. 细说WPF自定义路由事件

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

  4. WPF 自定义路由事件

    如何:创建自定义路由事件 首先自定义事件支持事件路由,需要使用 RegisterRoutedEvent 方法注册 RoutedEvent C#语法 public static RoutedEvent ...

  5. WPF自定义路由事件(一)

    首先自定义事件支持事件路由,需要使用 RegisterRoutedEvent 方法注册 RoutedEvent C#语法 public static RoutedEvent RegisterRoute ...

  6. WPF 自定义路由事件 与 附加路由事件

    为student添加附件事件

  7. WPF自学入门(四)WPF路由事件之自定义路由事件

    在上一遍博文中写到了内置路由事件,其实除了内置的路由事件,我们也可以进行自定义路由事件.接下来我们一起来看一下WPF中的自定义路由事件怎么进行创建吧. 创建自定义路由事件分为3个步骤: 1.声明并注册 ...

  8. WPF路由事件三:自定义路由事件

    与依赖项属性类似,WPF也为路由事件提供了WPF事件系统这一组成.为一个类型添加一个路由事件的方式与为类型添加依赖项属性的方法类似,添加一个自定义路由事件的步骤: 一.声明路由事件变量并注册:定义只读 ...

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

    本文摘要: 1:什么是路由事件: 2:中断事件路由: 3:自定义路由事件: 4:为什么需要自定义路由事件: 5:什么是冒泡事件和预览事件(隧道事件): 1:什么是路由事件 WPF中的事件为路由事件,所 ...

随机推荐

  1. 通过dblink impdp导入

    实验: 源数据库:10.5.129.160 dwhtest 需要导入的数据库:10.5.129.130 dwhtest 在10.5.129.130上创建DBLINK SQL> create  p ...

  2. 【转】Android--UI之ProgressBar--不错

    原文网址:http://www.cnblogs.com/plokmju/p/android_progressbar.html 前言 开门见山,开篇明意.这篇博客主要讲解一下Android中Progre ...

  3. 可压Navier-Stokes方程组的爆破现象

    在 Z.P. Xin, Blowup of smooth solutions to the compressible Navier-Stokes  equations with compact den ...

  4. HTML-通过点击网页上的文字弹出QQ添加好友页面

    在网上参考了部分方法,综合了一下. 发现有2中方式: 第一种是不能直接弹出添加界面的,只能弹出网页,再通过网页中的添加好友才能添加: 弹出的网页是这样的(我是写成在新的网页中打开) 现在看实现的代码: ...

  5. Longest Consecutive Sequence hashset

    public class Solution { public int longestConsecutive(int[] num) { HashSet<Integer> hash=new H ...

  6. 《Linear Algebra and Its Applications》-chaper6-正交性和最小二乘法-基本概念与定理

    这一章节我们主要讨论定义在R^n空间上的向量之间的关系,而这个关系概括来讲其实就是正交,然后引入正交投影.最佳逼近定理等,这些概念将为我们在求无解的线性方程组Ax=b的最优近似解打下基石. 正交性: ...

  7. 《A First Course in Probability》-chaper2-概率论公理

    概率论自身有一套很深的理论体系,读过<几何原本>的读者会知道,伟大的欧几里得之所以伟大,是因为它基于几条最基本的公理,推导除了整个欧式几何学的理论体系,同样,在概率论这里,一切的推导都是源 ...

  8. 【转】SVN linux命令及 windows相关操作(二)

    转自这里:http://www.uml.org.cn/pzgl/200904246.asp 1 安装及下载client 端 2 什么是SVN(Subversion)? 3 为甚么要用SVN? 4 怎么 ...

  9. 417. Pacific Atlantic Water Flow

    正常做的,用了645MS..感觉DFS的时候剪枝有问题.. 为了剪枝可能需要标记一个点的4种情况: 1:滨临大西洋,所有太平洋来的点可以通过: 2:濒临太平洋,所有大西洋来的点可以通过: 3:都不濒临 ...

  10. CSS3动画变形transition

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...