• WPF 学习笔记 路由事件


     

    1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向传递且路过多个中转结点,并让这个路由过程被恰当的处理。

    2,WPF有两种树,Logical Tree和Visual tree。 LogicTree上,充当叶子的一般都是控件,如果我们把WPF的控件也放在“放大镜下观察”,你会发现每个WPF空间本身也是一棵更细微级别的组件组成的树。用来观察WPF控件的放大镜是我们提到的Blend。如果把Logical Tree衍生至Template组件级别,我们的到的就是Visual Tree。

    3,路由事件是沿着Visual Tree传递的。

    4.一个事件包括5方面: 

    1. 事件的拥有者
    2. 事件
    3. 事件的响应者
    4. 事件处理器
    5. 订阅关系

    5. 路由事件的原理: 舍弃直接事件响应者,让不同的控件变成监听者。 而事件拥有者,只负责出发事件。

    6,使用WPF内置的路由事件:

    Wpf系统的大多数事件都是可路由事件。我们以Button的Click事件来说明路由事件的使用。

    1,如何添加监听者:

        this.gridRoot.AddHandler(Button.ClickEvent, new RoutedEventHandler(DefineMethod));

    上面这个AddHandler方法来自URElement类。 第一个参数是Button.ClickEvent, 这个叫做路由事件,这里也使用了类似依赖属性包装器的方式。

        这里要注意的是,路由事件方法中RoutedEventHandler.Source 是gridRoot. 如果想要得到Button的话,使用RoutedEventHandler.OrigenalSource.

       可以在Xaml中使用简化的方法:  <Grid X:Name= “gridRoot”  BackGround=”Lime” Button.Click=”ButtonClicked”>

    7,自定义路由事件

    三个步骤:

    1. 声明并注册路由事件
    2. 为路由事件添加CLR事件包装
    3. 创建可以激发路由事件的方法

    看Code:

    public abstract class ButtonBase: ContentControl, ICommandSource
    {
        public static readonly RoutedEvent ClickEvent =
        /*注册路由事件*/
       EventManager.RegisterRoutedEvent("Click",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));
        
        public event RoutedEventHandler Click
        {
              add {this.AddHandler(ClickEvent,value)}
              Remove {this.RemoveHandler(ClickEvent, value)}
         }
    
        protected virtual void OnClick()
       {
           RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent,this);
          this.RaiseEVent(newEvent);
       }
    }

    路由策略:

    1. Bubble  向上
    2. Tunnel 向下
    3. Direct  模仿CLR直接事件

    Demo 2: 实现一个继承自Button的TimeButton类,并添加路由事件

    //创建一个RoutedEventArgs类:
       class ReportTimeEventArgs:RoutedEventArgs
       {
             Public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
             :Base(routedEvent,source) {}
    
              Public DateTime ClickTime {get;set;}
        }
    
    class TimeButton : Button
    {
         //生命和注册路由事件
          public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent
               ("ReportTime",RoutingStrategy.Bubble,typeof(EventHandler<ReportTimeEventArgs>),typeof(TimeButton));
           
          //CLR 事件包装器
           public event RoutedEventhandler ReportTime
           {
               add {this.AddHandler(ReportTimeEvent , value) ;}
                remove {this.RemoveHandler(ReportTimeEvent,value); 
           }
    
    //激发路由事件,借用Click事件的激发方法
           protected override void OnClick()
           {
             base.OnClick();
             ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent, this);
             args.ClickTime = DateTime.Now;
             this.RaiseEvent(args);
           }
     //看看怎么使用吧
           local:TimeButton.ReportTime = “ReportTimeHandler”;
               //ReportTimeEvent 路由事件处理器
               private void ReportTimeHandler(objecet sender, ReportTimeEventArgs e)
               {
                    FrameworkElement element  = sender as FrameworkElement;
                    string timeStr  = e.ClickTime.ToLongTimeString();
                    string content = string.Format(“{0}  到达 {1}”,timeStr,element.Name);
                    this.listBox.Items.Add(content);
               }
    8, 如何让路由事件停止传播:
          使用e.handled 如果e.handled==true,则停止传递。
    9. RoutedEventArgs的Source和OriginalSource
    Source是logicTree的消息源头,而OriginalSource是visualTree上的源头。
    如果一个Button是在UserControl中,那么Source应该是UserControl , OriginalSource 应该是 Button/
    10. 事件也附加----深入浅出附加事件
    那些类拥有附加事件
    • Binding类: SourceUpdated事件、 TargetUpdated事件
    • Mouse类: MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等
    • Keyboard类: KeyDown事件、KeyUp事件
    对比路由事件会发现: 附加事件不具备显示在用户界面上的能力。
    看一个Demo:设计一个Student类,如果Student实例的Name属性值发生了变化就激发一个路由事件,然后使用界面元素来捕捉这个事件。
    public class Student
    {
        public static readonly RoutedEvent NameChangedEvent = EventManager.ResisterRoutedEvent
          ( "NameChanged",routingStrategy.Bubble,typeof(RoutedEventHandler),typeof(Student));
    
         public int Id {get;set;}
         public string Name {get;set;}
    }
    然后我们设计一个button. 
    <Button  x:Name = "button1" Content="OK"  Width="80" Height="80"   Click="Button_Click">
     看看后台代码 
       //添加事件监听器
      this.gridMain.AddHandler(Student.nameChangedEvent, new RoutedEventHandler(this.DtudentnameChangedHandler))
       //Click 事件处理器
       private void Button_Click(objcet sender, RoutedEventArgs e)
    {
           Student stu = new Student() {Id=10,Name="Tim"};
            stu.Name="Tom";
            //准备事件消息并发送路由事件
            //附加事件宿主是没有办法发送路由事件的,要借助一个FrameworkElement来RaiseEvent(arg)
            //其中RoutedEventArgs 有两个参数,一个附加事件,一个是实例。
           RoutedEventArgs arg = new RoutedEventArgs(Student.NameChangedEvent,stu);
           this.button1.RaiseEvent(arg);
    }
    
    //Grid 捕捉到nameChangedEvent后的处理器
    private void StudentNameChangedHandler(object sender, RoutedEventArgs e)
    {
          MessageBox.Show((e.OriginalSource as Student).Id.ToString());
    }
    11. 事件也附加2
    其实上面那个例子已经是一个附加文件了,但是微软的官方文档约定要为这个附加事件添加一个CLR包装以便XAML编辑器识别并进行只能提示。 但是,因为Student类不是UIElement的派生类,因为不具备Addhandler和 RemoveHandler这两个方法,所以不能使用CLR属性作为包装器:
    • 为目标UI元素附加事件侦听器的包装器是一个名为Add*Handler的public static 方法,星号代表事件名称
    public class Student
    {
         //声明并定义路由事件
        public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRouredEvent
        ("NameChanged",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typdof(Student));
    
         //为界面元素添加路由事件侦听
          public static void AddNameChangedHandler(DependencyObject d,RoutedEventHandler h)
          {
                 UIElement e = d as UIElement;
                 if(e!=null)
                 {
                       e.AddHandler(Student.NameChangedEvent, h);  
                  }          
           }
       
         public static voidRemoveNameChangedHandler(DependencyObject d,RoutedEventHandler h)
          {
                 UIElement e = d as UIElement;
                 if(e!=null)
                 {
                       e.AddHandler(Student.NameChangedEvent, h);  
                  }          
           }
      
    
         public int Id {get;set;}
         public string Name {get;set;}
    }

    public Window1()

    {

         Student.AddNameChangedHandler(this.gridMain, new RoutedEventHandler(this.StudnetnameChagnedHandler));

    }

    再次理解一下附加事件:

    UIElement类是路由事件宿主与附加事件宿主的分水岭,不但是因为从UIElemtn类开始才具备了界面上显示的能力,还因为RaiseEvent、AddHandler和RemoveHandler 这些方法也定义在UIElement类中。 如果在一个非UIElement派生类中注册了路由事件,则这个类的实例既不能自己激发,也无法自己侦听此路由事件。

    image

    转载:http://www.cnblogs.com/zhaoyun2007/archive/2012/12/06/2804581.html

  • 相关阅读:
    shutil文件去重模块
    Nexus构建npm、yum、maven私有仓库
    centos7添加自定义服务到systemctl
    Sonatype nuxus私有仓库介绍
    rancher单节点备份和恢复
    rancher证书过期X509:certificate has expired or is not ye valid
    清理docker日志
    mysql 9 hash索引和B+tree索引的区别
    mysql 8 索引
    mysql 7 慢查询+慢查询工具
  • 原文地址:https://www.cnblogs.com/qq247039968/p/4065096.html
Copyright © 2020-2023  润新知