WPF自定義路由事件(二)


WPF中的路由事件 as U know,和以前Windows消息事件區別不再多講,這篇博文中,將首先回顧下WPF內置的路由事件的用法,然后在此基礎上自定義一個路由事件。

1.WPF內置路由事件  

WPF中的大多數事件都是路由事件,WPF有3中路由策略:

具體不多講,單需要注意的是WPF路由事件是沿着VIsualTree傳遞的。VisualTree與LogicalTree的區別在於:LogicalTree的葉子節點是構成用戶界面的控件(xaml緊密相關),而VisualTree要連控件中的細微結構也算上。VisualTree是LogicalTree的擴展。

reference: Understanding the Visual Tree and Logical Tree in WPF

下面給出一個使用WPF內置路由事件的例子:

復制代碼
<Window x:Class="WPFRoutedEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" > <Grid x:Name="Grid1" Margin="10" Background="AliceBlue" MouseLeftButtonDown="Grid1_MouseLeftButtonDown"> <StackPanel Background="BurlyWood" Height="200" x:Name="StackPanel1" Button.Click="ButtonInStackPanel_Click" MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown"> <Button x:Name="Button1" Content="RoutedEvent" Click="Button1_Click" /> </StackPanel> </Grid> </Window>
復制代碼
復制代碼
using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace WPFRoutedEvent { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //Grid訂閱Button的Click事件 Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click)); } private void Button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Button Clicked."); // //e.Handled = true;  } private void ButtonInStackPanel_Click(object sender, RoutedEventArgs e) { MessageBox.Show("StackPanel Clicked."); } private void ButtonInGrid_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Grid Clicked."); } private void Grid1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("Grid Mouse Left button down."); } private void StackPanel1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("StackPanel Mouse Left button down."); } } }
復制代碼

Button的Click事件是一個路由事件,分別在StackPanel中和Grid中訂閱這個事件並進行相應的處理,分別用xaml代碼和C#代碼如下:

Click="Button1_Click"
Button.Click="ButtonInStackPanel_Click"
Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click));

StackPanel的MouseLeftButtonDown也是一個路由事件,也可以叫“附加事件”。其實“附加事件”也是路由事件,只是個文字游戲,為什么還要另外起個名字呢?原來路由事件的宿主都是那些擁有可視化實體的界面元素;而附加事件則不具備顯示在用戶界面上的能力。

常見的附加事件有:

Binding類:SourceUpdated事件、TargetUpdated事件。

Mouse類:MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等。

Keyboard類:KeyDown事件、KeyUp事件等。

Grid和StackPanel中均如下訂閱:

MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown"

程序運行如下:

 2.自定義路由事件    

 前面DebugLZQ寫過一篇博文,內容是關於自定義CLR事件的,參考:.NET自定義事件小結。下面來自定義一個WPF路由事件,各位博友可以比較下兩者的異同。

創建自定義路由事件大體可以分為三個步驟:

(1)聲明並注冊路由事件

(2)為路由事件添加CLR事件包裝

(3)創建可以激發路由事件的方法

下面我們自定義一個WPF路由事件,我們給事件攜帶個參數,為此需要創建一個RoutedEventArgs類的派生類。如下:

復制代碼
using System; using System.Windows; namespace MyRoutedEvent { //事件參數 class ReportTimeRoutedEventArgs:RoutedEventArgs { public ReportTimeRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { } public DateTime ClickTime { get; set; } } }
復制代碼

然后,創建一個Button類的派生類並按前面的步驟為其添加路由事件:

復制代碼
using System; using System.Windows.Controls; using System.Windows; namespace MyRoutedEvent { class TimeButton:Button { //聲明和注冊路由事件\ public static readonly RoutedEvent ReportTimeRoutedEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeRoutedEventArgs>), typeof(TimeButton)); //CLR事件包裝 public event RoutedEventHandler ReportTime { add { this.AddHandler(ReportTimeRoutedEvent, value); } remove { this.RemoveHandler(ReportTimeRoutedEvent, value); } } //激發路由事件,借用Click事件的激發方法 protected override void OnClick() { base.OnClick();//保證Button原有功能正常使用,Click事件被激發  ReportTimeRoutedEventArgs args = new ReportTimeRoutedEventArgs(ReportTimeRoutedEvent, this); args.ClickTime = DateTime.Now; this.RaiseEvent(args);//UIElement及其派生類  } } }
復制代碼

下面是程序界面的XAML代碼,看下如何消費這個路由事件:

復制代碼
<Window x:Class="MyRoutedEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MyRoutedEvent" Title="MainWindow" Height="350" Width="525"> <Grid x:Name="grid1" local:TimeButton.ReportTime="TimeButton_ReportTime"><!----> <Grid x:Name="grid2"> <Grid x:Name="grid3"> <StackPanel x:Name="stackPanel1"> <ListBox x:Name="listBox1"/> <local:TimeButton Width="200" Height="200" Background="Aquamarine" ReportTime="TimeButton_ReportTime" /><!----> </StackPanel> </Grid> </Grid> </Grid> </Window>
復制代碼

事件處理的后台代碼如下:

復制代碼
using System.Windows; namespace MyRoutedEvent { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void TimeButton_ReportTime(object sender, ReportTimeRoutedEventArgs e)//注意參數  { listBox1.Items.Add(e.ClickTime.ToLongTimeString()+"DebugLZQ"); }
} }
復制代碼

假如我如果想在后台代碼中消費定義的路由事件,該如何做呢?

  /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.grid1.AddHandler(TimeButton.OnReportTimeRoutedEvent, new RoutedEventHandler(MainWindow_MEvent));
            this.grid1.AddHandler(TimeButton.OnReportTimeRoutedEvent, new RoutedEventHandler(TimeButton_ReportTime1));//監聽OnReportTimeRoutedEvent的路由事件
            
        }

        private void TimeButton_ReportTime(object sender, ReportTimeRoutedEventArgs e)
        {
            listBox1.Items.Add(e.ClickTime.ToLongTimeString() + "DebugLZQ");

        }
        private void TimeButton_ReportTime1(object sender, RoutedEventArgs e)
        {
            ReportTimeRoutedEventArgs ss = e as ReportTimeRoutedEventArgs;//這里使用了一個轉換,父類轉換程子類的引用,只有轉換了,才能被AddHandler調用。

            listBox1.Items.Add(ss.ClickTime.ToLongTimeString() + "DebugLZQ");

        }
        void MainWindow_MEvent(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("dddd");
        }
    }

 

程序運行效果如下:

小結:UIElement類是路由事件和附加事件的分水嶺,因為從UIElement類開始才具備了再界面上顯示的能力,也因為RaiseEvent、AddHandler和RemoveHandler這些方法也定義在UIElement類中。附加事件也只能算是路由事件的一種用法而不是一個新的概念,其本質還是路由事件。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM