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. Office转HTML

    /// <summary> /// word转成html /// </summary> /// <param name="path"></ ...

  2. Beginning Python From Novice to Professional (7) - 类别

    类别 创建一个简单的类: #!/usr/bin/env python __metaclass__ = type class Person: def setName(self,name): self.n ...

  3. 编译 & 预处理

    编译(compilation , compile) 1.利用编译程序从源语言编写的源程序产生目标程序的过程. 2.用编译程序产生目标程序的动作. 编译就是把高级语言变成计算机可以识别的2进制语言,计算 ...

  4. js checkbox多选值采集

    var objs = document.getElementsByTagName("input"); for (var i = 0; i < objs.length; i++ ...

  5. Cocos2D-X2.2.3学习笔记3(内存管理)

    本章节介绍例如以下: 1.C/C++内存管理机制 2.引用计数机制 3.自己主动释放机制 1.C/C++内存管理机制 相信仅仅要懂oop的都知道NEW这个keyword吧,这个通俗点说事实上就是创建对 ...

  6. 将已有的工程项目添加到Xcode到Git管理中

    在Xcode中创建工程的时候,我们很容易的可以将新创建的工程添加到Git中,如图: 但是如果是本地已经有的工程,那该如何添加到Git中呢? 首先终端进入到该工程的目录. 然后: git init gi ...

  7. 小结css2与css3的区别

    CSS3引进了一些新的元素新的特性,我收集以下,自己做了一个小结: animation(基础动画)eg:  div{animation: myfirst 5s linear 2s infinite a ...

  8. SQL编程之生日问题

    在学习MySQL的时候,一个较为经典的SQL编程题目就是生日问题,已知某个用户的出生日期和当前日期,计算他近期的生日. 一般须要考虑两个问题 闰年2月是29天 今年的生日是否过完 比如:某人的生日是1 ...

  9. HTML5新增核心工具——canvas

    原文:HTML5新增核心工具--canvas Canvas元素称得上是HTML5的核心所在,它是一个依靠JavaScript绘制华丽图像的元素. Canvas由一个可绘制地区HTML代码中的属性定义决 ...

  10. java-list-remove()用法浅析 解决java list remove() 数据不对的问题

    在java中对list进行操作很频繁,特别是进行list启遍历,这些操作我们都会,也很熟悉,但是对java中list进行删除元素,remove list中的元素就不怎么熟悉了吧,可以说很陌生,是实际操 ...