解读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路由事件就到这里。电话都给朋友打爆了。再不出门就要出事了。拜拜。周末最后一个下午。    大家好好玩哦啊。

posted @ 2012-07-01 12:39  HeartDawn  阅读(1445)  评论(0编辑  收藏  举报