I am puzzled by this:
I have made a very simple example:
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 !");
}
}
}
now, as I explicitly put the PreviewMouseDown EventHandler on the Border and not on the label in my template, I expect that it will fire when I click on the (red) border of the control, but not when I click on the (blue) label.
However, the event is fired when I click on the (red) Border AND when I click on the (blue) label.
so why does the Label call an EventHandler that I explicitly attached to an other part of the controlTemplate (i.e.: the border)?
I’ve checked: If I remove the PreviewMouseDown="Border_PreviewMouseDown" code from the border’s properties, the event is not fired on the label any more.
what am I missing here?
and what would be the right way to do? How can I design my controlTemplate so that the PreviewMouseDown Event is fired only by a sub-part of the templated control?
thanks in advance
Edit: following Snowbear’s answer, I checked the originalSource of the event when I click on the Label. It is indeed the border. Why is this so? in what way is the border encapsulating the label in the template above? I specifically set them on different grid rows to avoid this, so how come?
Edit2 Just for the fun, I created a handler that only prints the sender/source/originalSource of the event, and I attached it in the template to The grid, the border and the scrollviewer.
Here Is what I get when I click ONCE (and only once) on the vertical scrollbar for instance:
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
this clearly settles the matter: The event is indeed tunnelled twice, for some reason, First with the TemplatedParent (i.e.: the RichtextBox) as Source, and Then with the contentPresenter (i.e.: the ScrollViewer) as Source.
By Merlin’s most baggy pants, I really wonder what went through the head of the MS Dev that programmed this…
Gee, you’ve uncovered a very strange behavior indeed. It appears that the mouse handling for all the elements that are part of a
TextBoxBasecontrol template can be revectored to the text content element and then tunnel/bubble from there. As a result, including a label in a rich text box control template means it will participate in mouse events on the rich text as though it were part of the rich text itself.To work around this problem, you can use a
ContentControlthat includes the associated elements and then forwards itsContentproperty to a “normal”TextBoxBasederived element. Here is a simplified XAML example. The first section reproduces the strange behavior in a simpler example and the second section shows how to use aContentControlto work around the problem.