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

5

22

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

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.

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

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

    }
}
Twotone answered 19/3, 2009 at 13:29 Comment(0)
S
26

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.

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..

Singularize answered 19/3, 2009 at 13:54 Comment(6)
what does the button do with my mouse click? I have no click attribute in the element, I'm not handling the click event in the code behind, so how can we say the button is doing something with the mouse clicks?Twotone
True, but the default implementation of button is to absorbe your mouse event to turn it into a click event... even if you do nothing with the click...Singularize
Found this relevant information about the sequence of the events: msdn.microsoft.com/en-us/library/…Koal
Grid seems to absorb mouse events as well.Decedent
@EdwardTanguay "how can we say the button is doing something with the mouse clicks?" -- It invokes all the handlers of its Click event. It's not the Button's fault that you didn't attach any handlers to that event.Predacious
All this time I was under the impression wpf did away with the click event completely because I couldn't call it on a user control....Cristacristabel
P
8

As others have said, it's because the MouseDown event gets handled by the Button before it can be bubbled further. You can see this in Reflector, in 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 MouseDown event, and indicate that you don't care if the event is handled. You can do this with the AddHandler method. It has a boolean overload that lets you listen for events which are already handled.

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

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

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

Proffer answered 19/3, 2009 at 18:41 Comment(2)
what is "MouseDownEvent" ? RoutedEvent is a sealed class, so some object must carry instances of those?Portillo
Ah, found something. Mouse.AddXXX(DependencyObject, EventHandler);Portillo
W
7

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

<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.

Washerwoman answered 19/3, 2009 at 14:0 Comment(2)
hmm, I've seen the Button.Click in articles as well but my StackPanel doesn't have that attribute (?), the only similar attribute I get in intellisense is "ButtonBase.Click" which gives me "No overload for 'TheStackPanel_MouseDown' matches delegate 'System.Windows.RoutedEventHandler'", why is that?Twotone
Hmmm, it works for me with both ButtonBase.Click and Button.Click in the XAML (I believe Button.Click just doesn't show in intellisense - but it should work) - and with the specified event signature. Have you changed the MouseButtonEventArgs to RoutedEventArgs?Washerwoman
K
2

It is because all the messages are being captured handled by Button and messages stop the message stops bubbling 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 handled or 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:

"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?"

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

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.

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.

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

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

Koal answered 19/3, 2009 at 13:56 Comment(1)
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?Twotone
P
2

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

TheButton.AddHandler(
    UIElement.MouseDownEvent, 
    new MouseButtonEventHandler(TheStackPanel_MouseDown),
    true);
Palestine answered 16/1, 2013 at 20:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.