出处: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. git add和git commit

    git命令使用:提交前可指定要提交哪些文件,然后使用git commit来提交 样例: git status 输出: Changes to be committed: modified:   app/ ...

  2. selenium验证码和错误截图

    验证码的识别: 1,破解验证码 OCR识别(一般使用tesseract-ocr) 人工智能(AI机器学习 TensorFlow,成本大) 2,绕过验证码 1, 让开发人员临时关闭验证码 2,提供万能验 ...

  3. 简述 OAuth 2.0 的运作流程(转)

    原文地址:http://www.barretlee.com/blog/2016/01/10/oauth2-introduce/ 本文将以用户使用 github 登录网站留言为例,简述 OAuth 2. ...

  4. Django中使用django_debug_toolbar

    一 概述 django_debug_toolbar 是django的第三方工具包,给django扩展了调试功能. 包括查看执行的sql语句,db查询次数,request,headers,调试概览等. ...

  5. jquery ajax 中实现给变量赋值

    我们在用JQuery的Ajax从后台提取数据后想把它赋值给全局变量,但是却怎么都赋不进,为什么呢? 原因其实很简单,我们用的Ajax是异步操作,也就是说在你赋值的时候数据还没提取出来,你当然赋不进去, ...

  6. Query to find the eligible indexes for rebuilding

    Query to find the eligible indexes for rebuilding The following script can be used to determine whic ...

  7. c++ opencv 3.2 +Mfc VS2015窗体显示图片方法

    本文仅涉及一些核心步骤,具体 OpenCV 的配置以及其他的细节问题,请参考 VS2010 / MFC + OpenCV 2.4.1打开图片. 1. 新建 MFC 对话框项目 基于对话框,不使用Uni ...

  8. RabbitMQ消息队列(一):详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  9. HDU_1142(最短路 + dfs)

    Jimmy experiences a lot of stress at work these days, especially since his accident made working dif ...

  10. 探索未知种族之osg类生物---起源

    任何程序都是有生命的,是生命就需要呼吸.例如普通的windows程序,当运行完main()函数后,就需要进入消息循环,来监听用户的各种操作,以便做出及时的回应.这样的每次循环就像生命的每次呼吸,来维持 ...