1. 程式人生 > 其它 >深入理解WPF路由事件

深入理解WPF路由事件

 WPF中有兩種事件模型:一種是在WinForm時代就存在的CLR事件;另一種是WPF時代的路由事件;

一、CLR事件定義與使用
  	//定義一個委託
    public delegate void ClickHandler(String Name);
     public class Test
    {
        /// 定義事件
        public event ClickHandler ClickEvent;
        /// <summary>
        /// 註冊事件,事件繫結方法
        /// </summary>
        /// <param name="handler"></param>
        public void RegisterClickEventHandler(ClickHandler handler)
        {
            ClickEvent += handler;
        }
        /// 登出事件
        public void DeRegisterClickEventHandler(ClickHandler handler)
        {
            ClickEvent -= handler;
        }
        /// <summary>
        /// 觸發事件
        /// </summary>
        /// <param name="UserName"></param>
        public void RaiseClickEvent(string UserName)
        {
            ClickEvent.BeginInvoke(UserName,null, null);
        }
    }

    public class TestEvent
    {

        public TestEvent()
        {
            Test test = new Test();
            test.RegisterClickEventHandler(On_Click);
            test.RegisterClickEventHandler(On_Click2);
            test.RaiseClickEvent("哈哈哈");
        }

        public void On_Click(String name)
        {
            Console.WriteLine(name);
        }
        public void On_Click2(String name)
        {
            Console.WriteLine(name+name);
        }
    }

CLR事件特點
1、事件發起者與接收者是緊密關聯,不容易解耦
2、如果利用事件傳遞訊息,必須是逐級傳遞,不比繁瑣,也不容易管理;

二、路由事件

    public delegate void ClickHandler(String Name);
     public class Test:UIElement
    {
        public event ClickHandler ClickEvent;
        public void RegisterClickEventHandler(ClickHandler handler)
        {
            ClickEvent += handler;
        }
        public void DeRegisterClickEventHandler(ClickHandler handler)
        {
            ClickEvent -= handler;
        }
        /// <summary>
        /// 定義路由事件
        /// </summary>
        public static readonly RoutedEvent Click2Event = EventManager.RegisterRoutedEvent(
                "Click2", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Test));
        /// <summary>
        /// 註冊路由事件,繫結方法
        /// </summary>
        /// <param name="handler"></param>
        public void AddClick2EventHandler(RoutedEventHandler handler)
        {
            this.AddHandler(Click2Event, handler);
        }
        /// <summary>
        /// 登出路由事件
        /// </summary>
        /// <param name="handler"></param>
        public void RemoveClick2EventHandler(RoutedEventHandler handler)
        {
            this.RemoveHandler(Click2Event, handler);
        }
        public void RaiseClickEvent(string UserName)
        {
            //觸發事件
            ClickEvent.BeginInvoke(UserName,null, null);
        }
       /// <summary>
       /// 觸發路由事件
       /// </summary>
       /// <param name="UserName"></param>
        public void RaiseClick2Event(string UserName)
        {
            RoutedEventArgs args = new RoutedEventArgs(Click2Event, UserName);
            //觸發事件
            this.RaiseEvent(args);
        }
    }
    public class TestEvent:UIElement
    {
        Test test = new Test();
        public TestEvent()
        {
            test.RegisterClickEventHandler(On_Click);
            this.AddHandler(Test.Click2Event, new RoutedEventHandler(On_Click2));
        }
        public void RaiseClick()
        {
            test.RaiseClick2Event("哈哈哈");
        }
        public void On_Click(String name)
        {
            Console.WriteLine(name);
        }
        public void On_Click2(object sender, RoutedEventArgs e)
        {
        }
    }

為了方便XAML中進行事件繫結,我們修改下事件繫結的新增和刪除,將兩個方法合併為一個

        public event RoutedEventHandler Click2
        {
            add { this.AddHandler(Click2Event, value); }
            remove { this.RemoveHandler(Click2Event, value); }   
        }

路由事件的特點:
 從路由事件的使用方式上看,路由事偵探同依賴屬性性一樣,也是由WPF中的基礎型別(路由事件是:EventManager;依賴屬性是DependencyProperty)統一管理,從而實現路由事件共享;

  • 1、定義事件
    宣告為靜態只讀型別,以Event作為名稱字尾; 並且是通過EventManager註冊;
  • 2、繫結方法
    呼叫UIElement中的AddHandler; 相應的解綁,採用RemoveHanlder
  • 3、觸發事件
    呼叫UIElement中的RaiseEvent方法觸發;
  • 4、通過RoutedEventArgs引數,確定事件及引數。
三、路由策略

 由於採用的是統一管理、統一繫結方法, 統一觸發方式,所以路由事件的管理更加方便,事件傳遞也更加靈活;
 路由事件,之所以叫“路由”,最主要在於其事件傳遞的特殊性;在註冊事件時,通過RoutingStrategy列舉,可以使用三種方式傳遞事件

  • RoutingStrategy.Direct
    與普通的.NET事件類似,只傳遞一層。它源自一個元素,並且不傳遞給其他元素。例如,MouseEnter事件(當滑鼠移動到一個元素上面時觸發)就是一個直接路由事件
  • RoutingStrategy.Bubble
    沿UI可視樹往上傳遞。例如,MouseDown事件就是一個冒泡路由事件。它首先被單擊的元素觸發,接下來就是該元素的父元素觸發,依此類推,直到WPF到達元素樹的頂部為止
  • RoutingStrategy.Tunnel
    沿UI可視樹往下傳遞,例如PreviewKeyDown就是一個隧道路由事件。在一個視窗上按下某個鍵,首先是視窗,然後是更具體的容器,直到到達按下鍵時具有焦點的元素
四、關鍵事件傳遞的中斷

 《深入淺出WPF》,以及《WPF高階程式設計》中都講到當RoutedEventArgs 中Handler屬性設定 true的時候,路由事件不再往下傳遞;其實設定Handled=true並不是終止事件的傳閱,這只是為事件做一個標記而已,從業務上說明,無需再做其它處理;但是,事件還是會繼續往下傳遞,只是處理方法是否會執行,得看我們呼叫UIElement.AddHandler(RoutedEvent, Delegate, Boolean)最後一個引數如何設定;
 MSDN解釋如下:
handledEventsToo Boolean
 如果為 true,則將按以下方式註冊處理程式:即使路由事件在其事件資料中標記為已處理,也會呼叫處理程式;如果為 false,則使用預設條件註冊處理程式,即當路由事件被標記為已處理時,將不呼叫處理程式。