我了解冒泡和隧道的工作原理。然而,我对如何使用它们感到困惑。
这是为什么:
我想处理鼠标单击事件。要冒泡它,有MouseDown
,要隧道它,有PreviewMouseDown
。然而,MouseDown
并不一定意味着用户单击了控件。也许用户按下按钮并移开以取消单击。如果按钮没有被点击,我就不想改变任何东西。
因此,我的问题是,冒泡和隧道策略有什么用处?
我了解冒泡和隧道的工作原理。然而,我对如何使用它们感到困惑。
这是为什么:
我想处理鼠标单击事件。要冒泡它,有MouseDown
,要隧道它,有PreviewMouseDown
。然而,MouseDown
并不一定意味着用户单击了控件。也许用户按下按钮并移开以取消单击。如果按钮没有被点击,我就不想改变任何东西。
因此,我的问题是,冒泡和隧道策略有什么用处?
如果事件列在RoutedEventArgs
中,则为路由事件。路由事件支持冒泡、隧道或直接的RoutingStrategy。让我们来看一下Button.Click
的事件处理程序:
private void Grid_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button Test clicked!");
}
这里指定了RoutedEventArgs
,所以它是一个路由事件。因为名称中没有指定preview,所以这是一个冒泡事件。这可以通过以下方式进行演示:
<Grid ButtonBase.Click="Grid_Click">
<Button Name="TestButton" Width="100" Height="30" Content="Test" />
</Grid>
TestButton
,事件会升级到 Grid
以上,并显示一条消息:
Button Test clicked!
Bubbling/Tunneling 策略的使用价值
Tunneling
许多标准控件都会监听事件,例如 KeyDown
, MouseDown
等。例如-DataGrid
控件。我希望按下回车键时调用添加记录功能。但是,DataGrid
已经有了 KeyDown
事件,因此该事件不会被触发。因此,您必须在 Tunnel 事件 - PreviewKeyDown
中执行逻辑,它将在 KeyDown
事件之前起作用。对于 RichTextBoxControl
同样适用。
Bubbling
有时,您需要针对特定事件的全局处理程序,以使其适用于 VisualTree 中的所有控件。自然地,您不能直接执行事件。因此,Bubbling 事件登场了。Button
可包含任何东西: Image
、其他 Button
等:
Button
中的 TextBlock/Image
。我们如何知道单击是在 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);
}
点击 按钮
后的结果:
你好,虽然你可以在网上找到一些关于这方面的好文章,但我还是会尝试回答这个问题。
假设您给一个按钮一个非常简单的外观,由一个矩形组成,并提供一个简单的文本作为内容。即使具有基本可视化元素,仍然存在两个元素:文本和矩形。无论鼠标是否位于文本或矩形上,按钮都应该响应鼠标点击。在标准的.NET事件处理模型中,这意味着为这两个元素都注册一个MouseLeftButtonUp事件处理程序。
利用WPF的内容模型时,这个问题会变得更加严重。一个按钮不限于只有纯文本作为标题 - 它可以包含任何对象作为内容。下面的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>
你说你知道冒泡/隧道的概念,所以我们不再深入讨论。但这就是这些事件的目的,即它们可以让你知道鼠标按钮在控件或其子控件上是按下还是释放。事件运行良好。对于你的情况,你应该使用按钮的Click
事件,它会告诉你鼠标是在按钮本身上按下和释放的。
谢谢
Grid
设置了一个处理程序,而不是为Button
设置,当点击Button
时,事件对Grid
起作用。事件在Button
上向上移动,并来到Grid
。据我所知,直接单击事件已经存在于*WinForms
*中。 - Anatoliy Nikolaevpreview
*,则为隧道事件。您可以查看我在答案中提供给您的文章。 - Anatoliy Nikolaev