我试图在一个简单的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时,将引发click事件,然后执行OnInner1处理程序。
之后,执行OnButton2处理程序,该处理程序使用sender参数设置RoutedEventArgs.Source属性。
如果构建并执行此代码,则可以看到输出结果。
当事件到达OnOuterButtonClick处理程序时,底部ListBox中的输出应为:

inner1,发件人:innerButton1;来源:innerButton1;原始来源:innerButton1
button2,发件人:button2;来源:innerButton1;原始来源:innerButton1
外部事件,发送方:stackPanel1;来源:button2;原始来源:innerButton1

但是输出是这个

inner1,发件人:innerButton1;来源:innerButton1;原始来源:innerButton1
button2,发件人:button2;来源:innerButton1;原始来源:innerButton1
外部事件,发送方:stackPanel1;来源:innerButton1;原始来源:innerButton1

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

为什么会这样?
谢谢

最佳答案

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

源属性如下所示:

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

    ...
  }
}


每当用户正在尝试设置源时,即事件为冒泡或隧穿时,都将引发此执行。

我假设,.net Framework的这一部分,也要注意这种行为,也正在捕获Exception,所以您不会意识到问题所在。实际上,当尝试设置Source Property时,事件正在冒泡时,Debugger会显示出来,在设置它后不会立即更改。

不幸的是,源代码仅显示Microsoft在事件冒泡(或隧穿)时不允许更改源属性,但为什么呢?

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

最后,您可以扩展button类,并引发自己的Event,其中包含适当的RoutedEventArgsWithHandlerHistory Object :)

09-18 13:09