22

在 MSDN 文章Understanding Routed Events and Commands In WPF中,它指出

一个事件将从源元素向上冒泡(传播)到可视化树,直到它被处理或到达根元素。

但是,在此示例中,当您单击按钮时,它不会“冒泡视觉树”以由父 StackPanel 事件处理,即单击按钮不会触发任何事件。

为什么不?如果不是这个,他们所说的“冒泡”是什么意思?

XAML:

<Window x:Class="TestClickEvents456.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel x:Name="TheStackPanel"
                Background="Yellow"
                MouseDown="TheStackPanel_MouseDown">
        <Button x:Name="TheButton"
                Margin="10"
                Content="Click This"/>
        <TextBlock x:Name="TheMessage"
                   Text="Click the button or the yellow area"/>
    </StackPanel>
</Window>

代码隐藏:

using System.Windows;
using System.Windows.Input;

namespace TestClickEvents456
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void TheStackPanel_MouseDown(object sender, MouseButtonEventArgs e)
        {
            TheMessage.Text = "StackPanel was clicked.";
        }

    }
}
4

5 回答 5

25

事件冒泡,直到它得到处理......

由于 Button 通过您的鼠标单击执行某些操作,因此它会吸收您的鼠标事件并将其转换为 ClickEvent。

如果您使用 PreviewMouseDown,您会看到 StackPanel 先于按钮接收事件。预览事件使用 Tunnel down 方法。

于 2009-03-19T13:54:19.927 回答
8

正如其他人所说,这是因为事件在可以进一步冒泡之前MouseDown得到处理。Button您可以在 Reflector 中看到这一点ButtonBase.OnMouseLeftButtonDown

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
    if (this.ClickMode != ClickMode.Hover)
    {
        e.Handled = true;
        // SNIP...
    }
    base.OnMouseLeftButtonDown(e);
}

一种解决方案是侦听MouseDown事件,并表明您不在乎是否处理了该事件。您可以使用该AddHandler方法执行此操作。它有一个布尔重载,可让您侦听已处理的事件。

如果您在某处执行此操作而不是在 XAML 中设置 MouseDown 处理程序:

TheStackPanel.AddHandler(MouseDownEvent, new MouseButtonEventHandler(TheStackPanel_MouseDown), true);

您将收到MouseDown上的所有事件TheStackPanel,无论它们是否已被处理。

于 2009-03-19T18:41:14.020 回答
7

此外,如果您希望 stackpanel 接收事件,请将 stackpanel xaml 更改为:

<StackPanel x:Name="TheStackPanel" 
            Background="Yellow"
            Button.Click="TheStackPanel_MouseDown" />

和事件签名:

private void TheStackPanel_MouseDown(object sender, RoutedEventArgs e)

在这种情况下,stackpanel 将接收按钮的单击事件。但是,单击堆栈面板本身不会触发任何事件,因为它专门侦听按钮单击。

于 2009-03-19T14:00:18.727 回答
2

这是因为所有消息都由Button处理,并且消息停止消息停止冒泡。答案在您的问题文本中是正确的:

一个事件将从源元素向上冒泡(传播)到可视化树,直到它被处理或到达根元素。

编辑:

Edward Tanguay(OP)评论了这个答案,我在这里复制他的评论,因为它非常相关:

“我没有看到按钮正在处理事件,即我在按钮上没有点击处理程序,我在 StackPanel 上有一个点击处理程序(MouseDown),因此我认为它会冒泡 PAST 按钮,因为按钮不处理它并由堆栈面板处理,对吗?”

你说的对。Button 未处理 MouseDown 事件,因为在该控件上没有为它指定处理程序。

但是,MouseDown 在某些方面是特别的。至少在 Windows 窗体中,它用于启动绘制和拖动等操作,因此,当控件获取事件时,即使您尚未为其定义处理程序,它也会继续捕获所有后续鼠标消息。此陷阱在控件将 Capture 属性设置为 True 时完成,这有效地阻止了后续事件冒泡。Windows 窗体在收到 MouseUp 事件时将 Capture 属性设置回 False。

我再说一遍,这就是它在 Windows 窗体中的工作方式,您可能想仔细检查一下,但是恕我直言,没有理由为什么 WPF 应该有所不同。

供参考:请参阅http://blogs.msdn.com/jfoscoding/archive/2005/07/28/444647.aspx上的“Windows 窗体处理”部分(从页面中间稍微向下滚动)。

注意:有关气泡和隧道事件引发序列的参考,请参阅我对 Arcturu 的回答的评论。

于 2009-03-19T13:56:40.170 回答
2

按钮事件抑制 mousedown 和 mouseup 因为按钮事件是高级事件并且有一些代码可以提供标志句柄 true 这导致抑制 mousdown 以解决此问题您可以在窗口的构造函数中添加此代码

TheButton.AddHandler(
    UIElement.MouseDownEvent, 
    new MouseButtonEventHandler(TheStackPanel_MouseDown),
    true);
于 2013-01-16T20:02:18.127 回答