主頁 > .NET開發 > WPF教程六:理解WPF中的隧道路由和冒泡路由事件

WPF教程六:理解WPF中的隧道路由和冒泡路由事件

2021-04-01 15:31:44 .NET開發

  WPF中使用路由事件升級了傳統應用開發中的事件,在WPF中使用路由事件能更好的處理事件相關的邏輯,我們從這篇開始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由,

事件最基本的用法

  在基于事件驅動的開發中,把代碼放在回應注冊的事件的處理函式內,比如Click事件、MouseDown事件、MouseUp事件等等,每個控制元件回應自己的注冊事件,有很多如果在事件上有相互關聯和影響的事件,就要在一個業務邏輯里寫比較多的代碼,而路由事件主要的優勢就是路由事件可以在元素樹上進行傳遞,并且沿著元素樹的傳播途徑被事件處理程式處理,這樣我們寫代碼的程序中時就可以更好的組織代碼到合適的位置,

   WPF事件模型和WPF屬性模型非常類似,與依賴項屬性一樣,路由事件由只讀的靜態欄位表示,在靜態建構式中注冊,并通過標準的.NET事件定義進行封裝,這里我們只講如何更好的使用,原理部分請看原始碼,比如ButtonBase提供的Click事件,

     <Button Content="事件處理程式" Click="Button_Click"/>
     private void Button_Click(object sender, RoutedEventArgs e)
        {
            //這是Click事件處理程式代碼部分,
        }

  在注冊事件后,在事件處理程式中第一個引數 sender提供引發該事件的物件,第二個引數是EventArgs物件,在WPF中如果事件不需要傳遞額外的資訊,可以使用RoutedEventArgs類,如果需要傳遞額外的資訊,就要是有繼承自RoutedEventArgs的物件,比如處理inkcanvas墨跡繪制的,比如處理多點觸控的,這些都是變相繼承RoutedEventArgs類,里面會包含在這種場景下更加多的資訊, 

 

 注冊事件的幾種寫法:

1)在XAML代碼中<Button x:Name="EventMessageButton" Content="事件處理程式" MouseUp="EventMessageButton_MouseUp"/>
2)在cs代碼中 EventMessageButton.MouseUp += EventMessageButton_MouseUp;
3)在cs代碼中 EventMessageButton.MouseUp += new MouseButtonEventHandler(EventMessageButton_MouseUp);

private void EventMessageButton_MouseUp(object sender, MouseButtonEventArgs e)
{
  //我是處理程式,
}

 

 

 第一種寫法:我們使用XAML檔案中在Button元素內使用MouseUp來創建后臺事件處理代碼 Btn_eventMessge_MouseUp

 第二種寫法:我們在后臺代碼中使用MouseUp+=的方式注冊,一種是New MouseButtonEventHandler傳入方法名,一種是匿名的直接傳入方法名,這三種注冊方式達成的效果是一樣的,

而這三種實際上使用的是事件封裝器,另一種方式是通過使用UIElement.AddHandler來直接連接事件,這里看個人習慣把,但是各種寫法主要解決的問題還是解耦,因為這些會關聯到后面的命令,影片,模板,觸發器,MVVM下的使用,等等,這是個比較長久的問題,所以在這里,能夠使用,看得明白,目前這個階段就可以了,

我們繼續往下,解除關聯

在注冊事件的時候,最好先使用-=來解除關聯,避免多次觸發不合符預期的監聽事件,斷開使用-=或者使用UIElement.RemoveHandler來解除關聯,  因為事件在多次+=注冊事件處理程式是可行的,而事件的多詞解除關系不會引發任何問題,因此不要擔心+=和-=不匹配的問題,

      public MainWindow()
        {
            InitializeComponent();

         EventMessageButton.MouseUp -= EventMessageButton_MouseUp;
         EventMessageButton.MouseUp += EventMessageButton_MouseUp;


        }

理解路由事件

我們知道了事件可以在元素上注冊事件處理程式,那么我們知道內容控制元件是可以相互嵌套各種奇奇怪怪的組合以達到自己想要的效果,在這種情況下我們假設一個比較常見的場景,我們有一個標簽,標簽中包含一個StackPanel面板,面板中包含一幅圖片和2個文本,

  

  <Label BorderBrush="Black" BorderThickness="1">
            <StackPanel>
                <TextBlock Margin="3">
                  我是圖片標題
                </TextBlock>
                <Image Source="1.png" Stretch="None"/>
                <TextBlock Margin="3">
                    我是圖片正文
                </TextBlock>
            </StackPanel>
  </Label>

我們的控制元件來回嵌套內容結構很復雜了,但是我們想在用戶點擊時只在一個地方回應我們的代碼,如果為每個元素都關聯同一個事件處理程式,代碼會很亂,而且難以維護,而路由事件就是為了解決這個問題的,路由事件分為三種:

1)和普通的.NET事件類似,直接路由事件(direct event) 他們源于一個元素,不傳遞給其他元素,比如MouseEnter事件,是直接路由事件,

2)向上傳遞的冒泡路由事件(bubbling event)比如MouseDown事件,是冒泡路由事件,該事件先由被單擊的元素引發,接下來被該元素的父元素引發,然后被父元素的父元素引發,以此類推,直到元素樹的頂部,

3)向下傳遞的隧道路由事件(tunneling event) 比如PreviewKeyDown事件,隧道路由事件在事件到達恰當的空間之前為預覽事件和終止事件提供了機會,比如PreviewKeyDown事件可以截獲是否按下了某個鍵,首先在視窗級別上,然后是更具體的容器,直到當按下時具有焦點的元素,

當使用EventManager.RegisterEvent()發給發注冊路由事件時,需要傳遞一個RoutingStrategy列舉,指示希望用于事件的事件行為,

MouseUP和MouseDown事件都是冒泡路由事件,因此當在上面的圖片中按下滑鼠左鍵后順序觸發MouseDown事件的順序是冒泡的,我們使用Snoop軟體抓取一下程序:

 

從圖中我們看到首先觸發的是PreviewMouseDown的隧道路由,他可以讓我們有機會預覽事件或終止事件,我們看到了從MainWindow開始到最終的Image結束,我們沒有終止路由,所以進行了下一輪的冒泡路由,從Image開始到MainWindow,

整個流程就結束了,我們看到路由事件提供了對事件處理非常豐富的功能,具體的隧道或冒泡行為可以參考RoutedEventArgs中的內容,

Source 屬性是引發事件的的物件, OriginalSource是最初是什么物件引發了事件,RoutedEvent為觸發的事件提供的RoutedEvent物件,里面是需要用到的當前的引數,比如滑鼠坐標,touch等等,Handled屬性的作用是終止事件是否繼續傳遞,

我們現在開始在這個例子上添加代碼,用于演示我們怎么處理冒泡路由,

<Window x:Class="WPFEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFEvent"
        mc:Ignorable="d" MouseUp="EventResponseProcess_MouseUp"
        Title="MainWindow" Height="450" Width="800">
    <Grid Margin="3" MouseUp="EventResponseProcess_MouseUp">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Label Margin="5" Grid.Row="0" HorizontalAlignment="Left" Background="AliceBlue" BorderBrush="Black" BorderThickness="1" MouseUp="EventResponseProcess_MouseUp">
            <StackPanel MouseUp="EventResponseProcess_MouseUp">
                <TextBlock Margin="3" MouseUp="SomethingClicked">
                  我是圖片標題
                </TextBlock>
                <Image Source="1.png" Stretch="None" MouseUp="EventResponseProcess_MouseUp"/>
                <TextBlock Margin="3">
                    我是圖片正文
                </TextBlock>
            </StackPanel>
        </Label>
        <ListBox Grid.Row="1" Margin="5" Name="MessageListBox"></ListBox>
        <CheckBox Grid.Row="2" Margin="5" Name="HandlerCheckBox">
            Handle first event
        </CheckBox>
        <Button Grid.Row="3" Margin="5" Padding="3" HorizontalAlignment="Right" Name="ClearButton" Click="ClearButton_Click">Clear List</Button>
    </Grid>
</Window>
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 WPFEvent
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        protected int eventCounter = 0;
        private void EventResponseProcess_MouseUp(object sender, MouseButtonEventArgs e)
        {
            eventCounter++;
            string message = $"#{ eventCounter}:\r\n Sender: {sender} \r\n Source: {e.Source} \r\n Original Source: {e.OriginalSource}"; 
            MessageListBox.Items.Add(message);
            e.Handled = (bool)HandleCheckBox.IsChecked;
        }

      private void ClearButton_Click(object sender, RoutedEventArgs e)
      { 

      }

    }
}

和上圖一樣,我們嘗試觀察執行程序,看下觸發程序,我們就能了解這個冒泡路由的作業程序了,上面寫的這個例子,主要是讓我們熟悉對于事件傳入引數OriginalSource的使用,勾選界面上的HandleCheckBox復選框可以終止冒泡事件,從而只觸發第一個Image的事件,可以自己寫代碼嘗試一下整個程序,

有個方法可以接收終止的事件訊息,使用AddHandler()多載的方法,

 

這里還有一個常用的技巧,事件的附加,

如下面的代碼,在StackPanel中不存在Button的Click事件,但是可以通過ButtonBase.Click獲取按鈕的點擊事件,此事件將會在StackPanel容器里面的任意按鈕被點擊時觸發,

   <StackPanel ButtonBase.Click="StackPanel_Click" Margin="5">
            <Button Content="按鈕A" Click="Button_Click"/>
            <Button Content="按鈕B" Click="Button_Click"/>
            <Button Content="按鈕C" Click="Button_Click"/>
   </StackPanel>

隧道路由這里就不寫了,前面已經講過,隧道路由命名都是Preview開頭的,隧道路由全部結束了之后,同級的冒泡路由才開始,隧道路由主要是做預先處理,可以停止路由事件,也可以在事件處理程式中寫一些對應的處理代碼,

這篇文章只是熟悉什么是路由事件,了解什么是冒泡路由、什么是隧道路由,所以理解這些是什么事件,這些事件在如何作業就可以了,后面會講到Window類的生命周期,才會去深入分析WPF下的事件程序,

 

我創建了一個C#相關的交流群,用于分享學習資料和討論問題,歡迎有興趣的小伙伴:QQ群:542633085

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/270449.html

標籤:.NET技术

上一篇:WPF MVVMLight SimpleIOC創建一次性實體

下一篇:WPF之影片

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more