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

许多标准控件监听事件,例如KeyDownMouseDown等。例如-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 中的隧道和冒泡事件何时有用?的主要内容,如果未能解决你的问题,请参考以下文章

WPF-12 路由事件之二

WPF Demo511 控件共用事件

JS 事件冒泡防止复选框选中状态

WPF中路由事件的传播

wpf中combox的点击事件是啥

WPF TextCompositionManager 事件说明