Странное поведение события во всплывающем окне внутри DataGridColumnHeader - PullRequest
0 голосов
/ 29 мая 2020

Я здесь не использую никаких внешних библиотек, просто WPF.

У меня есть DataGrid с настраиваемым DataGridColumnHeader. Заголовок этого столбца содержит ToggleButton для переключения всплывающего окна. Внутри всплывающего окна есть текстовое поле. Проблема, с которой я столкнулся, заключается в том, что двойной щелчок внутри TextBox вызывает событие MouseDoubleClick в DataGrid. Вот упрощенная версия, содержащая пронумерованные комментарии, к которым я буду обращаться позже

<Window x:Class="PopupsAreWeird.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PopupsAreWeird" xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="DataGridColumnHeader">
                        <!-- 1) This eventhandler is never called -->
                        <Grid Control.MouseDoubleClick="Grid_MouseDoubleClick_1">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>

                            <ContentPresenter Grid.Column="0" />
                            <ToggleButton x:Name="openToggle" Grid.Column="1" Content="Open" />

                            <Popup IsOpen="{Binding ElementName=openToggle, Path=IsChecked}" StaysOpen="True">
                                <!-- 2) This eventhandler is always called, and the problem I am having is there regardless of whether I set e.Handled = true in this handler or not -->
                                <TextBox Width="200" MouseDoubleClick="TextBox_MouseDoubleClick" />
                            </Popup>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel Orientation="Vertical">
        <!-- 3) This eventhandler is always called, but never should be -->
        <DataGrid MouseDoubleClick="DataGrid_MouseDoubleClick">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Header 1" Width="200" />
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</Window>

1) Я не понимаю, почему этот обработчик никогда не вызывается. Это должно было установить для e.Handled значение true, чтобы перестать подниматься вверх в DataGrid. Хотя Grid не имеет определения MouseDoubleClick, насколько я понимаю, я могу прикрепить обработчики событий для любого события к любому элементу (например, прикрепить ButtonBase.Click к элементу Panel). Разве это не правда, или здесь есть какой-то особый случай, который мне не хватает?

3) Я хочу, чтобы событие двойного щелчка не всплывало сюда, но даже если я установил e.Handled в true в обработчике событий 2, вызывается этот обработчик событий, и в этом обработчике e.Handled имеет значение false. Я предполагаю, что причина в том, что Popup определяется внутри DataGridColumnHeader, и по какой-то странной причине возникают 2 события: одно для дерева всплывающего окна и одно для дерева, содержащего элемент Popup, но это кажется немного бессмысленным .

Я знаю, что Popup - это своего рода странная вещь в WPF, но похоже, что я упускаю что-то очевидное. Есть ли способ добиться того, чего я хочу, т.е. не иметь событий (или хотя бы события MouseDoubleClick) до DataGrid?

Заранее спасибо, Дэвид

1 Ответ

1 голос
/ 29 мая 2020

1) Вы правы, к любому UIElement можно прикрепить обработчик перенаправленного события. Но на самом деле Popup - особый случай. Popup - это специальный элемент управления, который ведет себя как Window. Он может всплывать везде на экране, всегда отображается в самом верху и не обязательно привязан к самому приложению. Вот почему его визуальное дерево отделено от визуального дерева приложения. Popup.Child будет отдельным изолированным визуальным деревом. Microsoft Docs: всплывающее окно и визуальное дерево .
Поскольку перенаправленные события проходят через визуальное дерево для обработки любым узлом, имеет смысл, что восходящая маршрутизация / туннелирование перенаправленных событий внутри Popup остановит / запустит в root этого изолированного дерева. Таким образом, перенаправленные события, которые происходят в Popup, не могут быть обработаны за пределами Popup.

3) Краткая версия: Control.MouseDoubleClick (и предварительная версия) - это особое событие, которое ведет себя иначе. Это событие возникает на каждом UIElement на маршруте, когда событие проходит через визуальное дерево. Поэтому установка Handled на true не имеет никакого эффекта.
Чтобы решить вашу проблему, вы должны либо обработать UIElement.PreviewMousLeftButtonDown и проверить, равно ли MouseButtonEventArgs.ClickCount 2 для обнаружения двойного щелчка, а затем установить Handled = true
или проверьте, не является ли тип sender или RoutedEventArgs.Source не TextBox перед обработкой (явная фильтрация событий).

«Хотя это перенаправленное событие, похоже, следует за восходящей маршрут через дерево элементов, на самом деле это событие с прямой маршрутизацией, которое возникает в дереве элементов каждым UIElement. Если вы установите для свойства Handled значение true в обработчике событий MouseDoubleClick, последующие MouseDoubleClick события по маршруту будут происходить с Handled, установленным на false. Это событие более высокого уровня для потребителей элементов управления, которые хотят получать уведомление, когда пользователь дважды щелкает элемент управления и обрабатывает событие в приложении.

Авторы элементов управления, которые хотят обрабатывать двойные щелчки мышью, должны использовать событие MouseLeftButtonDown, когда ClickCount равно 2. Это приведет к тому, что состояние Handled будет распространяться соответствующим образом в случае w здесь другой элемент в дереве элементов обрабатывает событие.

Класс Control определяет события PreviewMouseDoubleClick и MouseDoubleClick, но не соответствующие события одиночного щелчка. Чтобы узнать, щелкнул ли пользователь элемент управления один раз, обработайте событие MouseDown (или одно из его аналогов) и проверьте, равно ли значение свойства ClickCount 1. "
Microsoft Docs: Control. МышьDoubleClick

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