事件冒泡和路由事件参数源属性

本文关键字:事件 属性 参数 路由 | 更新日期: 2023-09-27 18:36:20

我正在尝试在一个简单的WPF应用程序中理解RoutedEventArgs.Source属性。下面是 XAML 代码

    <Window x:Class="BubbleDemo.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">
    <StackPanel x:Name="stackPanel1" Button.Click="OnOuterButtonClick">
        <Button x:Name="button1" Content="Button 1" Margin="5" />
        <Button x:Name="button2" Margin="5" Click="OnButton2">
            <ListBox x:Name="listBox1">
                <Button x:Name="innerButton1" Content="Inner Button 1" Margin="4" Padding="4" Click="OnInner1" />
                <Button x:Name="innerButton2" Content="Inner Button 2" Margin="4" Padding="4" Click="OnInner2" />
            </ListBox>
        </Button>
        <ListBox ItemsSource="{Binding}" />        
    </StackPanel>
</Window>

这是背后的代码

using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace BubbleDemo
{
    public partial class MainWindow : Window
    {
        private ObservableCollection<string> messages = new ObservableCollection<string>();
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = messages;
        }
        private void AddMessage(string message, object sender, RoutedEventArgs e)
        {
            messages.Add(String.Format("{0}, sender: {1}; source: {2}; original source: {3}",
                message, (sender as FrameworkElement).Name,
                (e.Source as FrameworkElement).Name,
                (e.OriginalSource as FrameworkElement).Name));  
        }
        private void OnOuterButtonClick(object sender, RoutedEventArgs e)
        {
            AddMessage("outer event", sender, e);   
        }
        private void OnInner1(object sender, RoutedEventArgs e)
        {
            AddMessage("inner1", sender, e);
        }
        private void OnInner2(object sender, RoutedEventArgs e)
        {
            AddMessage("inner2", sender, e);
            e.Handled = true;
        }
        private void OnButton2(object sender, RoutedEventArgs e)
        {
            AddMessage("button2", sender, e);
            e.Source = sender;
        }
    }
}

当我单击 InnerButton1 时,单击事件被引发,然后执行 OnInner1 处理程序。之后执行 OnButton2 处理程序,该处理程序使用 sender 参数设置 RoutedEventArgs.Source 属性。如果生成并执行此代码,则可以看到输出结果。当事件到达 OnOuterButtonClick 处理程序时,底部列表框中的输出应为:

inner1,发件人:innerButton1;来源:innerButton1;原始来源:innerButton1
按钮 2,发件人:按钮 2;来源: 内按钮1;原始来源:内按钮1
外部事件,发送方:堆栈面板 1;来源:按钮2;原始来源:内按钮1

但输出是这样的

inner1,发件人:innerButton1;来源:innerButton1;原始来源:innerButton1
按钮 2,发件人:按钮 2;来源: 内按钮1;原始来源:内按钮1
外部事件,发送方:堆栈面板 1;来源: 内按钮1;原始来源:内按钮1

在 OnButton2 hander 中重新分配的 RoutedEventArgs.Source 属性已更改,但返回到 OnOuterButtonClick 处理程序中的引用 innerButton1。

为什么会这样?谢谢

事件冒泡和路由事件参数源属性

这是一个非常好的问题,我不得不查看 .net 的来源才能弄清楚为什么会这样:

属性如下所示:

public object Source
{
  get {return _source;}
  set
  {
    if (UserInitiated && InvokingHandler)
      throw new InvalidOperationException(SR.Get(SRID.RoutedEventCannotChangeWhileRouting));
    ...
  }
}

每当用户尝试设置源时,当事件冒泡或隧道时,都会抛出此可执行文件。

我假设,.net Framework的一部分,注意这种行为也捕获了异常,所以你没有意识到这个问题。事实上,当尝试设置 Source 属性时,当事件冒泡时,调试器会显示,设置后不会立即更改。

不幸的是,源代码只是显示Microsoft不允许在事件冒泡(或隧道)时更改源属性,但不允许更改原因

如果出于某种原因需要获取有关处理事件的 Prior 处理程序的信息,则可以创建自己的 RoutedEventArgs 扩展并添加另一个包含此信息的属性。

最后,您可以扩展 button 类,并引发您自己的 Event,其中包含相应的 RoutedEventArgsWithHandlerHistory 对象:)

这是一个

有趣的问题,需要反映.net Routing引擎。所以我发现每个UIElement都使用RaiseEvent()方法来启动RoutedEvent。这样做时,它首先构建EventRoute。在构建EventRoute时,它会根据RoutingStrategy创建调用处理程序的列表,即BubbleTunnel它上下UIElement所属的VisualTree,并找出有多少处理程序附加到给定RoutedEvent。很明显,对于innerButton1innerButton1,有三个处理程序。

现在UIElement得到了它的RoutedEvent EventRoute,接下来它在EventRoute上调用InvokeHandlers()。在循环中调用处理程序时,InvokeHandlerargs.Source重置为原始值,如下所示,它正在为 Bubble 策略执行此操作。

    for (int index = 0; index < this._routeItemList.Count; ++index)
    {
      if (index >= endIndex)
      {
        object bubbleSource = this.GetBubbleSource(index, out endIndex);
        if (!reRaised)
          args.Source = bubbleSource ?? source;
      }

因此,在每个处理程序调用之前,Source将重置为其原始值,因此在任何处理程序中更改它都不会传递给下一个处理程序。