1. 理解路由事件:WPF 通过事件路由(event routing)概念增强了传统的事件执行的能力和范围,允许源自某个元素的事件由另一个元素引发,例如,事件路由允许工具栏上的一个按钮点击的事件在被代码处理之前上传到工具栏,再由工具栏上传到所属窗体

2. 定义、注册和包装路由事件:和依赖性属性类似,它由只读的静态字段表示,在一个静态构造函数中注册,并通过一个标准的 .Net 事件定义进行包装。如 Button 的 Click 事件,该事件继承自抽象的 ButtonBase 基类

public abstract class ButtonBase:ContentControl...
{
//The event definition
public static readonly RoutedEvent ClickEvent; // event registration
static ButtonBase()
{
ButtonBase.ClickEvent = EventManager.RegisterRoutedEvent(
"Click",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));
...
} // The traditional event wrapper
public event RoutedEventHandler Click
{
add{
base.AddHandler(ButtonBase.ClickEvent,value);
}
remove{
base.RemoveHandler(ButtonBase.ClickEvent,value);
}
}
}

通过使用普通的事件定义进行包装,确保所有 .Net 的语言都能访问它。AddHandler 和 RemoveHandler 是在 FrameworkElement 中定义的,并被每个 WPF 元素继承。

3. 处理路由事件:从语法上,XAML 可以这样写

<Image Source="happy.jpg" Stretch="None" Name="img" MouseUp="img_MouseUp"/>

比较约定俗成的命名方式是“元素名_事件名”,如没有交互的极简事件也可以不命名元素

<Button Click="cmdOk_Click">OK</Button>

C# 还允许流线型的写法,隐式创建合适的委托对象

img.MouseUp += img_MouseUp;

上面的代码依赖事件包装器,事件包装器调用的是 UIElement.AddHandler() 方法,也可以直接调用直连事件

img.AddHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

也可以使用“定义事件的类的名称”而不是用“引用事件的类的名称”,如改造成下面的写法

img.AddHandler(UIElement.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

两种写法在语法上是等效的,但第二种的缺点是,不能明确的指明 MouseUpEvent 是由 Image 提供的,在嵌套的元素中,可能会引起混论和遗忘,是代码难于理解。

想要断开事件,只能使用代码

img.MouseUp -= img_MouseUp;

或 UIElement.RemoveHandler() 方法

img.RemoveHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));

为同一事件多次连接相同的事件处理程序,在技术上是可行的,但属于编码错误,事件处理程序会被触发多次,即 += 就多加一次,-= 也只减一次

4. 事件路由:模拟一个场景,一个 Label 包含一个 StackPanel 面板,面板中包含两个 TextBlock 和一个 Image。现在,想要点击 Label 中的任意元素都触发同一个事件处理程序。当然可以为每一个元素的 MouseUp 和 MouseDown 都关联到相同的事件处理程序,但这将使代码看起来混乱不好维护,WPF 使用事件路由模型提供了更好的方法。

路由事件有三种方式实现:

直接路由事件(Direct event),起源于一个元素,不传递其它元素。

在包含的层次中向上传递的冒泡路由事件(Bubbling event),例如 MouseDown 就是一个泡泡事件,首先由改元素引发、再由父元素引发、最后被父元素的父元素引发,直至 WPF 元素树顶部。

在包含的层次中向下传递的隧道路由事件(Tunneling event),隧道事件在到达恰当的控件前为预览事件(或可能终止的事件)创造了机会,例如,PreviewKeyDown 可以截获是否按下了一个键,首先在窗口级别上,然后是容器,直到到达按下键时具有焦点的元素。

使用 EventManger.RegisterEvent 注册事件时,传递 RoutingStrategy 枚举值指示希望应用于事件的行为

5. 处理挂起的事件:当事件的 Handle 属性被置为 true 意味着通知其它侦听对象此事件被处理过了,有一个方法用于接收被处理过的事件,它必须使用前面介绍过的 AddHandler 方法的一个重载版本,将第三个 bool 类型参数设置为 true(举以下代码这个例子不太恰当,会有隐患,在此遵照原内容)

cmdClecr.AddHandler(UIElement.MouseUpEvent,new MouseButtonEventHandler(cmdClear_MouseUp),true)

6. 附加事件:如果一个 StackPanel 里边有数个 Button 并且希望这些 Button 在点击时都能处理相同的事件,通常的想法是将所有 Button 的 Click 事件都关联到相同的处理程序,但在 WPF 中,可以通过在更高层次的元素上处理这个事件来解决,如下面

<StackPanel Click="DoSomething">
<Button Name="cmd1">Command 1</Button>
<Button Name="cmd2">Command 2</Button>
<Button Name="cmd3">Command 3</Button>
</StackPanel>

看起来写的很美,但实际不能运行,因为 StackPanel 木有 Click 事件,咋办?可以这样

<StackPanel Button.Click="DoSomething"/>

以“类名.事件名”的方式使用不同的关联事件的语法,解释一下,Click 是在 ButtonBase 中定义的,Button 继承自 ButtonBase。如果使用 ButtonBase.Click 的方式那么所有继承自 ButtonBase 的控件在单击时,都将使用“DoSomething”事件处理程序,Button.Click 更加精确。可以在代码中关联附加事件,但必须要用 AddHandler 而不是 +=,如(StackPanel 被命名为 pnlButtons)

pnlButtons.AddHandler(Button.Click,new RoutedEventHandler(DoSomething));

这会碰到一个问题,DoSomething 方法中如果想知道具体是谁引发了事件怎么办?可以依靠判断 sender 比如 sender == cmd1,但这需要为每个 Button 都设置 Name 属性,另一个选择是随按钮传递一个可在代码中使用的信息,比如为每个 Button 设置一个 Tag 属性

<StackPanel Click="DoSomething">
<Button Name="cmd1" Tag=“The first button”>Command 1</Button>
<Button Name="cmd2" Tag=“The second button”>Command 2</Button>
<Button Name="cmd3" Tag=“The third button”>Command 3</Button>
</StackPanel>

然后在 DoSomething 中这样

private void DoSomething(object sender,RoutedEventArgs e )
{
object tag = ((FrameworkElement)sender).Tag;
MessageBox.Show((string)tag);
}

7. 隧道路由:和冒泡类型,方向相反,在一个控件中引发的事件最先从顶层(通常是窗体)开始引发,然后是容器,最后到达实际引发的控件。隧道事件容易识别,它们都是以 Preview 开头,WPF 通常成对的定义冒泡和隧道,且隧道路由事件总是在冒泡路由事件前被引发。注意,如果隧道事件被标记为已处理,那么冒泡将不会被引发,这是因为两者共享一个 RoutedEventArg 对象的实例。

点击下载代码 —— 需 .Net Framework 4 或以上

迟到的 WPF 学习 —— 路由事件的更多相关文章

  1. WPF - 善用路由事件

    原文:WPF - 善用路由事件 在原来的公司中,编写自定义控件是常常遇到的任务.但这些控件常常拥有一个不怎么好的特点:无论是内部还是外部都没有使用路由事件.那我们应该怎样宰自定义控件开发中使用路由事件 ...

  2. 学习WPF——了解路由事件

    入门 我们先来看一个例子 前台代码: 后台代码: 点击按钮的运行效果第一个弹出窗口 第二个弹出窗口: 第三个弹出窗口: 说明 当点击按钮之后,先触发按钮的click事件,再上查找,发现stackpan ...

  3. WPF的路由事件、冒泡事件、隧道事件(预览事件)

    本文摘要: 1:什么是路由事件: 2:中断事件路由: 3:自定义路由事件: 4:为什么需要自定义路由事件: 5:什么是冒泡事件和预览事件(隧道事件): 1:什么是路由事件 WPF中的事件为路由事件,所 ...

  4. WPF:自定义路由事件的实现

    路由事件通过EventManager,RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数:通过RaiseEvent方法来 ...

  5. 【WPF】路由事件

    总结WPF中的路由事件,我将学到的内容分为四部分来逐渐掌握 第一部分:wpf中内置的路由事件 以Button的Click事件来说明内置路由事件的使用 XAML代码: <Window x:Clas ...

  6. WPF自定义路由事件(二)

    WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件. 1.WPF内置路由事件 WPF ...

  7. WPF中路由事件的传播

    路由事件(RoutedEvent)是WPF中新增的事件,使用起来与传统的事件差别不大, 但传播方式是完全不同的. 路由事件的传播方式 通过RoutingStrategy来定义传播的方式 public ...

  8. WPF 冒泡路由事件

    在WPF中,例如,可以构建一个包含图形的按钮,创建一个具有文本和图片混合内容的标签,或者为了实现滚动或折叠的显示效果在一个特定的容器中放置内容.甚至可以多此重复嵌套,直到达到您所希望的层次深度. 这种 ...

  9. WPF 隧道路由事件

    阅读本文前,请先了解 冒泡路由事件:http://www.cnblogs.com/andrew-blog/p/WPF_BubbledEvent.html 隧道路由事件的工作方式和冒泡路由事件相同,但方 ...

随机推荐

  1. Spring搭建MVC WEB项目[转]

    原文链接:http://blog.csdn.net/initphp/article/details/8208349 1.创建一个web项目 2.假设,我们已经安装完毕Spring所需要的依赖包,以及一 ...

  2. Quartz.Net任务统一调度框架

    山寨版Quartz.Net任务统一调度框架   TaskScheduler 在日常工作中,大家都会经常遇到Win服务,在我工作的这些年中一直在使用Quartz.Net这个任务统一调度框架,也非常好用, ...

  3. js中,(function(){})()的用法解析

    (function($){...})(jQuery)  含义 经常用,今天总结一下,下文摘自某网友的总结: (function($){...})(jQuery)实际上是匿名函数,不懂得朋友可以继续往下 ...

  4. 远程数据client交换器

    不太繁忙的文本. 要被写入

  5. 熊猫猪新系统測试之三:iOS 8.0.2

    本来本猫要等到8.1版本号出来后再做測试的,结果等来等去就是迟迟不推送更新呀!说好10月20号的iOS 8.1呢?为了一鼓作气写完,就先不等了.先拿手头的iOS 8.0.2系统做一下測试吧! 8.x系 ...

  6. ShellExecuteEx的使用方法

    关于怎样在c++中启动外部的exe程序,之前看到在百度一搜就看到了: ShellExecute(this->m_hWnd,"open","calc.exe" ...

  7. 管理员控制Windows Service

    C# 以管理员方式启动Winform,进而使用管理员控制Windows Service   问题起因: 1,) 问题自动分析Windows服务在正常运行时,确实会存在程序及人为原因导致该服务停止.为了 ...

  8. JavaScript语法细节——引用与复制

    原文:JavaScript语法细节--引用与复制 我们都知道,JS中变量的赋值有两种方式,最近在折腾自己写的标签栏插件,碰到了很多平时没注意的问题.正好,那边处理清楚了,稍微整理一下关于引用与复制相关 ...

  9. mysql 使用记号

    1. 避免重复入库的插入记录方法 向一个数据库中插入且在未存在的情况下插入一行记录.若有主键可以区分则可以使用 replace into 方法, 单偏偏很多时候数据库设计者会设计自增主键,replac ...

  10. visual studio 辅助工具

    resharper  这是一个收费软件 需要下载对应版本的注册机. 效果如图所示: 这里的using 很多没有用到,他会用灰色标记,你都可以统统去掉. 声明一个类 ,要求 首字母大写,如果你小写了,他 ...