WPF 中的隧道和冒泡事件何时有用?
Posted
技术标签:
【中文标题】WPF 中的隧道和冒泡事件何时有用?【英文标题】:When are tunneling and bubbling events useful in WPF? 【发布时间】:2013-09-07 09:59:45 【问题描述】:我了解冒泡和隧道的工作原理。但是,我对使用它们感到困惑。 原因如下:
我想处理鼠标点击事件。要冒泡,有MouseDown
,要挖隧道,有PreviewMouseDown
。但是,MouseDown
并不一定意味着用户单击了控件。可能是用户按下按钮并离开它以取消点击。
如果没有单击按钮,我不想更改任何内容。
所以我的问题是,冒泡/隧道策略有什么用?
【问题讨论】:
【参考方案1】:如果事件列在RoutedEventArgs
,那么它就是路由事件。路由事件支持 Bubble、Tunnel 或 Direct 的 RoutingStrategy。我们来看看Button.Click
的事件处理函数:
private void Grid_Click(object sender, RoutedEventArgs e)
MessageBox.Show("Button Test clicked!");
这里指定了RoutedEventArgs
,所以是路由事件。因为 preview 没有在名字中指定,所以这个 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
,另一个Button
,等等:
用户可以点击Button
中的TextBlock/Image
。我们如何知道点击是在Button
中?没错,就是借助 Bubbling 事件。
更多信息,请参见:
Understanding Routed Events and Commands In WPF
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
的结果:
【讨论】:
但是,据我所知,点击是直接事件而不是冒泡事件。 @SanSolo:如果是直接的,那么消息Button Test clicked!
没有出现。我们为Grid
设置了一个处理程序,而不是Button
,然后单击Button
,该事件对Grid
起作用。 Button
的事件向上移动,来到Grid
。直接点击事件,据我所知,一直在WinForms
.
是的,你是对的。我运行了您提供的示例来检查发件人、来源和原始来源。 Thw 值分别为 Grid、Button 和 Button:Test。那么默认情况下,所有 WPF 事件都是冒泡事件吗?这令人困惑...隧道事件有 preview 关键字,但不知道哪些是冒泡事件。
@SanSolo:默认情况下,所有事件都是RoutedEvents
。没错,如果名称在 preview
中,则它是隧道事件。您可以查看我在回答中给您的文章。
谢谢。我在another msdn article 上发现了这一点:>根据事件定义,事件路由可以沿两个方向之一行进,但通常路由从源元素行进,然后通过元素树向上“冒泡”,直到到达元素树根(通常是页面或窗口)。所以,默认策略是冒泡。【参考方案2】:
你好,虽然你可以在网上找到一些关于这个的好文章,但我仍然会尝试回答这个问题。
假设你给一个按钮一个由单个矩形组成的非常简单的外观,并提供一段简单的文本作为内容即使有这样基本的视觉效果,仍然存在两个元素:文本和矩形。按钮应该无论鼠标是在文本还是矩形上,都响应鼠标单击。在标准 .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 遍历 用户界面元素树,调用附加到的路由事件的所有处理程序 从原始元素一直到用户界面树根的任何节点。
【讨论】:
该答案阐明了对冒泡/隧道的需求,这是我的问题的标题。但是,我仍然对我们为什么要使用 MouseUp 或 MouseDown 感到困惑,因为它不是像 Click 这样的决定性操作。 MousewheelUp/Down 我可以看到它有用的场景。但是在 MouseUp 的情况下,我们改变了一些东西,用户离开按钮,没有点击......我们会在哪里使用它?【参考方案3】:正如你所说,你知道冒泡/隧道的概念,所以不要讨论这个。但这就是这些事件的目的,即它们让您知道控件或其子项上的鼠标按钮是向下还是向上。事件工作正常。对于您的场景,您应该使用按钮的 Click
事件告诉如果鼠标在按钮本身上上下移动。
谢谢
【讨论】:
点击将是一个直接事件。我的困惑是关于冒泡/隧道事件的使用以上是关于WPF 中的隧道和冒泡事件何时有用?的主要内容,如果未能解决你的问题,请参考以下文章