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. Android dumpsys命令的使用

    Android提供的dumpsys工具能够用于查看手机中的应用程序和系统服务信息与状态,手机连接电脑后能够直接命令行运行adb shell dumpsys 查看全部支持的Service可是这样输出的太 ...

  2. SQL于DML(数据库操作语言)采用

    1.Insert语句: INSERT [INTO] table [(column1, column2, column3, . . .)] VALUES(value1, value2, value3, ...

  3. elasticsearch的rest搜索--- 查询

    目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0   三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 四. 查询 1. 查询的官网的文档   ...

  4. MongoDB在window下的安装

    1.下载mongodb的windows版本号,有32位和64位版本号,依据系统情况下载,下载地址:http://www.mongodb.org/downloads 2.解压缩至D:/mongodb就可 ...

  5. 【百度地图API】建立全国银行位置查询系统(四)——如何利用百度地图的数据生成自己的标注

    原文:[百度地图API]建立全国银行位置查询系统(四)--如何利用百度地图的数据生成自己的标注 摘要: 上一章留个悬念,"如果自己没有地理坐标的数据库,应该怎样制作银行的分布地图呢?&quo ...

  6. Swift语言指南(一)--语言基础之常量和变量

    原文:Swift语言指南(一)--语言基础之常量和变量 Swift 是开发 iOS 及 OS X 应用的一门新编程语言,然而,它的开发体验与 C 或 Objective-C 有很多相似之处. Swif ...

  7. java程序连接MongoDB副本集测试

    三个节点有一个节点挂掉也不会影响应用程序客户端对整个副本集的读写! public class TestMongoDBReplSet { public static void main(String[] ...

  8. 解决Win7下一个VC++6.0您不能直接打开多个project问题

    于Win7操作系统,只需双击打开多个VC ++6.0的project当文件,前方和后方的工作区将关闭工作区,这项.VC++6.0仅仅通过文件-->开放式工作区-->找到磁盘project档 ...

  9. Appium根据xpath获取控件实例随笔

    如文章<Appium基于安卓的各种FindElement的控件定位方法实践>所述,Appium拥有众多获取控件的方法.其中一种就是根据控件所在页面的XPATH来定位控件. 本文就是尝试通过 ...

  10. SQL点滴6—“微软不认识闰年2月29日”&字符"N"的作用

    原文:SQL点滴6-"微软不认识闰年2月29日"&字符"N"的作用 http://www.cnbeta.com/articles/50580.htm这个 ...