C# 为什么按钮单击事件不会像 MSDN 文章所说的那样将可视化树“冒泡”到 StackPanel?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/662201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 12:21:22  来源:igfitidea点击:

Why doesn't button click event "bubble up visual tree" to StackPanel as MSDN article states?

c#wpfevent-routing

提问by Edward Tanguay

In the MSDN article Understanding Routed Events and Commands In WPF, it states

在 MSDN 文章了解 WPF 中的路由事件和命令中,它指出

an event will bubble (propagate) up the visual tree from the source element until either it has been handled or it reaches the root element.

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

However, in this example, when you click the button, it doesn't "bubble up the visual tree" to get handled by the parent StackPanel event, i.e. clicking on the button fires no event.

但是,在本例中,当您单击按钮时,它不会“使可视树冒泡”以得到父 StackPanel 事件的处理,即单击按钮不会触发任何事件。

Why not? What do they mean then by "bubbling up"if not this?

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

XAML:

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

    }
}

采纳答案by Arcturus

The event bubbles up, until it gets handled...

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

Since the Button does something with your mouse clicks, it absorbs your mouse event and turns it into a ClickEvent.

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

If you use the PreviewMouseDown, you see that the StackPanel first receives the event before the button does.. Preview events use the Tunnel down approach..

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

回答by vmarquez

It is because all the messages are being captured handledby Button and messages stop the message stopsbubbling there. The answer is right in your question's text:

这是因为所有的消息都被抓获处理由按钮和消息停止消息停止冒泡那里。答案就在您的问题文本中:

An event will bubble (propagate) up the visual tree from the source element until either it has been handledor it reaches the root element.

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

EDIT:

编辑:

Edward Tanguay (OP) commented on this answer and I am copying his comment here because it is very relevant:

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

"I don't see that the button IS handling the event, i.e. I have no click handler on the button, I DO have a click handler (MouseDown) on the StackPanel and hence I would think it would bubble up PAST the button since the button doesn't handle it and get handled by the stackpanel which does, right?"

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

You are right. Button is not handling the MouseDown event because no handler ha been specified for it at that control.

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

But, then, MouseDown is particular in some way. At least in Windows Forms it is used to initiate actions as drawing and dragging so, when a control gets the event, it proceeds to trap all the subsequent mouse messages even if you have not defined handlers for it. This trap is done when the control sets Capture property to True and this effectively stop subsequent events from being bubbled up. Capture property is set back to False by Windows Forms when it gets a MouseUp event.

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

I repeat, this is the way it works in Windows Forms, you may want to double-check this but, IMHO there is no reason why this should be different for WPF.

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

For reference: Se the section "Windows Forms processing" at http://blogs.msdn.com/jfoscoding/archive/2005/07/28/444647.aspx(scroll slightly down from the middle of the page).

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

Note: See my comment to Arcturu's answer for a reference on bubble and tunneling events raise sequences.

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

回答by Razzie

In addition, if you want the stackpanel to receive the event, change the stackpanel xaml to:

另外,如果想让stackpanel接收事件,把stackpanel的xaml改成:

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

and the event signature to:

和事件签名:

private void TheStackPanel_MouseDown(object sender, RoutedEventArgs e)

In this case, the stackpanel will recieve the button's click event. However, clicking on the stackpanel itself won't fire any event, since it listens specifically to a button click.

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

回答by Robert Macnee

As others have said, it's because the MouseDownevent gets handled by the Buttonbefore it can be bubbled further. You can see this in Reflector, in ButtonBase.OnMouseLeftButtonDown:

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

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

One solution is to listen for a MouseDownevent, and indicate that you don't care if the event is handled. You can do this with the AddHandlermethod. It has a boolean overload that lets you listen for events which are already handled.

一种解决方案是侦听MouseDown事件,并表明您不关心该事件是否得到处理。你可以用这个AddHandler方法来做到这一点。它有一个布尔重载,可让您侦听已处理的事件。

If you do this somewhere instead of setting the MouseDown handler in XAML:

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

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

You'll receive all MouseDownevents on TheStackPanel, regardless of whether they've been handled.

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

回答by Mohamed Elsayed Ali

button event Suppress the mousedown and mouseup because button event is high level event and have bit of code that give flag handle true this cause Suppressed for mousdown to resolve this problem you can add this code in constructor of window

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

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