08_深入浅出话事件

备注:类关系结构图如下:

在这里插入图片描述

事件(Event)

Logical Tree 和 Visual Tree

  • WPF 中有两种树,一种是逻辑树(Logical Tree),一种是可视元素树(Visual Tree)。我们所见到的所有树形结构都是 Logical Tree,Logical Tree最显著的特点就是它完全由布局组件和控件构成,每个 WPF 控件本身也是一棵由更细级别的组件组成,他们是 Visual Tree。大多数情况下我们都是和Logical Tree 打交道。
  • 如果想在 Logical Tree 上查找元素,可以借助 LogicalTreeHelper 类的 static 方法。
    • BringIntoViw:把选定的元素带进用户可视区域,经常用于可滚动的视图。
    • FindLogicalNode:按照给定的名称(Name 属性)查找元素,包括子级树上的元素。
    • GetChildren:查找所有直接子级元素。
    • GetParent:获取直接父级元素。
  • 如果想在 Visual Tree 上查找元素,则可以借助 VisualTreeHelper 类的 static 方法。
  • 注意:当一个路由事件被激发之后是沿着 Visual Tree 走的,只有这样,藏在 Template 里的控件才能把消息送出来。

直接事件模型

  • 事件的几个关键部分(以 Button 的点击事件为例,Button 的名字为 myButton):
    • 事件的拥有者:myButton。
    • 事件:myButton.Click
    • 事件的响应者:窗体本身
    • 事件处理器:this.myButton_Click 方法
    • 订阅关系:this.myButton.Click += new System.EventHandler(this.myButton_Click);
    • myButton_click 方法代码
      private void myButton_Click(object sender, EventArgs e)
      {
          if(sender is Button)
          {
              MessageBox.Show((sender as Button).Name);
          }
      }
      
  • 直接事件模型的弊端
    • 每对消息是“发送 -> 响应”关系,必须建立显示的点对点订阅关系。
    • 事件的宿主必须能够直接访问事件的响应者,不然无法建立订阅关系。

路由事件模型

  • 与 直接事件模型 区别:直接事件激发时,发送者直接将消息通过事件订阅关系传送给事件响应者,事件响应者进行响应。路由事件的拥有者和响应者没有直接显式的订阅关系,拥有者只负责激发事件,事件有谁响应他并不知道,他会沿着树进行传递,看谁进行侦听处理。

  • 一个例子:
    在这里插入图片描述
    在这里插入图片描述
    备注:

  • AddHandler 方法源自 UIElement 类,也就是说,所有 UI 控件都具有这个方法。

  • Button.ClickEvent 就像每个依赖属性都拥有自己的 CLR 属性包装一样,每个路由事件都拥有自己的 CLR 事件。

  • private void ButtonClicked(object sender, RoutedEventArgs e)中的 sender 实际上是 gridRoot 而不是被单击的 Button,这与传统的直接事件不太一样,如果想看事件的源头,可以(e.OriginalSource as FrameworkElement).Name;

  • 上面为元素添加路由事件也可以在 XAML 中写:

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

自定义路由事件

  • 一个例子
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 备注:
    • 创建自定义的路由事件大体可以分为下面三步:
      • 声明并注册路由事件。
      • 为路由事件添加 CLR 事件包装器。
      • 创建可以激发路由事件的方法。
    • 为路由事件添加 CLR 事件和依赖属性不一样,那个是 get 和 set,这个是 add 和 remove。
    • 激发路由事件用的是 RaiseEvent 方法,传统直接事件激发是通过 Invoke 方法。
    • 事件注册参数说明:
      • 第一个参数和 CLR 事件包装器的名称一致。
      • 第二个参数为路有时间的策略,有三种:**Bubble(冒泡式)**表示有里往外,**Tunnel(隧道式)**表示由外到里,和 Bubble 相反,**Direct(直达式)**模仿 CLR 直接事件,直接将事件传达事件处理器。
      • 第三参数为事件处理器类型。
      • 第四个有路由事件宿主类型。
    • 例子中有多处对路由事件进行了侦听。
    • 注意 ReportTimeEventArgs 继承对象,他的作用就是将参数进行包装,以防有多个参数不好传参。
    • 如何让一个路由事件在某个节点不在继续传递呢,使用e.Handled=true.在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42310008/article/details/112794894