代码改变世界

无废话WPF系列13:路由事件

  敏捷的水  阅读(1648)  评论(1编辑  收藏  举报

逻辑树

<Window>
  <Grid>
    <Button>
      <StackPanel>
        <Image/>
        <TextBlock/>
      </StackPanel>
    </Button>
  </Grid>
</Window>

但是实际上这些元素在运行时会扩展为可是树

image
 

事件路由

对逻辑树和可视树有所了解很有必要,因为路由事件主要是根据可视树进行路由。路由事件支持三种路由策略:气泡、隧道和直接。

气泡事件最为常见,它表示事件从源元素扩散(传播)到可视树,直到它被处理或到达根元素。这样您就可以针对源元素的上方层级对象处理事件。例如,您可向嵌入的 Grid 元素附加一个 Button.Click 处理程序,而不是直接将其附加到按钮本身。气泡事件有指示其操作的名称(例如,MouseDown)。

隧道事件采用另一种方式,从根元素开始,向下遍历元素树,直到被处理或到达事件的源元素。这样上游元素就可以在事件到达源元素之前先行截取并进行处理。根据命名惯例,隧道事件带有前缀 Preview(例如 PreviewMouseDown)。

直接事件类似 .NET Framework 中的正常事件。该事件唯一可能的处理程序是与其挂接的委托。

通常,如果为特殊事件定义了隧道事件,就会有相应的气泡事件。在这种情况下,隧道事件先触发,从根元素开始,下行至源元素,查找处理程序。一旦它被处理或到达源元素,即会触发气泡事件,从源元素上行,查找处理程序。气泡或隧道事件不会仅因调用事件处理程序而停止路由。如果您想中止隧道或气泡进程,可使用您传递的事件参数在事件处理程序中将事件标记为已处理。

示例(我们在Grid上加了一个Button.Click的附加事件:

1
2
3
4
5
6
7
8
9
10
<Window x:Class="DeepXAML.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DeepXAML"      
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="250" Width="450">
    <Grid x:Name="rootGrid" Button.Click="rootGrid_Click">
        <Button x:Name="btnOK" Margin="30">OK</Button>
    </Grid>
</Window>

 

后台代码:

1
2
3
4
5
private void rootGrid_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show((e.Source as FrameworkElement).Name); //btnOk
    MessageBox.Show((e.OriginalSource as FrameworkElement).Name); //btnOk
}

 

source是指LogicTree的源途,orginalSource指的是VisualTree上的源

 

自定义路由事件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Window x:Class="DeepXAML.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DeepXAML"      
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="250" Width="450">
    <Grid x:Name="rootGrid" >
        <StackPanel x:Name="stp1" local:TestButton.ClickTimeEvent="TimeHanler">
            <StackPanel x:Name="stp2" local:TestButton.ClickTimeEvent="TimeHanler">
                <StackPanel x:Name="stp3" local:TestButton.ClickTimeEvent="TimeHanler">
                    <ListBox x:Name="listBox"></ListBox>
                    <local:TestButton local:TestButton.ClickTimeEvent="TimeHanler" Height="50" Margin="30">OK</local:TestButton>
                </StackPanel>
            </StackPanel>
        </StackPanel>      
    </Grid>
</Window>
1
后台代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Controls;
 
namespace DeepXAML
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
     
        }
 
        private void TimeHanler(object sender, TimeEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            string strTime = e.ClickTime.ToLongTimeString();
            this.listBox.Items.Add( element.Name+":"+ strTime);
        }             
    }
 
    public class TimeEventArgs : RoutedEventArgs
    {
        public TimeEventArgs(RoutedEvent routedEvent, object source):base(routedEvent,source)
        {          
        }
        public DateTime ClickTime { get; set; }
    }
 
    public class TestButton : Button
    {
      public static RoutedEvent timeEvent=
          EventManager.RegisterRoutedEvent("ClickTimeEvent", RoutingStrategy.Bubble, typeof(EventHandler<TimeEventArgs>), typeof(TestButton));
 
        public event  RoutedEventHandler ClickTimeEvent
        {
          add {this.AddHandler(timeEvent,value);}
          remove{this.RemoveHandler(timeEvent,value);}
        }
 
        protected override void  OnClick()
        {
              base.OnClick();
            TimeEventArgs args=new TimeEventArgs(timeEvent,this);
            args.ClickTime=DateTime.UtcNow;
            this.RaiseEvent(args);
        }
    }   
}
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
历史上的今天:
2008-02-27 ten sentences(91-100)
2008-02-27 ten sentences(81-90)
2008-02-27 ten sentences(71-80)
2008-02-27 ten sentences(61-70)
2008-02-27 ten sentences(51-60)
点击右上角即可分享
微信分享提示