Wpf自定义路由事件
创建自定义路由事件大体可以分为三个步骤:
①声明并注册路由事件。
②为路由事件添加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自定义路由事件的更多相关文章
- WPF:自定义路由事件的实现
路由事件通过EventManager,RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数:通过RaiseEvent方法来 ...
- WPF自定义路由事件(二)
WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件. 1.WPF内置路由事件 WPF ...
- 细说WPF自定义路由事件
WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件. 1.WPF内置路由事件 W ...
- WPF 自定义路由事件
如何:创建自定义路由事件 首先自定义事件支持事件路由,需要使用 RegisterRoutedEvent 方法注册 RoutedEvent C#语法 public static RoutedEvent ...
- WPF自定义路由事件(一)
首先自定义事件支持事件路由,需要使用 RegisterRoutedEvent 方法注册 RoutedEvent C#语法 public static RoutedEvent RegisterRoute ...
- WPF 自定义路由事件 与 附加路由事件
为student添加附件事件
- WPF自学入门(四)WPF路由事件之自定义路由事件
在上一遍博文中写到了内置路由事件,其实除了内置的路由事件,我们也可以进行自定义路由事件.接下来我们一起来看一下WPF中的自定义路由事件怎么进行创建吧. 创建自定义路由事件分为3个步骤: 1.声明并注册 ...
- WPF路由事件三:自定义路由事件
与依赖项属性类似,WPF也为路由事件提供了WPF事件系统这一组成.为一个类型添加一个路由事件的方式与为类型添加依赖项属性的方法类似,添加一个自定义路由事件的步骤: 一.声明路由事件变量并注册:定义只读 ...
- WPF的路由事件、冒泡事件、隧道事件(预览事件)
本文摘要: 1:什么是路由事件: 2:中断事件路由: 3:自定义路由事件: 4:为什么需要自定义路由事件: 5:什么是冒泡事件和预览事件(隧道事件): 1:什么是路由事件 WPF中的事件为路由事件,所 ...
随机推荐
- ssh 认证指定端口
[root@database2 ~]# cat ssh.sh if [ ! $# -eq 2 ] ;then echo "请输入用户密码以空格分开" exit else ssh-k ...
- SQL*Net more data from client
SQL*Net more data from client The server is waiting on the client to send more data to its client sh ...
- 一起啃PRML - Preface 前言
一起啃PRML - 前言 Preface @copyright 转载请注明出处 http://www.cnblogs.com/chxer/ PRML,Pattern Recognition and M ...
- Java---基于TCP协议的相互即时通讯小程序
这是几年前,新浪的一个面试题~要求是3天之内实现~ 通过TCP 协议,建立一个服务器端. 通过配置服务器端的IP和端口: 客户端之间就可以相互通讯~ 上线了全部在线用户会收到你上线的通知. 下线了全部 ...
- leetcode 字符串分割对称
public class Solution { public List<List<String>> partition(String s) { int len=s.length ...
- poj 1704 Georgia and Bob(阶梯博弈)
Georgia and Bob Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 8656 Accepted: 2751 D ...
- iOS中控件的Frame属性和Bounds属性的区别
在iOS中,每个控件都是继承于UIView的,都会有视图的属性存在,控制这个视图的位置就有Frame和Bounds两个属性 frame指的是:该view在父view坐标系统中的位置和大小.(参照点是父 ...
- [git] git 的基本认知
版本管理 ( Version Control ) 版本管理系统是一个记录文件变更的系统,让你在一段时间后可以恢复指定版本的文件.版本管理系统大致可分为三类:独立的本地版本管理系统.中心化版本管理系统. ...
- 常见的HTTP状态码深入理解
状态码的职责是当客户端向服务器端发送请求时,描述返回请求结果.借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了什么错误. RFC2616定义的状态码,由3位数字和原因短信组成. 数字中的第 ...
- usb转串口如何配置?
概述 USB转串口输出,在kernel启动阶段加载相应的usb转串口芯片驱动,加载成功后,可通过usb转串口与pc机端交互. 步骤 1. 在kernel配置中选中usb转串口驱动: 2. 传给内核 ...