日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

WPF路由事件中的三種策略介紹_實用技巧

作者:.NET開發菜鳥 ? 更新時間: 2022-04-28 編程語言

什么是路由事件

路由事件是具有更強傳播能力的事件,它可以在元素樹中向上冒泡和向下隧道傳播,并且能夠沿著傳播路徑被事件處理程序來處理。

路由事件允許事件在某個元素上被處理,即使這個事件源自于另外一個元素。事件路由允許某個元素的事件由另外一個元素引發。

路由事件是一種可以針對元素樹中的多個偵聽器而不是僅僅針對引發該事件的對象調用處理程序的事件。路由事件是一個CLR事件。

路由事件與一般事件的區別在于:路由事件是一種用于元素樹的事件,當路由事件觸發后,它可以向上或向下遍歷可視樹和邏輯樹,他用一種簡單而持久的方式在每個元素上觸發,而不需要任何定制的代碼(如果用傳統的方式實現一個操作,執行整個事件的調用則需要執行代碼將事件串聯起來)。

路由事件的路由策略:

所謂的路由策略就是指:路由事件實現遍歷元素的方式。

路由事件一般使用以下三種路由策略:

  • 冒泡路由事件:冒泡路由事件在包含層次中向上傳遞,即由事件源向上傳遞一直到根元素。
  • 直接路由事件:直接路由事件與普通的.NET事件是非常相似的,他們都起源于一個元素,并且不能夠傳遞給其它的元素。 只有事件源才有機會響應事件。
  • 隧道路由事件:從元素樹的根部調用事件處理程序并依次向下深入直到事件源。一般情況下,WPF提供的輸入事件都是以隧道/冒泡對實現的。隧道事件常常被稱為Preview事件。

1、冒泡路由事件

XAML代碼如下:


    
        
            
                
                
            
            
                
            
            
                
            
        
    

運行效果如下所示:

當單擊Left按鈕的時候,Button.Click事件被觸發,并且沿著ButtonLeft→CanvasLeft→GridA→GridRoot→Window這條路線向上傳遞,當單擊Right按鈕就會沿著ButtonRight→CanvasRight→GridA→GridRoot→Window這條路線向上傳遞,這里還沒有添加監聽器,所以是沒有反應的。

如何加入監聽器,我們可以再XAML中添加,XAML代碼如下:


    
        
            
                
                
            
            
                
            
            
                
            
        
    

我們在XAML代碼中添加了Button.Click="Button_Click"這個事件處理器,就是監聽器,并且事件處理交由Button_Click負責,后臺Button_Click代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfRouteEventByBubble
{
    /// 
    /// MainWindow.xaml 的交互邏輯
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("我到達了:" + (sender as FrameworkElement).Name);
        }
    }
}

我們分析一下,那兩個參數到底是什么呢?

  • 參數一:sender,這是聽者,就是監聽的地方,如果點擊了Left按鈕,那么Left按鈕就會大聲說:“我被點擊了”這個事件向上傳遞,知道到了設有監聽Button.Click事件的地方,這個地方就是sender。
  • 參數二:是RoutEventArgs類型的,這個參數攜帶了一些重要信息,例如事件是從哪里來的,上一個傳到哪里等,都可以利用這個參數來查詢。

運行效果如下:

我們會發現,當點擊button按鈕時,ButtonLeft、CanvasLeft、GridA、GridRoot中的事件都會觸發,這就是冒泡路由策略的功能所在,事件首先在源元素上觸發,然后從每一個元素向上沿著樹傳遞,直到到達根元素為止(或者直到處理程序把事件標記為已處理為止),從而調用這些元素中的路由事件。

如果把Button_Click事件修改為:

 private void Button_Click(object sender, RoutedEventArgs e)
 {
        MessageBox.Show("我到達了:" + (sender as FrameworkElement).Name);
        e.Handled = true;//讓事件停止冒泡
}

則以上事件就不會沿著ButtonLeft→CanvasLeft→GridA→GridRoot→Window這條路線傳遞下去,只會執行ButtonLeft的事件。

MouseUp就是一個冒泡路由事件,看下面的XAML代碼:


    
        
            
            
            
            
        

        

        
        Handle first event
        
    

當我們點擊Image的時候,發發生冒泡路由事件,會一層層的向外傳遞,傳遞順序:Image->StackPanel->Label->Grid->Window

后端代碼如下:

using System.Windows;

namespace 路由事件
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        protected int eventCounter = 0;
        private void SomethingClicked(object sender, RoutedEventArgs e)
        {
            eventCounter++;
            string message = $"#{eventCounter.ToString()}:\r\n" +
                $"Sender:{sender.ToString()}\r\n" +
                $"Source:{e.Source}\r\n" +
                $"Original Source:{e.OriginalSource}";
            lstMessage.Items.Add(message);
            // Handled允許終止事件的冒泡或者終止隧道過程
            // 設置Handled=True,事件就不會繼續傳遞了
            e.Handled = (bool)chkHandle.IsChecked;
        }

        private void cmdClear_click(object sender, RoutedEventArgs e)
        {
            eventCounter = 0;
            lstMessage.Items.Clear();
        }
    }
}

運行程序,輸出結果如下:

可以看到:輸出結果就是按照我們上面的順序輸出的。我們把Grid的MouseUp事件去掉,在看輸出結果:

這時就沒有Grid控件了。勾選下面的復選框,在執行結果:

這時只有Image控件被觸發了,其它控件的MouseUp被終止了。

2、隧道路由事件

隧道路由事件跟冒泡路由事件一樣,都是在包含層次中,但是隧道路由事件的傳遞方向跟冒泡路由事件正好相反:隧道路由事件首先是從根元素上被觸發,然后從每一個元素向下沿著樹傳遞,直到到達根元素為止(或者直到到達處理程序把事件標記為已處理為止)。

冒泡路由事件是向上傳遞,隧道路由事件是向下傳遞。

隧道路由事件在事件到達恰當的控件之前,為預覽事件提供了機會。首先是在窗口級別上,也就是頂級,然后是更具體的容器,最后直到到達按下鍵時具有焦點的元素。我們很容易就可以識別隧道事件,因為他們都是以單詞Preview開頭的。WPF通常可以成對的定義冒泡路由事件和隧道路由事件。如果我們找到一個MouseUp的冒泡路由事件,還可以找到一個PreviewMouseUp的隧道路由事件。隧道路由事件總是在冒泡路由事件之前被觸發。

XAML代碼如下;


    
        
    

后臺代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Wpf路由事件管道策略
{
    /// 
    /// MainWindow.xaml 的交互邏輯
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("windows被點擊");
        }

        private void grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("grid被點擊");
        }

        private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("button被點擊");
        }
    }
}

程序運行效果:

特別值得注意的是:管道事件按照慣例,他們的名字中都有一個preview前綴,一般來說管道事件都有他的配對的冒泡事件,例如:PreviewMouseDown和MouseDown就是配對事件,如果同時存在的話,那么就會先執行管道事件然后才執行配對的冒泡事件。當然e.Handled=true,依然能夠阻斷事件。

看下面的示例代碼:


    
        
            
            
            
            
        

        

        
        Handle first event
        
    

后端事件代碼:

using System.Windows;

namespace 路由事件
{
    /// 
    /// Window1.xaml 的交互邏輯
    /// 
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        protected int eventCounter = 0;

        private void SomeKeyPressed(object sender, RoutedEventArgs e)
        {
            eventCounter++;
            string message = $"#{eventCounter.ToString()}:\r\n" +
                $"Sender:{sender.ToString()}\r\n" +
                $"Source:{e.Source}\r\n" +
                $"Original Source:{e.OriginalSource}" +
                $"Event:{e.RoutedEvent}";
            lstMessage.Items.Add(message);
            // Handled允許終止事件的冒泡或者終止隧道過程
            // 設置Handled=True,事件就不會繼續傳遞了
            e.Handled = (bool)chkHandle.IsChecked;
        }

        private void cmdClear_click(object sender, RoutedEventArgs e)
        {
            eventCounter = 0;
            lstMessage.Items.Clear();
        }
    }
}

運行結果:

可以看到:執行順序是從頂級元素到最里層的元素。

注意:如果要使用冒泡路由事件,只需要將PreviewKeyDown改為KeyDown即可。

3、直接策略

事件僅僅在源元素上觸發,這個與普通的.Net事件的行為相同,不同的是這樣的事件仍然會參與一些路由事件的特定機制,如事件觸發器等。

該事件唯一可能的處理程序是與其掛接的委托。?

路由事件的事件處理程序的簽名(即方法的參數):

他與通用的.net事件處理程序的模式一致,也有兩個參數:第一個為:System.Object對象,名為sender,第二個參數(一般名為e)是一個派生于System.EventArgs的類。sender參數就是該處理程序被添加的元素,參數e是RoutedEventArgs的一個實例提供了4個有用的屬性:

  • Source---邏輯樹中開始觸發該事件的的元素。
  • originalSource--可視樹中一開始觸發該事件的元素。
  • handled---布爾值,設置為true表示事件已處理,在這里停止。
  • RoutedEvent---真正的路由事件對象,(如Button.ClickEvent)當一個事件處理程序同時用于多個路由事件時,它可以有效地識別被出發的事件。

原文鏈接:https://www.cnblogs.com/dotnet261010/p/6368791.html

欄目分類
最近更新