首页 文章

为什么按钮点击事件“冒泡视觉树”到StackPanel,如MSDN文章所述?

提问于
浏览
21

在MSDN文章Understanding Routed Events and Commands In WPF中,它说明

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

但是,在此示例中,单击按钮时, it doesn't "bubble up the visual tree" to get handled by the parent StackPanel event ,即单击按钮不会触发任何事件 .

为什么不? What do they mean then by "bubbling up" 若不是这个?

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>

code-behind:

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.";
        }

    }
}

5 回答

  • 2

    事件冒出来,直到它被处理完毕......

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

    如果您使用PreviewMouseDown,您会看到StackPanel首先在按钮之前接收到该事件 . 预览事件使用隧道向下方法 .

  • 8

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

    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);
    

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

  • 25

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

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

    和事件签名:

    private void TheStackPanel_MouseDown(object sender, RoutedEventArgs e)
    

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

  • 7

    这是因为所有消息都被Button处理并且消息停止消息停止冒泡 . 答案就在您的问题文本中:

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

    编辑:

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

    “我没有看到按钮IS正在处理事件,即按钮上没有单击处理程序,我在StackPanel上有一个单击处理程序(MouseDown),因此我认为它会冒泡PAST按钮按钮不处理它并由stackpanel处理,对吧?“

    你是对的 . Button没有处理MouseDown事件,因为没有为该控件指定处理程序 .

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

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

    供参考:请在http://blogs.msdn.com/jfoscoding/archive/2005/07/28/444647.aspx处查看"Windows Forms processing"部分(从页面中间稍微向下滚动) .

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

  • 2

    按钮事件抑制mousedown和mouseup因为按钮事件是高级事件并且有一些代码使得标志句柄为真这个原因抑制了mousdown来解决这个问题你可以在窗口的构造函数中添加这个代码

    TheButton.AddHandler(
        UIElement.MouseDownEvent, 
        new MouseButtonEventHandler(TheStackPanel_MouseDown),
        true);
    

相关问题