2011-02-17 37 views
2

我爲此感到困惑:WPF事件處理髮射了錯誤的元素

我做了一個很簡單的例子:

MainWindow.xaml:

<Window x:Class="Test.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"> 

    <Window.Resources> 
     <Style TargetType="RichTextBox"> 
      <Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="RichTextBox"> 
         <Grid Height="100" Width="200"> 
          <Grid.RowDefinitions> 
           <RowDefinition/> 
           <RowDefinition/> 
          </Grid.RowDefinitions> 
          <Label Background="Blue" Grid.Row="0">Label</Label> 
          <Border PreviewMouseDown="Border_PreviewMouseDown" Background="Red" Grid.Row="1"> 
           <ScrollViewer x:Name="PART_ContentHost" /> 
          </Border> 
         </Grid> 
        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </Window.Resources> 

    <Grid> 
     <RichTextBox> 
      <FlowDocument> 
       <FlowDocument.Blocks> 
        <Paragraph> 
         oaizeropiazuerpoaizeurpoaizeurpaozieurpaozieru 
        </Paragraph> 
       </FlowDocument.Blocks> 
      </FlowDocument> 
     </RichTextBox> 
    </Grid> 
</Window> 

MainWindow.xaml.cs:

using System.Diagnostics; 
using System.Windows; 
using System.Windows.Input; 

namespace Test 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
     InitializeComponent(); 
     } 

     private void Border_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
     { 
      Debug.WriteLine("Click !"); 
     } 

    } 
} 

現在,因爲我明確地將PreviewMouseDown EventHandler放在邊框上,而不是放在模板的標籤上,所以我期望當我點擊控件的(紅色)邊框時會觸發,但當點擊(藍色)標籤時不會觸發。

然而,當我點擊(紅色)邊境事件被觸發當我點擊(藍色)標籤上。

那麼,爲什麼標籤調用我明確地連接到控件模板(即:邊境)的另一部分的事件處理程序?

我檢查:如果我請從邊框的屬性PreviewMouseDown="Border_PreviewMouseDown"代碼,該事件不會觸發該標籤的任何更多。

我缺少什麼嗎?

,什麼是做正確的方式?如何設計我的controlTemplate,以便PreviewMouseDown事件僅由模板化控件的子部分觸發?

在此先感謝

編輯:以下Snowbear的回答,我查了事件的originalSource當我點擊標籤。這確實是邊界。這是爲什麼?邊界如何在上面的模板中封裝標籤?我特意將它們放在不同的網格行上以避免這種情況,那麼怎麼來?爲了好玩,我創建了一個處理程序,它只打印事件的sender/source/originalSource,並將其附加到網格,邊框和scrollviewer的模板中。

下面是當我在垂直滾動條點擊一次(也是唯一一次),比如我得到什麼:

Clic -- Sender: System.Windows.Controls.Grid -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: MyRichTextBox 
Clic -- Sender: System.Windows.Controls.Border -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: MyRichTextBox 
Clic -- Sender: System.Windows.Controls.ScrollViewer -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: MyRichTextBox 
Clic -- Sender: System.Windows.Controls.Grid -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: System.Windows.Controls.ScrollViewer 
Clic -- Sender: System.Windows.Controls.Border -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: System.Windows.Controls.ScrollViewer 
Clic -- Sender: System.Windows.Controls.ScrollViewer -- OriginalSource: Microsoft.Windows.Themes.ScrollChrome -- Source: System.Windows.Controls.ScrollViewer 

這顯然結算事項:本次活動確實隧道兩次,出於某種原因,首先與TemplatedParent(即:在RichTextBox)作爲源,然後與contentPresenter(即:ScrollViewer中)作爲源。

梅林最寬鬆的褲子,我真的不知道通過MS開發的頭編程這...

回答

0

哎呀,你確實發現了一個很奇怪的行爲。看來,鼠標處理對於所有屬於一個TextBoxBase控制模板的一部分可以revectored到文本內容元素,並從那裏再隧道/氣泡的元素。因此,在富文本框控件模板中包含標籤意味着它將參與富文本中的鼠標事件,就像它是富文本本身的一部分一樣。

要解決此問題,可以使用包含關聯元素的ContentControl,然後將其Content屬性轉發給派生元素的「正常」TextBoxBase。這是一個簡化的XAML示例。第一部分再現了一個簡單的例子奇怪的行爲,第二部分展示瞭如何使用一個ContentControl來解決該問題。

<Grid> 
    <StackPanel> 
     <TextBox Text="Some text"> 
      <TextBox.Template> 
       <ControlTemplate TargetType="TextBox"> 
        <StackPanel> 
         <Rectangle Fill="Green" Width="200" Height="50"/> 
         <Border x:Name="RedBorder" PreviewMouseDown="Border_PreviewMouseDown" Background="Red"> 
          <AdornerDecorator x:Name="PART_ContentHost" /> 
         </Border> 
        </StackPanel> 
       </ControlTemplate> 
      </TextBox.Template> 
     </TextBox> 
     <ContentControl Content="Some text"> 
      <ContentControl.Template> 
       <ControlTemplate TargetType="ContentControl"> 
        <StackPanel> 
         <Rectangle Fill="Green" Width="200" Height="50"/> 
         <Border x:Name="RedBorder" PreviewMouseDown="Border_PreviewMouseDown" Background="Red"> 
          <TextBlock Text="{TemplateBinding Content}"/> 
         </Border> 
        </StackPanel> 
       </ControlTemplate> 
      </ContentControl.Template> 
     </ContentControl> 
    </StackPanel> 
</Grid> 
0

你錯過冒泡事件在這裏什麼地方。 http://msdn.microsoft.com/en-us/library/ms742806.aspx#routing_strategies

您可以檢查RoutedEventArgs.OriginalSource確定單擊該邊框。

UPDATE:好吧,它看起來像我錯過了,但不完全。我打了一些時間與你的樣品,它看起來像RichTextBox(或可能TextBoxBase)確實與PART_ContentHost這迫使tunnelling事件去實現這部分的東西。你的示例在RichTextBox中運行良好,因此我假設它使用自己的模板進行操作。雖然我不知道如何進一步調查它,但沒有進入.Net資源。

+0

「鼓泡:事件源上的事件處理程序被調用路由事件然後路由到連續的父元素,直到到達元素樹根」。通過任何手段<標籤是不邊界的父,那麼,爲什麼該事件會爲標籤邊界起泡?我明白爲什麼它會冒泡到RichTextBox,它是模板父類,但標籤在這裏是如何作爲邊界的父項? – David 2011-02-17 14:04:32

+0

我編輯了我的問題關於你的答案。我認爲你是在正確的軌道上,但是這仍然不能解釋一切(見編輯) – David 2011-02-17 14:09:15