我试图理解一个简单的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 Handler之后,它使用 sender 参数设置RoutedEventArgs.Source属性。 如果构建并执行此代码,则可以看到输出结果。 当事件到达OnOuterButtonClick处理程序时,底部ListBox中的输出应为:
inner1,sender:innerButton1; source:innerButton1;原始来源:innerButton1
button2,发件人:button2; source:innerButton1;原始来源:innerButton1
外部事件,发送者:stackPanel1;来源: button2 ;原始来源:innerButton1
但输出是
inner1,sender:innerButton1; source:innerButton1;原始来源:innerButton1
button2,发件人:button2; source:innerButton1;原始来源:innerButton1
外部事件,发送者:stackPanel1;来源: innerButton1 ;原始来源:innerButton1
OnButton2 hander中重新分配的RoutedEventArgs.Source属性已更改,但返回到OnOuterButtonClick处理程序中的引用innerButton1。
为什么会这样? 感谢
答案 0 :(得分:1)
这是一个非常好的问题,我不得不查看.net的来源,找出为什么是这样的:
Source 属性如下所示:
public object Source
{
get {return _source;}
set
{
if (UserInitiated && InvokingHandler)
throw new InvalidOperationException(SR.Get(SRID.RoutedEventCannotChangeWhileRouting));
...
}
}
当用户尝试设置源时,抛出此execption,同时事件是冒泡或隧道。
我假设,.net框架中负责此行为的部分也捕获了异常,所以你没有意识到这个问题。事实上,在尝试设置源属性时,当事件冒泡时,调试器显示,设置后立即更改不。
不幸的是,源代码只显示 Microsoft在事件冒泡(或隧道)时不允许更改Source-Property,但为什么。
如果您 - 无论出于何种原因 - 需要获取有关处理事件的 Prior 处理程序的信息,您可以创建自己的RoutedEventArgs
扩展并添加另一个包含此信息的属性。
最后,您可以扩展button
类,并引发包含相应RoutedEventArgsWithHandlerHistory
对象的自己的事件:)
答案 1 :(得分:1)
这是一个有趣的问题,并要求反映.net Routing
引擎。所以我发现每个UIElement
使用RaiseEvent()
方法来启动RoutedEvent
。在这样做时,它首先构建EventRoute
。在构建EventRoute
时,它会根据RoutingStrategy
创建Invoke处理程序列表,即Bubble
和Tunnel
,它会VisualTree
向上和向下UIElement
{1}}属于并找出有多少处理程序附加到给定的RoutedEvent
。很明显,对于innerButton1
和innerButton1
来说,有三个处理程序。
现在UIElement
获得EventRoute
的{{1}},然后RoutedEvent
调用InvokeHandlers()
。在循环中调用处理程序时,EventRoute
将InvokeHandler
重置为它在下面的原始值,就像它为Bubble策略所做的那样。
args.Source
因此,在每个处理程序调用之前, 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;
}
将重置为其原始值,因此在任何处理程序中更改它将不会传递给下一个处理程序。