• 解读WPF中事件


     

        WPF中的事件我们称之为路由事件。那么它和.NET的传统事件有什么区别了。我们首先来了解下路由事件。
       路由事件是可以在UI树形结构进行传递的。也就是说激发控件可以把这个事件消息传递到根节点上去。在树形节点
    上,只要监听器,都可以读到这个事件信息。这样可以看出,路由事件是一对多的。而传统的.NET事件却是单对单的。

       好了,废话不多说。我们先来看看路由事件是如何使用的。路由事件的注册和我们的依赖属性是差不多的。
       大致就是以下三个步骤:
      /*
             * 路由事件的定义
             * 1、声明路由事件
             *      路由事件是一个静态的,只读的,类型是RoutedEvent
             *      使用EventManger来进行注册
             *
             * 2、定义CLR包装器来进行事件的添加和删除
             *
             * 3、激发路由事件
             */

       我们现在来看几个例子:
       第一个例子路由事件的传递性:
       XAML:
       <Window x:Class="RouteEventWPF.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window1" Height="300" Width="300">
        <Grid x:Name="Grid1" ButtonBase.Click="MyBtn_Click">
            <Grid x:Name="Grid2" ButtonBase.Click="MyBtn_Click">
                <Grid x:Name="Grid3" ButtonBase.Click="MyBtn_Click">
                    <Button x:Name="MyBtn" Click="MyBtn_Click" Content="Test"></Button>
                </Grid>
            </Grid>
        </Grid>
       </Window>


       以上代码大家是不是看到一个ButtonBase.Click这个是说明该Grid监听了鼠标点击事件(下面将给出后台添加监听器)
       后台代码:

        /// <summary>
        /// Window1.xaml 的交互逻辑
        /// </summary>
        public partial class Window1 : Window{

        public Window1()
            {
                InitializeComponent();
            }

       private void MyBtn_Click(object sender, RoutedEventArgs e)
            {
                FrameworkElement element = sender as FrameworkElement;
                MessageBox.Show(element.Name);
            }

         }

    当我们点击了按钮会弹出四个框出来。分别是MyBtn,Grid3,Grid2,Grid1大家可以试试

        上面说了。这里在XAML文件中添加了监听器了。     现在我们在后台添加监听器:    

    // 第一个参数表示要监听什么样的事件。第二个参数表示处理程序是什么,这里是用到了委托    

    this.Grid3.AddHandler(Button.ClickEvent, new RoutedEventHandler(MyBtn_Click));

        到目前为止,我们都没有看到一开始所说的事件的注册。好了。现在我们自己定义一个路由事件。来看看
        路由事件的整体注册使用流程:

        

    第一步:定义路由事件类
        /// <summary>
        /// 自定义路由事件
        /// </summary>
        public class ReportTimeEventArgs : RoutedEventArgs
        {
            /// <summary>
            /// 调用父类的构造
            /// </summary>
            /// <param name="routedEvent"></param>
            /// <param name="sender"></param>
            public ReportTimeEventArgs(RoutedEvent routedEvent,Object sender):base(routedEvent,sender){}
    
            /// <summary>
            /// 定义自己的属性
            /// </summary>
            public DateTime ClickTime { get; set; }
        }
    
        第二步:在我们要用到的地方定义,我这里重写了Button类。
         /// <summary>
        /// 定义一个自己的Button
        /// 在里面注册自己定义的路由事件
        /// </summary>
        public class TimeButton : Button
        {
            /*
             * 路由事件的定义
             * 1、声明路由事件
             *      路由事件是一个静态的,只读的,类型是RoutedEvent
             *      使用EventManger来进行注册
             * 
             * 2、定义CLR包装器来进行事件的添加和删除
             * 
             * 3、激发路由事件
             */
    
            /// <summary>
            /// 路由事件的声明
            /// </summary>
            public static readonly RoutedEvent ReportTimeEvent =
                EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Tunnel,
                typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton));
    
            /// <summary>
            /// 事件添加和删除
            /// </summary>
            public event RoutedEventHandler ReportTime
            {
                add { this.AddHandler(ReportTimeEvent, value); }
                remove { this.RemoveHandler(ReportTimeEvent, value); }
            }
    
            /// <summary>
            /// Button的点击
            /// 触发路由事件
            /// </summary>
            protected override void OnClick()
            {
                base.OnClick();
                ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
                args.ClickTime = DateTime.Now;
                this.RaiseEvent(args);
            }
        }
    
        看到了吧这里就是我们定义路由事件的效果了啊。
    
        第三步:使用我们定义的路由
        <Window x:Class="RouteEventWPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:RouteEventWPF.Until"
            local:TimeButton.ReportTime="Window_ReportTime"
            Title="MainWindow" Height="350" Width="525" x:Name="RoutedTitle">
        <Grid x:Name="grid_1" local:TimeButton.ReportTime="Window_ReportTime">
            <Grid x:Name="grid_2" local:TimeButton.ReportTime="Window_ReportTime">
                <Grid x:Name="grid_3" local:TimeButton.ReportTime="Window_ReportTime">
                    <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="Window_ReportTime" >
                        <ListBox x:Name="lstBox" Height="230"></ListBox>
                        <local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="报时" local:TimeButton.ReportTime="Window_ReportTime"/>
                    </StackPanel>
                </Grid>
            </Grid>
        </Grid>
        </Window>
        
        大家要注意这里几个地方:
        xmlns:local="clr-namespace:RouteEventWPF.Until"这是应用我们刚才定义的几个类的命名空间
        后台代码很简单:
        请看代码如下:
        private void Window_ReportTime(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            string str = e.ClickTime.ToLongTimeString();
            string content = string.Format("{0}到达{1}", str, element.Name);
            this.lstBox.Items.Add(content);
    
            // 让路由事件停止,不再向上传递
            if (element.Name.Equals("grid_2")) {
            e.Handled = true;
            }
        }
    
        这段代码有部分要注意,如何让路由事件到了某个节点就不再继续传递了。这里给出了例子:
        e.Handled = true;把这个设置成True就可以了。注意了哦啊。
    
         以上就是路由事件的注册和使用方法。大家去敲一敲。看看效果。

         在这里再给大家补充一个东西就是附加路由。意思就是把一个路由事件附加到别的对象上。
         这里我们来看看:
         我定义了一个Student类

        

     public  class Student
        {
           /// <summary>
           /// 注册路由
           /// </summary>
           public static readonly RoutedEvent StudentEventArgs = 
               EventManager.RegisterRoutedEvent("StudentEvent", RoutingStrategy.Bubble,
               typeof(RoutedEventHandler), typeof(Student));
    
           public string ID { get; set; }
           public string Name { get; set; }
    
           /*
            * 够着CLR包装器
            */
    
           /// <summary>
           /// 添加路由事件
           /// </summary>
           /// <param name="d">依赖属性,给哪一个UI添加</param>
           /// <param name="h">路由委托</param>
           public static void AddNameChangedHandler(DependencyObject d, RoutedEventHandler h)
           {
               UIElement e = d as UIElement;
               if (e != null)
               {
                   e.AddHandler(Student.StudentEventArgs, h);
               }
           }
    
           /// <summary>
           /// 删除
           /// </summary>
           /// <param name="d"></param>
           /// <param name="h"></param>
           public static void RemoveNameChangedHandler(DependencyObject d,RoutedEventHandler h) 
           {
               UIElement e = d as UIElement;
               if (e != null)
               {
                   e.RemoveHandler(Student.StudentEventArgs, h);
               }
           }
        }
        我们来使用这个所谓的附加路由:
        <Window x:Class="RouteEventWPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:RouteEventWPF.Until"
            Title="MainWindow" Height="350" Width="525" x:Name="RoutedTitle">
        <Grid x:Name="grid_1">
            <Button Content="注册事件" x:Name="btnAttachEvent" Height="80" Click="btnAttachEvent_Click"></Button>
        </Grid>
      </Window>
    
      后台代码
    	public MainWindow()
    	{
    	    InitializeComponent();
    
    	   // this.grid_1.AddHandler(Student.StudentEventArgs, new RoutedEventHandler(Student_Hander));
    
    	    Student.AddNameChangedHandler(this.grid_1, new RoutedEventHandler(Student_Hander));
    	}
    
            private void Student_Hander(object sender,RoutedEventArgs e) 
            {
                MessageBox.Show((e.OriginalSource as Student).Name.ToString());
            }
    
    	private void btnAttachEvent_Click(object sender, RoutedEventArgs e)
            {
                Student stu = new Student() { ID = "10001", Name = "WangYu" };
                stu.Name = "Tom";
                RoutedEventArgs args = new RoutedEventArgs(Student.StudentEventArgs, stu);
                this.btnAttachEvent.RaiseEvent(args);
            }
    

      

       大家不知道有没有发现。这里的是通过Button把这个路由事件给抛出去的。而不是自己抛出去的。    所以称这样的事件为附加路由。    为什么他不能自己抛出去了啊。主要是因为我定义的这个类没有RaiseEvent方法。所以他没有办法抛出去。

       关于路由事件的具体就是这么多。    哦,漏和大家说了一定东西。就是路由传递形式。    路由的传递形式有三种。大家在注册路由的时候有没有发现这个参数    RoutingStrategy.Bubble 这里就说明了路由的传递方式。    RoutingStrategy这个枚举有三个。分别是Tunnel,Bubble,Direct    这三个什么意思。这里我不想说。大家试着去改改上面的代码,自己跑出看看。

       好了。各位今天的WPF路由事件就到这里。电话都给朋友打爆了。再不出门就要出事了。拜拜。周末最后一个下午。    大家好好玩哦啊。

  • 相关阅读:
    pod间访问
    Android地图下钻(坑我好久)
    学习进度23
    学习进度22
    人月神话读后感03
    团队介绍
    人月神话读后感02
    学习进度21
    学习进度20
    学习进度19
  • 原文地址:https://www.cnblogs.com/heardawn/p/2571792.html
Copyright © 2020-2023  润新知