DataGridColumnHeader 中 Popup 中的奇怪事件行为
Weird event behavior in a Popup inside a DataGridColumnHeader
我在这里没有使用任何外部库,只是普通的 WPF。
我有一个带有自定义 DataGridColumnHeader 的 DataGrid。此列 header 包含用于切换弹出窗口的切换按钮。在弹出窗口中有一个文本框。我遇到的问题是在 TextBox 内双击会引发 DataGrid 上的 MouseDoubleClick 事件。这是一个简化版本,其中包含我稍后将参考的编号评论
<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) 我想避免双击事件冒泡到这里,但即使我在事件处理程序 2 中将 e.Handled 设置为 true,也会调用此事件处理程序,而在该处理程序中,e.Handled是假的。我认为原因是 Popup 是在 DataGridColumnHeader 内部定义的,并且出于某种奇怪的原因,正在引发 2 个事件,一个用于弹出树的树,一个用于包含 Popup 元素的树,但这似乎有点荒谬.
我知道 Popup 在 WPF 中是一种奇怪的东西,但这似乎我遗漏了一些明显的东西。有什么方法可以实现我想要的,即没有事件(或至少 MouseDoubleClick 事件)冒泡到 DataGrid?
提前致谢,
大卫
1) 你是对的,你可以将路由事件的处理程序附加到任何 UIElement
。但确实 Popup
是一个特例。 Popup
是一个特殊的控件,其行为类似于 Window
。它可以在屏幕上随处弹出,始终呈现在最顶层,并且不一定绑定到应用程序本身。这就是它的可视化树与应用程序的可视化树分离的原因。 Popup.Child
将是一个独立的独立可视化树。 Microsoft Docs: Popup and the Visual Tree.
由于路由事件遍历可视化树以由任何节点处理,因此 Popup
内的 bubbling/tunneling 路由事件将 stop/start 位于该孤立树的根部是有意义的。因此,无法在 Popup
.
之外处理源自 Popup
的路由事件
3) 简短版:Control.MouseDoubleClick
(和预览版)是一个行为不同的特殊事件。当事件遍历可视化树时,路由上的每个 UIElement
都会引发此事件。所以设置 Handled
为 true
没有效果。
要解决您的问题,您应该处理 UIElement.PreviewMousLeftButtonDown
并检查 MouseButtonEventArgs.ClickCount
是否等于 2
以检测双击,然后设置 Handled = true
或者在处理之前检查 sender
或 RoutedEventArgs.Source
的类型是否不是 TextBox
(显式事件过滤)。
"Although this routed event seems to follow a bubbling route through
an element tree, it actually is a direct routed event that is raised
along the element tree by each UIElement
. If you set the Handled
property to true
in a MouseDoubleClick
event handler, subsequent
MouseDoubleClick
events along the route will occur with Handled
set to
false
. This is a higher-level event for control consumers who want to
be notified when the user double-clicks the control and to handle the
event in an application.
Control authors who want to handle mouse double clicks should use the
MouseLeftButtonDown
event when ClickCount
is equal to two. This will
cause the state of Handled
to propagate appropriately in the case
where another element in the element tree handles the event.
The Control
class defines the PreviewMouseDoubleClick
and
MouseDoubleClick
events, but not corresponding single-click events. To
see if the user has clicked the control once, handle the MouseDown
event (or one of its counterparts) and check whether the ClickCount
property value is 1
."
Microsoft Docs: Control.MouseDoubleClick
我在这里没有使用任何外部库,只是普通的 WPF。
我有一个带有自定义 DataGridColumnHeader 的 DataGrid。此列 header 包含用于切换弹出窗口的切换按钮。在弹出窗口中有一个文本框。我遇到的问题是在 TextBox 内双击会引发 DataGrid 上的 MouseDoubleClick 事件。这是一个简化版本,其中包含我稍后将参考的编号评论
<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) 我想避免双击事件冒泡到这里,但即使我在事件处理程序 2 中将 e.Handled 设置为 true,也会调用此事件处理程序,而在该处理程序中,e.Handled是假的。我认为原因是 Popup 是在 DataGridColumnHeader 内部定义的,并且出于某种奇怪的原因,正在引发 2 个事件,一个用于弹出树的树,一个用于包含 Popup 元素的树,但这似乎有点荒谬.
我知道 Popup 在 WPF 中是一种奇怪的东西,但这似乎我遗漏了一些明显的东西。有什么方法可以实现我想要的,即没有事件(或至少 MouseDoubleClick 事件)冒泡到 DataGrid?
提前致谢, 大卫
1) 你是对的,你可以将路由事件的处理程序附加到任何 UIElement
。但确实 Popup
是一个特例。 Popup
是一个特殊的控件,其行为类似于 Window
。它可以在屏幕上随处弹出,始终呈现在最顶层,并且不一定绑定到应用程序本身。这就是它的可视化树与应用程序的可视化树分离的原因。 Popup.Child
将是一个独立的独立可视化树。 Microsoft Docs: Popup and the Visual Tree.
由于路由事件遍历可视化树以由任何节点处理,因此 Popup
内的 bubbling/tunneling 路由事件将 stop/start 位于该孤立树的根部是有意义的。因此,无法在 Popup
.
Popup
的路由事件
3) 简短版:Control.MouseDoubleClick
(和预览版)是一个行为不同的特殊事件。当事件遍历可视化树时,路由上的每个 UIElement
都会引发此事件。所以设置 Handled
为 true
没有效果。
要解决您的问题,您应该处理 UIElement.PreviewMousLeftButtonDown
并检查 MouseButtonEventArgs.ClickCount
是否等于 2
以检测双击,然后设置 Handled = true
或者在处理之前检查 sender
或 RoutedEventArgs.Source
的类型是否不是 TextBox
(显式事件过滤)。
"Although this routed event seems to follow a bubbling route through an element tree, it actually is a direct routed event that is raised along the element tree by each
UIElement
. If you set theHandled
property totrue
in aMouseDoubleClick
event handler, subsequentMouseDoubleClick
events along the route will occur withHandled
set tofalse
. This is a higher-level event for control consumers who want to be notified when the user double-clicks the control and to handle the event in an application.Control authors who want to handle mouse double clicks should use the
MouseLeftButtonDown
event whenClickCount
is equal to two. This will cause the state ofHandled
to propagate appropriately in the case where another element in the element tree handles the event.The
Control
class defines thePreviewMouseDoubleClick
andMouseDoubleClick
events, but not corresponding single-click events. To see if the user has clicked the control once, handle theMouseDown
event (or one of its counterparts) and check whether theClickCount
property value is1
."
Microsoft Docs: Control.MouseDoubleClick