我了解冒泡和隧道的工作原理。但是,我对使用它们感到困惑。原因如下:
我想处理鼠标点击事件。要冒泡,有MouseDown
,要挖隧道,有PreviewMouseDown
。但是,MouseDown
并不一定意味着用户单击了控件。可能是用户按下按钮并离开它以取消点击。如果未单击按钮,我不想更改任何内容。
所以我的问题是,冒泡/隧道策略有什么用?
我了解冒泡和隧道的工作原理。但是,我对使用它们感到困惑。原因如下:
我想处理鼠标点击事件。要冒泡,有MouseDown
,要挖隧道,有PreviewMouseDown
。但是,MouseDown
并不一定意味着用户单击了控件。可能是用户按下按钮并离开它以取消点击。如果未单击按钮,我不想更改任何内容。
所以我的问题是,冒泡/隧道策略有什么用?
如果事件被列出RoutedEventArgs
,那么它就是路由事件。路由事件支持 Bubble、Tunnel 或 Direct 的 RoutingStrategy。让我们看一下事件处理程序Button.Click
:
private void Grid_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button Test clicked!");
}
有指定RoutedEventArgs
,所以它是路由事件。因为预览中没有指定名字,所以这个Bubble事件。这可以通过以下方式证明:
<Grid ButtonBase.Click="Grid_Click">
<Button Name="TestButton" Width="100" Height="30" Content="Test" />
</Grid>
当您单击 时TestButton
,事件将上升到 上方Grid
,并显示一条消息:
按钮测试点击了!
Usefulness of Bubbling/Tunneling strategies
Tunneling
许多标准控件监听事件,例如KeyDown
,MouseDown
等。例如 -DataGrid
控件。我想通过按回车键调用该函数添加记录。但DataGrid
已经有KeyDown
事件,所以没有引发事件。所以你必须在隧道事件中做你的逻辑 -PreviewKeyDown
它将在KeyDown
事件之前工作。这同样适用于RichTextBoxControl
。
Bubbling
有时,您需要针对特定事件的全局处理程序,因此它适用于 VisualTree 中的所有控件。自然,一个直接的事件你是做不到的。因此在舞台上出现了冒泡事件。
另一个原因是 WPF 的意识形态。这Button
可以包含任何内容: Image
, anotherButton
等:
用户可以点击TextBlock/Image
中的Button
。我们怎么知道点击是 in Button
?没错,在 Bubbling 事件的帮助下。
欲了解更多信息,请参阅:
Edit
我改变了一点Click
处理程序:
private void Grid_Click(object sender, RoutedEventArgs e)
{
String message = "#" + eventCounter.ToString() + ":\r\n" +
" Sender: " + sender.ToString() + ":\r\n" +
" Source: " + e.Source + ":\r\n" +
" Original Source: " + e.OriginalSource;
lstEvents.Items.Add(message);
}
点击结果Button
:
嗨,虽然你可以在网上得到一些关于这个的好文章,但我仍然会尝试回答这个问题。
假设你给一个按钮一个由单个矩形组成的非常简单的外观,并提供一段简单的文本作为内容即使有这样的基本视觉效果,仍然存在两个元素:文本和矩形。按钮应该响应鼠标单击鼠标是在文本还是矩形上。在标准的 .NET 事件处理模型中,这意味着为这两个元素注册一个 MouseLeftButtonUp 事件处理程序。
当利用 WPF 的内容模型时,这个问题会变得更糟。Button 不限于将纯文本作为标题——它可以包含任何对象作为内容。下面的 xaml 并不是特别雄心勃勃,但即使这样也有六个可见元素:黄色轮廓圆圈、眼睛的两个点、嘴巴的曲线、文本和按钮背景本身。为每个元素附加事件处理程序将是乏味且低效的。要工作 MouseDown 我们必须在这段代码中添加 8 个 MouseDownEvents。
<Button PreviewMouseDown="PreviewMouseDownButton" MouseDown="MouseDownButton">
<Grid PreviewMouseDown="PreviewMouseDownGrid" MouseDown="MouseDownGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Canvas PreviewMouseDown="PreviewMouseDownCanvas" MouseDown="MouseDownCanvas" Width="20" Height="18" VerticalAlignment="Center">
<Ellipse PreviewMouseDown="PreviewMouseDownEllipse" MouseDown="MouseDownEllipse" x:Name="myEllipse" Canvas.Left="1" Canvas.Top="1" Width="16" Height="16" Fill="Yellow" Stroke="Black" />
<Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
<Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
<Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" MouseDown="Path_MouseDown_1"/>
</Canvas>
<TextBlock Grid.Column="1" MouseDown="TextBlock_MouseDown_1">Click!</TextBlock>
</Grid>
</Button>
WPF 使用 RoutedEvents Bubble/Tunnel /Normal,这比普通事件更彻底。WPF 不只是调用附加到引发事件的元素的处理程序,而是遍历用户界面元素树,调用附加到任何节点的路由事件的所有处理程序,从原始元素一直到用户界面树的根。
正如你所说,你知道冒泡/隧道的概念,所以不要进入那个。但这就是这些事件的目的,即它们让您知道控件或其子项上的鼠标按钮是向下还是向上。事件工作正常。对于您的场景,您应该使用Click
按钮的事件来判断鼠标是否在按钮本身上下。
谢谢