WPF EventHandler выстрелил не по тому элементу - PullRequest
2 голосов
/ 17 февраля 2011

Я озадачен этим:

Я сделал очень простой пример:

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 на Границу, а не на метку в моем шаблоне, я ожидаю, что он будет срабатывать, когда я щелкаю (красную) границу элемента управления, но не когда я нажимаю на (синяя) метка.

Однако событие вызывается, когда я нажимаю (красную) границу И , когда я нажимаю (синюю) метку.

Так почему же Label вызывает EventHandler, который я явно прикрепил к другой части элемента controlTemplate (т.е. границы)?

Я проверил: если я удаляю код PreviewMouseDown="Border_PreviewMouseDown" из свойств границы, событие больше не вызывается для метки.

что мне здесь не хватает?

а что бы правильно сделать? Как я могу спроектировать свой controlTemplate так, чтобы событие PreviewMouseDown вызывалось только частью элемента шаблонного элемента управления?

заранее спасибо

Редактировать: , следуя ответу Снежного медведя, я проверял исходный источник события, когда нажимал на Метку. Это действительно граница. Почему это так? каким образом граница инкапсулирует метку в шаблоне выше? Я специально установил их на разных строках сетки, чтобы избежать этого, так как получилось?

Edit2 Просто для забавы, я создал обработчик, который печатает только отправителя / 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) в качестве Source, а затем с contentPresenter (т.е. ScrollViewer) в качестве Source.

Из-за самых мешковатых штанов Мерлина, мне действительно интересно, что произошло через голову MS Dev, который запрограммировал это ...

Ответы [ 2 ]

0 голосов
/ 18 февраля 2011

Ну и дела, вы действительно обнаружили очень странное поведение.Похоже, что обработка мышью для всех элементов, которые являются частью шаблона элемента управления TextBoxBase, может быть обнаружена для элемента текстового содержимого, а затем туннелем / пузырем оттуда.В результате включение метки в шаблон элемента управления расширенного текстового поля означает, что он будет участвовать в событиях мыши для расширенного текста, как если бы он был частью самого расширенного текста.

Чтобы обойти эту проблему, вы можетеиспользуйте 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 голосов
/ 17 февраля 2011

Вы пропускаете бурлящие события здесь.http://msdn.microsoft.com/en-us/library/ms742806.aspx#routing_strategies

Вы можете проверить RoutedEventArgs.OriginalSource, чтобы определить, какая граница была нажата.

ОБНОВЛЕНИЕ : Хорошо, похоже, я пропустил, но не полностью,Я некоторое время играл с вашим семплом, похоже, что RichTextBox (или, возможно, TextBoxBase) что-то делает с PART_ContentHost, что заставляет события tunnelling идти к этой части.Ваш пример хорошо работает с RichTextBox, поэтому я предполагаю, что он делает что-то с собственным шаблоном.Хотя я не буду исследовать это дальше, не вдаваясь в источники .Net.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...