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中的事件为路由事件,所 ...
随机推荐
- 协定类型不具有 ServiceContractAttribute 特性
协定类型 ZBMService.QueryHistoryData 不具有 ServiceContractAttribute 特性.若要定义有效协定,指定的类型(协定接口或服务类)必须具有 Servic ...
- Nginx缓存配置及nginx ngx_cache_purge模块的使用
ngx_cache_purge模块的作用:用于清除指定url的缓存 下载地址:http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz 1. ...
- JavaScript高级程序设计4.pdf
虽然执行环境的类型总共只有两种——全局和局部(函数),但还有其他方法延长作用域链,有些语句可以在作用域链的前端临时增加一个变量对象,执行后会被移除try-catch语句的catch块和with语句 w ...
- [Git] Github客户端上publish后一直转圈,web上未上传成功
连续试了几次,publish后一直处于publish状态,点击其它repositories再点回来就没动静了,也看不到Sys按钮...最后发现,是要等很久才会成功,天朝的网络伤不起
- STL之deque、queue、stack、list
首先deque和vector是很像的 但是de代表double双向的队列那么deque可以从首部添加(删除)元素也可以从尾部添加(删除)元素. .push_back(elem) .push_front ...
- 京东集团副总裁李大学:像CEO一样思考 - Dancen的专栏 - 博客频道 - CSDN.NET
京东集团副总裁李大学:像CEO一样思考 - Dancen的专栏 - 博客频道 - CSDN.NET 京东集团副总裁李大学:像CEO一样思考
- hdu5127 Dogs' Candies CDQ分治 动态凸包
传送门 题意 有三种操作 加入一个二元组\((x,y)\) 删除一个二元组\((x,y)\) 给出一个二元组\((a,b)\),问\(ax+by\)的最大值 题解 \(z=ax+by \Rightar ...
- Linux下安装 TestLink常见问题解决方法
Read/write permissions For security reason we suggest that directories tagged with [S] on following ...
- Android常用网址[转]
转自:http://my.oschina.net/u/593225/blog/404423 1.AndroidDevTools URL: http://www.androiddevtools.cn/ ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(27)-权限管理系统-分配用户给角色
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(27)-权限管理系统-分配用户给角色 分配用户给角色,跟分配角色给用户操作是基本一致的. 打开模块维护,展 ...