Button.MouseDown
Asked Answered
J

4

8

I'm relatively new with WPF. I'm trying to understand the difference between MouseDownEvent and PreviewMouseDownEvent.

I understand the WPF event strategies and i understand that the MouseDown event is a bubbling event and the PreviewMouseDown is a tunneling event.

I also understand the order of which these events are being fired - according to this MSDN overview http://msdn.microsoft.com/en-us/library/ms742806.aspx#routing (there is a diagram with example there).

So i tried to code some my self, check this for example:

<Grid x:Name="grid" Width="250">
    <StackPanel Mouse.MouseDown="StackPanel_MouseDown" PreviewMouseDown="StackPanel_PreviewMouseDown">
    <WPFVisualizerExample:MyButton x:Name="B1" PreviewMouseDown="B1_PreviewMouseDown" MouseDown="B1_MouseDown" Margin="5,5,5,5">
            <WPFVisualizerExample:MyButton x:Name="B2" PreviewMouseDown="B2_PreviewMouseDown" MouseDown="B2_MouseDown"  Margin="5,5,5,5">
                <WPFVisualizerExample:MyButton x:Name="B3" PreviewMouseDown="B3_PreviewMouseDown" MouseDown="B3_MouseDown"  Margin="5,5,5,5">Click Me</WPFVisualizerExample:MyButton>
            </WPFVisualizerExample:MyButton>
    </WPFVisualizerExample:MyButton>           
    </StackPanel>        
</Grid>

I have an event handler for each of the events (the preview and non-preview) and i wanted to see what is happening, which of the event is being thrown (i have a message box shown for each event).

The 'MyButton' user control simply extends the base Button and override the OnMouseDown and OnPreviewMouseDown to set the e.Handled false:

    protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
    {            
        base.OnMouseDown(e);
        e.Handled = false;
    }

    protected override void OnPreviewMouseDown(System.Windows.Input.MouseButtonEventArgs e)
    {            
        base.OnPreviewMouseDown(e);
        e.Handled = false;
    }

(tried with this and without this).

According to the MSDN overview (in the link above), if i have 3 elements then the events route should be as follows:

PreviewMouseDown (tunnel) on root element.

PreviewMouseDown (tunnel) on intermediate element #1.

PreviewMouseDown (tunnel) on source element #2.

MouseDown (bubble) on source element #2.

MouseDown (bubble) on intermediate element #1.

MouseDown (bubble) on root element.

So I expected the the message boxes to be shown according to the above. From some reason - which I don't understand only the preview events are being thrown (according to what the MSDN says Preview_B1=>Preview_B2=>Preview_B3). My expectations were: Preview_B1=>Preview_B2=>Preview_B3=>NonPreview_B3=>NonPreview_B2=>NonPreview_B1.

But the non-preview events are not being thrown at all.

So basically I don't understand the route of the events, from MSDN overview I understood that the route starts from the root element, goes down (tunnel) to the source element and then back up (bubble) to the root element, but this is not what happening in practice.

It is really important for me to understand how this events are working, i probably miss-understand something basic here, your help will be appreciated.

THANX!! -Gili

Juna answered 5/6, 2010 at 21:25 Comment(0)
T
7

Since the function of the Button control is to produce a Click event (or fire a Command) it treats the Mouse events a little differently than other controls. When Button receives a PreviewMouseDown event it converts it to a Click event, cancelling any further tunnelling of Preview and the MouseDown bubbling event. There is some variation in this depending on the ClickMode setting of the Button. If you'd rather use the Mouse events themselves you can just use a ContentControl instead (try it and you should see exactly what you expected) but you'll probably find it's much easier to use Button.Click in most cases.

UPDATE

There must be something else going on in your code that's causing the event routing to stop. Here's an example similar to yours that I verified that shows expected output (some styling added to make the elements obvious):

<Window.Resources>
    <Style TargetType="{x:Type ContentControl}">
        <Setter Property="BorderThickness" Value="5" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="Background" Value="PaleGreen" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ContentControl}">
                    <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <ContentPresenter/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<StackPanel Tag="Panel" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
    <ContentControl BorderBrush="Red" Tag="C1" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
        <ContentControl BorderBrush="Green" Tag="C2" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
            <ContentControl Content="Click Me" BorderBrush="Blue" Tag="C3" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown"/>
        </ContentControl>
    </ContentControl>
</StackPanel>

and simple handlers to write to Debug:

private void PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    Debug.WriteLine((sender as FrameworkElement).Tag + " Preview");
}

private void MouseDown(object sender, MouseButtonEventArgs e)
{
    Debug.WriteLine((sender as FrameworkElement).Tag + " Bubble");
}

Debug Output (if center text is clicked) is:

Panel Preview
C1 Preview
C2 Preview
C3 Preview
C3 Bubble
C2 Bubble
C1 Bubble
Panel Bubble

Twiggy answered 5/6, 2010 at 22:27 Comment(1)
Please see my answer above, i have changed the Button to ContentControl and i still get the same result. (I just want to understand how RoutedEvents work in WPF, the documentation seem to say one thing and in practice different things happen). THANX ALOT!!!Juna
B
2

I'm studying routed event concept nowadays and in my test applications the main difference is in the abusive usage of MessageBox.Show() method. It seems that when you use it to notify about the events being raised, at first all the tunelling PreviewMouseDown events are raised and the appropriate message boxes are shown, and the fact that they acquire focus aborts or handles the bubbling MouseDown events. In the given code example we can change the way of notification (e.g. Debug.WriteLine as in John's example or update some string dependency property binded to some TextBox.Text)

Billings answered 26/11, 2012 at 8:49 Comment(0)
D
0

Amatsu is right! I had exactly the same problem. I've created a button and registered the MouseDown and PreviewMouseDown-Event. In the eventhandler i've just displayed a messagebox for debugging-purposes. I was always wondering why i just get the PreviewMouseDown-Event, not the MouseDown-Event. The reason was the MessageBox. So i changed the MessageBox into the System.Diagnostic.WriteLine and everything worked fine:)

Thanks!

Deceased answered 18/2, 2013 at 17:34 Comment(0)
L
0

This question is old, but I'm working in the same area, and I may have an answer. John Bowen had no problem with this. He was testing using Debug trace statements. Those who are having problems with this are testing using MessageBoxes.

And that's the problem: A mouse down on a button sets the focus and captures the mouse. But if you display a MessageBox, it will steal the focus and the mouse capture. At that point, the button is really confused, because the mouse is down on it, but it doesn't have the capture or focus. After that, you wouldn't expect the normal progression of mouse messages. The mouse has been taken away!

Lilytrotter answered 28/8, 2017 at 19:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.