14

我了解冒泡和隧道的工作原理。但是,我对使用它们感到困惑。原因如下:

我想处理鼠标点击事件。要冒泡,有MouseDown,要挖隧道,有PreviewMouseDown。但是,MouseDown并不一定意味着用户单击了控件。可能是用户按下按钮并离开它以取消点击。如果未单击按钮,我不想更改任何内容。

所以我的问题是,冒泡/隧道策略有什么用?

4

3 回答 3

8

如果事件被列出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 事件的帮助下。

欲了解更多信息,请参阅:

了解 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

在此处输入图像描述

于 2013-09-04T05:15:23.660 回答
3

嗨,虽然你可以在网上得到一些关于这个的好文章,但我仍然会尝试回答这个问题。

假设你给一个按钮一个由单个矩形组成的非常简单的外观,并提供一段简单的文本作为内容即使有这样的基本视觉效果,仍然存在两个元素:文本和矩形。按钮应该响应鼠标单击鼠标是在文本还是矩形上。在标准的 .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 不只是调用附加到引发事件的元素的处理程序,而是遍历用户界面元素树,调用附加到任何节点的路由事件的所有处理程序,从原始元素一直到用户界面树的根。

于 2013-09-04T04:36:45.247 回答
1

正如你所说,你知道冒泡/隧道的概念,所以不要进入那个。但这就是这些事件的目的,即它们让您知道控件或其子项上的鼠标按钮是向下还是向上。事件工作正常。对于您的场景,您应该使用Click按钮的事件来判断鼠标是否在按钮本身上下。

谢谢

于 2013-09-04T03:18:16.407 回答