2013-10-21 46 views
2

我想了解一個簡單的WPF應用程序中的RoutedEventArgs.Source屬性。這裏是XAML代碼事件冒泡和RoutedEventArgs來源屬性

<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處理程序後,該處理程序將發件人參數設置爲RoutedEventArgs.Source屬性。 如果您構建並執行此代碼,則可以看到輸出結果。 當事件到達OnOuterButtonClick處理程序時,底部ListBox中的輸出應爲:

inner1,sender:innerButton1;來源:innerButton1;原始來源:innerButton1
button2,sender:button2;來源:innerButton1;原始來源:innerButton1
外部事件,發件人:stackPanel1;來源:button2;原始來源:innerButton1

但輸出是這個

inner1,發件人:innerButton1;來源:innerButton1;原始來源:innerButton1
button2,sender:button2;來源:innerButton1;原始來源:innerButton1
外部事件,發件人:stackPanel1;來源:innerButton1;原始來源:innerButton1

在OnButton2投手重新分配的RoutedEventArgs.Source屬性更改,但返回到OnOuterButtonClick處理程序中引用innerButton1。

爲什麼會發生這種情況? 感謝

回答

1

這是一個很好的問題,我必須尋找到.NET的來源搞清楚爲什麼它是這樣的:

來源房產看起來是這樣的:

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

    ... 
    } 
} 

只要用戶嘗試設置源,而事件是鼓泡或隧道,就會拋出此可執行選項。

我假設.net框架的一部分,照顧這種行爲也捕捉異常,所以你不知道這個問題。實際上,當試圖設置源屬性時,當事件冒泡時,調試器顯示,即而不是在設置後立即更改。

不幸的是,源代碼,只是表明微軟不允許更改來源 - 財產,而該事件是冒泡(或隧道),但不爲什麼

如果您 - 無論出於何種原因 - 需要獲取有關處理事件的處理程序的信息,您可以創建自己的擴展RoutedEventArgs並添加另一個包含此信息的屬性。

終於可以延長button類,並提出自己的事件,包含相應RoutedEventArgsWithHandlerHistory對象:)

1

這是一個有趣的問題,並要求以反映.NET Routing引擎。所以我發現每個UIElement使用RaiseEvent()方法來啓動RoutedEvent。在這樣做的時候,它首先構建了EventRoute。在構建EventRoute時,它根據RoutingStrategy創建調用處理程序列表,即BubbleTunnel它上下移動到UIElement所屬的VisualTree,並找出給定的RoutedEvent附加了多少個處理程序。顯然,在你的情況下,對於innerButton1innerButton1有三個處理程序。

現在UIElement得到EventRoute爲其RoutedEvent,接下來它調用InvokeHandlers()EventRoute。在調用循環中的處理程序時,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被複位到其因此原始值改變它的任何處理程序內將不會被傳遞到下一個處理程序。