出处:https://www.cnblogs.com/JerryWang1991/archive/2013/03/29/2981103.html

最近因为工作需要学习WPF方面的知识,因为以前只关注的是B/S架构的东西,可是没想到参加工作的第一个项目竟然是C/S架构的WPF方面的开发,因为Web方面主要是请求响应模型,没有事件这个东西,在学习webform时虽然是基于事件模型的也有没有认真的研究事件,因为它已经逐渐被mvc或者其他方式(比如ashx和jquery easyui等类似的)替代,现在是CS架构了,需要把这块知识补上。

1、简单的事件模型

事件的前身是消息,消息的本质就是一组数据记录这要执行的操作,然后消息处理函数根据消息的数据执行相应的操作,那么在消息处理函数中就充斥这大量的判断或者switch,这样对于大型应用程序的开发带来了不少麻烦。为了简单的开发微软封装了一套简单的事件模型。以前了解过window form的应该都知道,当托一个按钮到窗体后然后双击按钮就可以在.cs代码自动生成有关事件的代码,这就是一个简单的事件模型

事件模型包括一下几个部分,我们对应到一个winform程序的Demo中如下:

事件的拥有者:就是按钮:button1

事件:就是button1.Click,在Form1.cs中

事件的处理器就是这个方法button1_Click

订阅关系:也就是说事件和事件处理器如何建立联系的呢:

打开Form1.Designer.cs找答案

 this.button1.Click += new System.EventHandler(this.button1_Click);

这里就建立了事件和事件处理器的联系,当然一个事件我们也可以定义多个处理器相应。

最后一个响应者:就是窗体本身

2、路由事件模型

传统的简单事件模型中,在消息激发是将消息通过事件订阅的然后交给事件的相应者,事件的相应者使用事件的处理器来做出相应,这样就存在一个问题,用户控件内部的事件就不能被外界订阅,因为事件的宿主必须能够直接访问到事件的响应者。

路由事件的事件拥有者和事件的相应者之间则没有直接的显式订阅关系,事件的拥有者则只负责激发事件,事件将有谁相应它并不知道,事件的响应者则有事件的监听器,针对事件进行监听,当有此类事件传递至此事件响应者就使用事件处理器来相应事件并决定此事件是否继续传递。比如像上一个程序中的,点击“点我”以后事件就开始激发了,然后事件就会在控件树上进行传递,事件的响应者安装了监听器,当监听到这个事件进行响应,并决定这个事件是否继续传递。

如果当事件在某个节点处理以后,不想让它继续传递,可以把它标记为“已处理”,就会停止路由,所有的路由事件都共享一个公共的事件数据基类 RoutedEventArgs。RoutedEventArgs 定义了一个采用布尔值的 Handled 属性。把事件设为已处理只要把Handled属性设为true即可

Xaml代码:

<Window x:Class="RouteEventWpfDemo.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="GridA">
<Grid x:Name="GridB">
<Grid x:Name="GridC">
<Button Canvas.Left="101" Canvas.Top="68" Content="Button" Height="23" Name="ButtonA" Width="75" />
</Grid>
</Grid>
</Grid>
</Window>

xaml的交互逻辑代码

  public partial class MainWindow : Window
{/// <summary>
/// 主窗口构造器
/// </summary>
public MainWindow()
{
InitializeComponent();
//为GridA添加Button.ClickEvent监听
this.GridA.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonA_Click));
//为GridB添加Button.ClickEvent监听
this.GridB.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonA_Click));
//为GridC添加Button.ClickEvent监听
this.GridC.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonA_Click));
} private void ButtonA_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(((Grid)sender).Name);
}

3、自定义路由事件

自定义路由事件大体需要三个步骤:

1、声明并注册路由事件

2、为路由事件添加CLR事件包装

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

Xaml代码

<Window x:Class="MyRouteEventWpfDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyRouteEventWpfDemo"
local:TimeButton.ReportTime="ReportTimeHandler"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="GridA" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="GridB" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="GridC" local:TimeButton.ReportTime="ReportTimeHandler">
<StackPanel x:Name="StackPanelA" local:TimeButton.ReportTime="ReportTimeHandler">
<ListBox x:Name="listBox"/>
<local:TimeButton x:Name="btnTime" Content="时间" local:TimeButton.ReportTime="ReportTimeHandler" />
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>

自定义的ReportTimeEventArgs

 /// <summary>
/// 可以记录时间的RoutedEventArgs
/// </summary>
public class ReportTimeEventArgs : RoutedEventArgs
{
public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
: base(routedEvent, source)
{ } /// <summary>
/// 记录点击时间
/// </summary>
public DateTime ClickTime { get; set; }
}

定义一个时间Button

/// <summary>
/// 自定义个一个时间控件
/// </summary>
public class TimeButton : Button
{
//声明并注册路由事件
/*
* 1、第一个参数ReportTime 为路由事件的名称
* 2、第二个参数是路由事件的策略,包括Bubble冒泡式,Tunnel隧道式,Direct直达式(和直接事件类似)
* 3、第三个参数用于指定事件处理器的类型
* 4、第四个参数用于指定事件的宿主是哪一种类型
*/
public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent
("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton)); //CLR事件包装器
public event RoutedEventHandler ReportTime
{
add { this.AddHandler(ReportTimeEvent, value); }
remove { this.RemoveHandler(ReportTimeEvent, value); }
} //激发路由事件,借用Click事件的激发方法
protected override void OnClick()
{
base.OnClick(); ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
args.ClickTime = DateTime.Now;
this.RaiseEvent(args);
} //CLR事件包装器
public event RoutedEventHandler ReportTime
{
add { this.AddHandler(ReportTimeEvent, value); }
remove { this.RemoveHandler(ReportTimeEvent, value); }
} //激发路由事件,借用Click事件的激发方法
protected override void OnClick()
{
base.OnClick(); ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
args.ClickTime = DateTime.Now;
this.RaiseEvent(args);
}
}
}

xaml的交互逻辑代码

    /// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); } private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
string timeStr = e.ClickTime.ToString("HH:mm:ss");
string content = string.Format("{0}==>到达{1}", timeStr, element.Name);
this.listBox.Items.Add(content);
}
}

4、附加事件

在wpf中还有一种事件附加事件,像前面说的路由事件它的事件宿主都是我们可以看到的界面元素,但是附加事件不具备显式在用户界面上的能力,比如一个文本框的改变,鼠标的按下,键盘的按下这些事件都是附加事件的例子

还是上面的例子我们给他加上颜色改变事件

在TimeButton类中添加如下代码

        /// <summary>
/// 声明颜色属性
/// </summary>
public string Color { get; set; } /// <summary>
/// 声明并注册颜色改变路由事件
/// </summary>
public static readonly RoutedEvent ColorChangedEvent = EventManager.RegisterRoutedEvent
("ColorChanged", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton)); /// <summary>
/// 添加颜色改变事件
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
public static void AddColorChangedHandler(DependencyObject d, RoutedEventHandler e)
{
UIElement ui = (UIElement)d;
if (ui != null)
{
ui.AddHandler(TimeButton.ColorChangedEvent, e) ;
}
} /// <summary>
/// 删除颜色改变事件
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
public static void RemoveColorChangedHandler(DependencyObject d, RoutedEventHandler e)
{
UIElement ui = (UIElement)d;
if (ui != null)
{
ui.RemoveHandler(TimeButton.ColorChangedEvent, e);
}
}

MainWindow.cs改为

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//btnTime添加路由事件监听
TimeButton.AddColorChangedHandler(this.btnTime, new RoutedEventHandler(this.ColorChangedHandler));
} private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
string timeStr = e.ClickTime.ToString("HH:mm:ss");
string content = string.Format("{0}==>到达{1}", timeStr, element.Name);
this.listBox.Items.Add(content); this.btnTime.Color = "123";
//准备消息传给路由事件
RoutedEventArgs args = new RoutedEventArgs(TimeButton.ColorChangedEvent, this.btnTime);
//引发事件
this.btnTime.RaiseEvent(args);
} private void ColorChangedHandler(object sender, RoutedEventArgs e)
{
MessageBox.Show((e.OriginalSource as TimeButton).Color);
}
}

参考:http://msdn.microsoft.com/zh-cn/library/ms742806.aspx

http://msdn.microsoft.com/zh-cn/library/bb613550.aspx

----转载请注明出处http://www.cnblogs.com/JerryWang1991/ 谢谢!

WPF中的路由事件(转)的更多相关文章

  1. WPF中自定义路由事件

    public class MyButtonSimple: Button { // Create a custom routed event by first registering a RoutedE ...

  2. WPF 中的 路由事件

    public class ReportTimeEventArgs:RoutedEventArgs { public ReportTimeEventArgs(RoutedEvent routedEven ...

  3. WPF学习之路由事件

    原文:http://www.cnblogs.com/lxy131/archive/2010/08/10/1796754.html WPF中新添加了一种事件---路由事件 路由事件与一般事件的区别在于: ...

  4. 在WPF中应用弱事件模式

    http://www.cnblogs.com/rickiedu/archive/2007/03/15/676021.html 在wpf中应用弱事件模式        感谢VS 的Intellisens ...

  5. 整理:WPF中应用附加事件制作可以绑定命令的其他事件

    原文:整理:WPF中应用附加事件制作可以绑定命令的其他事件 目的:应用附加事件的方式定义可以绑定的事件,如MouseLeftButton.MouseDouble等等 一.定义属于Control的附加事 ...

  6. WPF 学习笔记 路由事件

    1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向 ...

  7. WPF原理剖析——路由事件

    一.路由事件与传统事件传统事件的触发者和处理者是紧密相连的,而路由事件则不是,路由事件允许一个元素的事件有另外的元素触发.也即就是说路由事件的拥有者和响应者之间没有显示的订阅关系.事件的拥有者只负责激 ...

  8. WPF中的Command事件绑定

    在项目中使用Command绑定能够使我们的代码更加的符合MVVM模式.不了解的同学可能不清楚,只有继承自ButtonBase类的元素才可以直接绑定Command(Button.CheckBox.Rad ...

  9. WPF手动触发路由事件

    MouseButtonEventArgs args = , MouseButton.Left); args.RoutedEvent = UIElement.MouseLeftButtonDownEve ...

随机推荐

  1. oracle 修改数据精度问题

    今天,在建表后插入语句时,报了这样的错误,对于这样的错误,搜集了一些资料,进行了总结如下: 建表语句: 1 2 3 4 5 6 7 8 9 create table pre_info( pre_cod ...

  2. 开发增强现实(AR)教程——识别图的那些坑

    第一期:Vuforia识别图的那些坑 一.Vuforia的图片识别机制 大学时学习的是计算机科学的数字媒体方向,图像处理粗略接触过,对于Vuforia的图片识别机制,只能大概讲一下步骤和猜想,无法给出 ...

  3. tcp/ip通信第5期之服务器端程序

    /* 此程序是tcp/ip通信服务器端程序,测试运行在redhat5上 重构readline函数,解决粘包问题——利用“\n”识别一个消息边界 */ #include<stdio.h> # ...

  4. 【svn】服务器搭建和迁移

    导语 svn客户端大部分开发都会用到,但是为什么我们仍然需要svn服务端呢? 理由可能有: 1,我们想存放一些属于自己的文档,而不像被其他人发现(在自己的网络环境中,安全性更高,更易用,不依赖于公司, ...

  5. BCHABC/BCHSV的矛盾所在

    BCHABC: 将BCH以后发展智能合约: 消息方面: 吴忌寒:BCH分叉不可避免 未来可能继续分叉 近日,吴忌寒在北大光华管理学院的区块链培训课程上发表演讲表示,在这种言论自由地环境下,我认为分裂就 ...

  6. mysql修改表引擎Engine

    修改my.ini,在[mysqld]下加上default-storage-engine=INNODB 其中红色字体部分是要指定的引擎名称.用sql语句修改已经建成表的引擎:alter table ta ...

  7. Redis进阶实践之二十 Redis的配置文件使用详解

    一.引言 写完上一篇有关redis使用lua脚本的文章,就有意结束Redis这个系列的文章了,当然了,这里的结束只是我这个系列的结束,但是要学的东西还有很多.但是,好多天过去了,总是感觉好像还缺点什么 ...

  8. 洛谷1462(重题1951) 通往奥格瑞玛的道路(收费站_NOI导刊2009提高(2))

    1462原题链接 1951原题链接 显然答案有单调性,所以可以二分答案,用\(SPFA\)或\(dijkstra\)跑最短路来判断是否可行即可. 注意起点也要收费,\(1462\)数据较水,我一开始没 ...

  9. POJ 2762 Going from u to v or from v to u?- Tarjan

    Description 判断一个有向图是否对于任意两点 $x$,  $y$ 都有一条路径使$x - >y$或 $y - >x$ Solution 对于一个强联通分量内的点 都是可以互相到达 ...

  10. css 需要阴影的效果

    box-shadow: 0 0 10px 10px #b9bcbf; CSS3 box-shadow 属性 CSS 参考手册 实例 向 div 元素添加 box-shadow: div { box-s ...