WPF学习之事件(一)

  就像属性系统在WPF中得到升级、进化为依赖属性一样,事件系统在WPF中也被升级,从而进化成为——路由事件(Routed Event),并在其基础上衍生出命令传递机制。就让我们一起来领略这些新消息机制的风采吧!

  1、近观WPF的树形结构。

  路由(Route)一词的大意为:起点和终点之间有若干个中转站,从起点出发后经过每个中转站时都要进行选择,最终以正确(比如最短或者最快)的路径到达终点。我们知道,WPF的UI是由布局组件和控件构成的属树形结构。因此,当这棵树上的某个节点激发出某个事件的时候,程序员可以选择以传统的直接事件模型让响应者响应之,也可以让这个事件在UI组件树沿着一定的方向传递且路过多个中转点,并在这个路由中被恰当的处理。因为WPF事件的路由环境是UI组件树,因此我们有必要先研究一下这棵树。4

  在WPF中有两种树,一个为逻辑树(Logical Tree),另一个叫可视化元素树(Visual Tree)。逻辑树最大的特点就是它完全由布局组件和控件构成(包括列表控件中的条目元素),简单点说,就是它的每个节点不是布局组件就是控件。相对的可视化元素树其实是对Logical Tree的一种细分。在Logical Tree中,节点是一般都是控件,Visual Tree就是有组成控件的更加细小的组件(他们不是控件,而是一些可视化组件,派生自Visual类)来构成的。

  2、事件的来龙去脉

  事件的前身是消息(Message)Windows就是消息驱动的操作系统。消息驱动对于一个刚刚入门的Windows开发人员来说门槛太高,随着微软面向对象平台的成熟,微软把之前的消息机制封装成更容易让人们理解的事件模型。整个消息机制在事件模型中被简化成三个特点:

  • 事件的拥有者:即消息的发送者。事件的宿主可以在某些条件下激发它所拥有的事件,即事件被触发
  • 事件的响应者:即消息的接收者、处理者。事件接收者使用其事件的处理器(Event Handel)对事件作出响应
  • 事件的订阅关系:事件的拥有者可以随时的触发事件,但是事件发生之后会不会得到响应要看有没有事件的响应者,或者说要看这件事件有没有被关注

  直接事件模型是传统.Net开发中对象之间相互协调、沟通信息的主要手段,它在很大程度上简化了程序的开发。然后直接事件模型并不完美,它的不完美之处就在于事件的响应者与事件的拥有者之间必须建立事件订阅这样一个关系。

  直接事件模型的弱点在以下两种情况下会暴露出来:

  (1)程序运行期在容器中动态生成一组相同的控件,每个控件的同一个事件都使用同一个事件处理器来响应。面对这种情况,我们在动态生成代码的同时就需要显示书写事件订阅代码。

  (2)用户控件内部事件不能被外接所订阅,必须对用户控件定义新的事件向外界暴露内部事件。当模块划分很细的时候,UI组件的层级会很多,如果想让最外层的容器订阅深层控件的某个事件就需要为每一层组件定义用于暴露内部事件的事件,形成事件链。

  路由事件的出现,很好的解决了上面提到的两个问题。

  3、路由事件

  为了降低由事件订阅带来的耦合度和代码量,WPF退出了路由事件机制。路由事件与直接事件的区别在于:直接事件被触发时,发送者直接将消息通过事件订阅交给事件响应者,事件响应者在作出相应的事件;路由事件则没有显示的订阅关系,事件的发送者只负责触发事件,至于是谁响应事件它并不关心,但是事件的响应者提前安装事件的监听器,针对某类事件进行监听,当有此类事件传过来的时候,事件响应者怎会进行响应事件并决定事件是否再被传递。

  3.1 使用WPF内置的路由事件

  此处我们以Button的Click事件来说明路由事件的使用。

  首先进行如下布局:(XAML代码如下)

  

<Window x:Class="_01_使用WPF内置路由事件.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_01_使用WPF内置路由事件"
        mc:Ignorable="d"
        Title="Routed" Height="200" Width="200">
    <Grid>
        <Grid Name="gridRoot" Background="Lime">
            <Grid Name="gridA" Margin="10" Background="Blue">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Canvas Name="canvasLeft" Grid.Column="0" Background="Red" Margin="10">
                    <Button Name="buttonLeft" Content="Left" Width="40" Height="100" Margin="10"></Button>
                </Canvas>
                <Canvas Name="canvasRight" Grid.Column="1" Background="Yellow" Margin="10">
                    <Button Name="buttonRight" Content="Right" Width="40" Height="100" Margin="10"></Button>
                </Canvas>
            </Grid>
        </Grid>
    </Grid>
</Window>

  其运行效果及Logical Tree结构如下:

        

  当单击buttonLeft时,Button.Click事件就会沿着buttonLeft---CanvasLeft-----gridA-------gridRoot-----Window线路传递。因为目前还没有哪个节点侦听Click事件,所以单击按钮之后尽管事件向上传递却并没有接到响应。下面,我们让gridRoot安装针对Button.Click的事件侦听器。

  方法很简单,就是在窗体的构造器中调用gridRoot的AddHandler方法把想侦听的事件和事件处理器关联起来:

  

public MainWindow()
        {
            InitializeComponent();
            //为gridRoot安装针对Button.Click事件的监听器
            this.gridRoot.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonClicked));
        }

  上面的代码让最外层的Grid(gridRoot)能够捕捉到从“内部”飘出来的按钮单击事件,捕捉到会用this.ButtonClicked方法来进行响应处理。ButtonClicked代码如下:

  

 private void ButtonClicked(object sender,RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as FrameworkElement).Name);
        }

  这里有一点非常重要:因为路由事件(的消息)是从内部一层层传递出来最后到达最外层的gridRoot,并且由gridRoot元素把消息事件交给Button_Click方法来处理,所以传入Button_Click方法的参数obj实际上是gridRoot而不是被单击的Button,这与直接的传统事件有些不一样。如果想查看事件的源头(最初发起者)怎么办呢?答案是使用e.OriginalSource,使用它的时候需要是用as/is操作符或着强制类型把它识别/转换为正确的类型。

  运行程序单击右边的按钮,效果如下:

  

  上述为元素添加路由时间在XAML代码里面也可以完成,只需要把XAML代码改成这样即可:

  

<Grid x:Name="gridRoot" Background="Lime" Button.Click="Button_Click">  

  To Be Continue

时间: 2024-12-28 15:27:42

WPF学习之事件(一)的相关文章

迟到的 WPF 学习 —— 路由事件

1. 理解路由事件:WPF 通过事件路由(event routing)概念增强了传统的事件执行的能力和范围,允许源自某个元素的事件由另一个元素引发,例如,事件路由允许工具栏上的一个按钮点击的事件在被代码处理之前上传到工具栏,再由工具栏上传到所属窗体 2. 定义.注册和包装路由事件:和依赖性属性类似,它由只读的静态字段表示,在一个静态构造函数中注册,并通过一个标准的 .Net 事件定义进行包装.如 Button 的 Click 事件,该事件继承自抽象的 ButtonBase 基类 public a

WPF学习之事件的学习(二)

3.2自定义路由事件 为了方便程序中对象之间的通信,通常需要我们自己定义一些路由事件.那么如何去创建自定义路由事件呢?下面通过一个例子来说明自定义路由事件的创建. 创建自定义路由事件大体来说分为三个步骤: 声明并注册路由事件 首先,定义路由事件与依赖属性的定义手法极为相似——申明一个由public static readonly修饰的RoutedEvent类型的字段,然后使用EventManager类的RegisterRoutedEvent方法进行注册. 完整的注册路由事件的代码如下: //声明

【WPF学习】第十四章 事件路由

原文:[WPF学习]第十四章 事件路由 由上一章可知,WPF中的许多控件都是内容控件,而内容控件可包含任何类型以及大量的嵌套内容.例如,可构建包含图形的按钮,创建混合了文本和图片内容的标签,或者为了实现滚动或折叠的显示效果而在特定容器中放置内容.设置可以多次重复嵌套,直至达到你所希望的层次深度.如下所示: <Window x:Class="RouteEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2

WPF自定义路由事件

一 概要 本文通过实例演示WPF自定义路由事件的使用,进而探讨了路由事件与普通的CLR事件的区别(注:"普通的CLR事件"这个说法可能不太专业,但是,我暂时也找不到什么更好的称呼,就这么着吧,呵呵.)(扩展阅读:例说.NET事件的使用). 二 实例演示与说明 1 新建DetailReportEventArgs类,该类派生自RoutedEventArgs类,RoutedEventArgs类包含与路由事件相关的状态信息和事件数据.DetailReportEventArgs类中定义了属性Ev

WPF学习------XAML 语法详述

XAML 语言规范 XAML 语言规范中也定义或引用了此处定义的 XAML 语法术语. XAML 是一种基于 XML 并遵循或扩展 XML 结构规则的语言. 其中某些术语共享自或基于描述 XML 语言或 XML 文档对象模型时常用的术语. 有关 XAML 语言规范的更多信息,请从 Microsoft 下载中心下载 [MS-XAML]. XAML 和 CLR XAML 是一种标记语言. 顾名思义,公共语言运行时 (CLR) 实现了运行时执行. XAML 本身并非 CLR 运行时直接使用的一种公共语

8 WPF学习之深入浅出话属性

转载:http://blog.csdn.net/fwj380891124/article/details/8131080 通过前面的学习,我们已经知道Data Binding是WPF"数据驱动UI"理念的基础.上一章我们将主要的精力放在了Binding的数据源这一端,研究了Binding的Source和Path.本章我们将把目光移向Binding的目标端,研究一下什么样的对象才能作为Binding的Target以及Binding将把数据送往何处. 1.1      属性(Propert

WPF学习笔记2&mdash;&mdash;XAML之2

三.事件处理程序与代码隐藏 例如,为一个Page添加一个Button控件,并为该Button添加事件名称Button_Click: <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ExampleNamespace.ExampleP

WPF学习07:MVVM 预备知识之数据绑定

MVVM是一种模式,而WPF的数据绑定机制是一种WPF内建的功能集,两者是不相关的. 但是,借助WPF各种内建功能集,如数据绑定.命令.数据模板,我们可以高效的在WPF上实现MVVM.因此,我们需要对各种MVVM相关的WPF内建功能集进行了解,才能在扎实的基础上对MVVM进行学习与实践. 本文是WPF学习03:Element Binding的后续,将说明实现数据绑定的三个重点:DataContext INotifyPropertyChanged IValueConverter MVVM简介 MV

WPF学习笔记(二)----Events, Commands

1.配置事件和事件处理(Configuring Events and Event Handling) 事件在WPF编程是明显不同于那些在传统的Windows窗体的编程.WPF使用路由事件,可以存在多个控制和多个处理程序.路由事件允许您添加多个层次的复杂性和复杂的用户界面,响应用户输入.学习路由事件,包括如何处理一个路由事件,定义和注册一个新的路由事件,处理应用程序生命周期事件,并使用EventManager类. 1.1 下面使用一个实例说明如何定义.注册.封装路由事件 public abstra