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

2,WPF有两种树,Logical Tree和Visual tree。 LogicTree上,充当叶子的一般都是控件,如果我们把WPF的控件也放在“放大镜下观察”,你会发现每个WPF空间本身也是一棵更细微级别的组件组成的树。用来观察WPF控件的放大镜是我们提到的Blend。如果把Logical Tree衍生至Template组件级别,我们的到的就是Visual Tree。

3,路由事件是沿着Visual Tree传递的。

4.一个事件包括5方面:

  1. 事件的拥有者
  2. 事件
  3. 事件的响应者
  4. 事件处理器
  5. 订阅关系

5. 路由事件的原理: 舍弃直接事件响应者,让不同的控件变成监听者。 而事件拥有者,只负责出发事件。

6,使用WPF内置的路由事件:

Wpf系统的大多数事件都是可路由事件。我们以Button的Click事件来说明路由事件的使用。

1,如何添加监听者:

this.gridRoot.AddHandler(Button.ClickEvent, new RoutedEventHandler(DefineMethod));

上面这个AddHandler方法来自URElement类。 第一个参数是Button.ClickEvent, 这个叫做路由事件,这里也使用了类似依赖属性包装器的方式。

这里要注意的是,路由事件方法中RoutedEventHandler.Source 是gridRoot. 如果想要得到Button的话,使用RoutedEventHandler.OrigenalSource.

可以在Xaml中使用简化的方法:  <Grid X:Name= “gridRoot”  BackGround=”Lime” Button.Click=”ButtonClicked”>

7,自定义路由事件

三个步骤:

  1. 声明并注册路由事件
  2. 为路由事件添加CLR事件包装
  3. 创建可以激发路由事件的方法

看Code:

public abstract class ButtonBase: ContentControl, ICommandSource
{
public static readonly RoutedEvent ClickEvent =
/*注册路由事件*/
EventManager.RegisterRoutedEvent("Click",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase)); public event RoutedEventHandler Click
{
add {this.AddHandler(ClickEvent,value)}
Remove {this.RemoveHandler(ClickEvent, value)}
} protected virtual void OnClick()
{
RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent,this);
this.RaiseEVent(newEvent);
}
}

路由策略:

  1. Bubble  向上
  2. Tunnel 向下
  3. Direct  模仿CLR直接事件

Demo 2: 实现一个继承自Button的TimeButton类,并添加路由事件

//创建一个RoutedEventArgs类:
class ReportTimeEventArgs:RoutedEventArgs
{
Public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
:Base(routedEvent,source) {} Public DateTime ClickTime {get;set;}
}
class TimeButton : Button
{
//生命和注册路由事件
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);
}
//看看怎么使用吧
       local:TimeButton.ReportTime = “ReportTimeHandler”;
           //ReportTimeEvent 路由事件处理器
           private void ReportTimeHandler(objecet 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);
           }
8, 如何让路由事件停止传播:
      使用e.handled 如果e.handled==true,则停止传递。
9. RoutedEventArgs的Source和OriginalSource
Source是logicTree的消息源头,而OriginalSource是visualTree上的源头。
如果一个Button是在UserControl中,那么Source应该是UserControl , OriginalSource 应该是 Button/
10. 事件也附加----深入浅出附加事件
那些类拥有附加事件
  • Binding类: SourceUpdated事件、 TargetUpdated事件
  • Mouse类: MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等
  • Keyboard类: KeyDown事件、KeyUp事件
对比路由事件会发现: 附加事件不具备显示在用户界面上的能力。
看一个Demo:设计一个Student类,如果Student实例的Name属性值发生了变化就激发一个路由事件,然后使用界面元素来捕捉这个事件。
public class Student
{
public static readonly RoutedEvent NameChangedEvent = EventManager.ResisterRoutedEvent
( "NameChanged",routingStrategy.Bubble,typeof(RoutedEventHandler),typeof(Student)); public int Id {get;set;}
public string Name {get;set;}
}
然后我们设计一个button. 
<Button  x:Name = "button1" Content="OK"  Width="80" Height="80"   Click="Button_Click">
 看看后台代码 
   //添加事件监听器
  this.gridMain.AddHandler(Student.nameChangedEvent, new RoutedEventHandler(this.DtudentnameChangedHandler))
   //Click 事件处理器
   private void Button_Click(objcet sender, RoutedEventArgs e)
{
Student stu = new Student() {Id=10,Name="Tim"};
stu.Name="Tom";
//准备事件消息并发送路由事件
        //附加事件宿主是没有办法发送路由事件的,要借助一个FrameworkElement来RaiseEvent(arg)
        //其中RoutedEventArgs 有两个参数,一个附加事件,一个是实例。
RoutedEventArgs arg = new RoutedEventArgs(Student.NameChangedEvent,stu);
this.button1.RaiseEvent(arg);
} //Grid 捕捉到nameChangedEvent后的处理器
private void StudentNameChangedHandler(object sender, RoutedEventArgs e)
{
MessageBox.Show((e.OriginalSource as Student).Id.ToString());
}
11. 事件也附加2
其实上面那个例子已经是一个附加文件了,但是微软的官方文档约定要为这个附加事件添加一个CLR包装以便XAML编辑器识别并进行只能提示。 但是,因为Student类不是UIElement的派生类,因为不具备Addhandler和 RemoveHandler这两个方法,所以不能使用CLR属性作为包装器:
  • 为目标UI元素附加事件侦听器的包装器是一个名为Add*Handler的public static 方法,星号代表事件名称
public class Student
{
//声明并定义路由事件
public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRouredEvent
("NameChanged",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typdof(Student)); //为界面元素添加路由事件侦听
public static void AddNameChangedHandler(DependencyObject d,RoutedEventHandler h)
{
UIElement e = d as UIElement;
if(e!=null)
{
e.AddHandler(Student.NameChangedEvent, h);
}
} public static voidRemoveNameChangedHandler(DependencyObject d,RoutedEventHandler h)
{
UIElement e = d as UIElement;
if(e!=null)
{
e.AddHandler(Student.NameChangedEvent, h);
}
} public int Id {get;set;}
public string Name {get;set;}
}

public Window1()

{

Student.AddNameChangedHandler(this.gridMain, new RoutedEventHandler(this.StudnetnameChagnedHandler));

}

再次理解一下附加事件:

UIElement类是路由事件宿主与附加事件宿主的分水岭,不但是因为从UIElemtn类开始才具备了界面上显示的能力,还因为RaiseEvent、AddHandler和RemoveHandler 这些方法也定义在UIElement类中。 如果在一个非UIElement派生类中注册了路由事件,则这个类的实例既不能自己激发,也无法自己侦听此路由事件。

转载:http://www.cnblogs.com/zhaoyun2007/archive/2012/12/06/2804581.html

WPF 学习笔记 路由事件的更多相关文章

  1. WPF学习之路由事件

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

  2. WPF学习笔记-用Expression Design制作矢量图然后导出为XAML

    WPF学习笔记-用Expression Design制作矢量图然后导出为XAML 第一次用Windows live writer写东西,感觉不错,哈哈~~ 1.在白纸上完全凭感觉,想象来画图难度很大, ...

  3. WPF 学习笔记-在WPF下创建托盘图标

    原文:WPF 学习笔记-在WPF下创建托盘图标 首先需要在项目中引用System.Windows.Forms,System.Drawing; using System; using System.Co ...

  4. WPF 学习笔记-设置属性使窗口不可改变大小

    原文:WPF 学习笔记-设置属性使窗口不可改变大小 调整Windows下的ResizeMode属性: ResizeMode = NoResize Resize属性是控制Windows是否可以改变大小, ...

  5. 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

    学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...

  6. WPF学习笔记(8):DataGrid单元格数字为空时避免验证问题的解决

    原文:WPF学习笔记(8):DataGrid单元格数字为空时避免验证问题的解决 如下图,在凭证编辑窗体中,有的单元格不需要数字,但如果录入数字后再删除,会触发数字验证,单元格显示红色框线,导致不能执行 ...

  7. WPF中的路由事件(转)

    出处:https://www.cnblogs.com/JerryWang1991/archive/2013/03/29/2981103.html 最近因为工作需要学习WPF方面的知识,因为以前只关注的 ...

  8. Angular6 学习笔记——路由详解

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

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

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

随机推荐

  1. sql 比较不同行不同字段值

    需求:在一个表table中有两三列,分别是"货物名称"."进货时间"."出货时间"."存放天数",货物名称和两种&quo ...

  2. js Web存储方式

    JSON是数据交互中最常用的一种数据格式. 由于各种语言的语法都不同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串> 传递之后,可以讲JSON字符串,在解析为JSON对象. ...

  3. JavaScript 的使用基础总结③

    JavaScript 中的对象 JavaScript 中的所有事物都是对象:字符串.数值.数组.函数... JavaScript 允许自定义对象. (一)数组 数组对象的作用是:使用单独的变量名来存储 ...

  4. 团队作业4----第一次项目冲刺(Alpha版本)4.28

    a.提供当天站立式会议照片 会议内容 今天我们主要针对统计结果的表现形式进行了一些讨论,我们考虑是直接显示统计数据或者是用一些直观的图形来体现,最后经过讨论我们大部分人认为选择数据与图形更加形象直观. ...

  5. 201521123040《Java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代 ...

  6. 201521123045 《Java程序设计》第7周学习总结

    Java 第七周总结 1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Obj ...

  7. 201521123007《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 补充上周异常 异常堆栈追踪:获得异常发生的根源 创建自己的异常 自定义异常类不是由Java系统监测到的异常, ...

  8. Java课程设计——GUI密码生成器201521123035

    1.团队课程设计博客链接 (http://www.cnblogs.com/wuling15/p/7061857.html) 2.个人负责模块或任务说明 (1)确定课题并进行任务分工 (2)编写随机数产 ...

  9. 关于Linux的loop设备

    偶然发现/dev目录里有几个loop设备,一番搜索得知,这是一种伪设备(pseudo-device),它可以把一个文件连接为设备(就像Windows下用虚拟光驱挂载ISO文件). 遂做实验验证: 1. ...

  10. Java简单实用方法一

    整理以前的笔记,在学习Java时候,经常会用到一些方法.虽然简单但是经常使用.因此做成笔记,方便以后查阅 这篇博文先说明构造和使用这些方法. 1,判断String类型数据是否为空 String类型的数 ...