How to prevent RoutedEvent Tunnel/Bubble to avoid double click in sibling in WPF?

74 views Asked by At

Below is the simple snippet of what I'm trying to achieve. Expectation:

  • Having two buttons one above another.
  • While single/double click on top button, top button should hide and bottom button should be visible.

Issue :

  • When I double click on the top button, top button hides, but it triggers MouseDoubleClick in bottom button. And I'm not sure how to avoid that.

     <Grid x:Name="innerGrid" >
         <Button x:Name="second"  Content="click" Width="100" Background="Red" MouseDoubleClick="Button_MouseDoubleClick" Click="second_Click"   Height="50"/>
         <Button x:Name="first"  Content="click" Width="200" Background="Green" MouseDown="Button_MouseDown"  Height="50"/>
     </Grid>
    
    
    
    
         private void Button_MouseDown(object sender, MouseButtonEventArgs e)
         {
             (sender as FrameworkElement).Visibility = Visibility.Collapsed;
         }
    
         private void Button_MouseDoubleClick(object sender, MouseButtonEventArgs e)
         {
             (sender as Button).Background = Brushes.Yellow;
         }
    
         private void second_Click(object sender, RoutedEventArgs e)
         {
             second.Background = Brushes.Red;
             first.Visibility = Visibility.Visible;
         }
    

I have tried putting e.Handled as true in Preview and Bubble events but not able to get the expected behavior.

Please let me know how to achieve that.

Thanks.

1

There are 1 answers

0
Keithernet On

Here is an approach that should fix your issues and work the way you want. However it is a bit of a hack, and you lose the pressed state of the buttons.

The overall idea is to use PreviewMouseDown events and DispatcherTimers to keep single and double-clicks separate for each button.

The first button just needs to process a single-click and make sure not to propagate a double-click. Because it's timer waits for all clicks within 200ms, we can easily do that.

The second button is unique in that it needs to separately process a double-click and a single-click. We use a separate 200ms timer that will tally up the click count and then will process the proper code.

Here is the edited XAML:

<Grid
    x:Name="innerGrid"
    HorizontalAlignment="Center"
    VerticalAlignment="Center">
    <Button
        x:Name="second"
        Width="100"
        Height="50"
        Background="Red"
        Content="click"
        PreviewMouseDown="Second_OnPreviewMouseDown" />
    <Button
        x:Name="first"
        Width="200"
        Height="50"
        Background="Green"
        Content="click"
        PreviewMouseDown="First_OnPreviewMouseDown" />
</Grid>

Here is the code-behind:

public partial class MainWindow : Window
{
    private readonly DispatcherTimer firstClickTimer;
    private readonly DispatcherTimer secondClickTimer;

    private int firstClickCount;
    private int secondClickCount;

    public MainWindow()
    {
        InitializeComponent();

        firstClickTimer = new DispatcherTimer
        {
            Interval = TimeSpan.FromMilliseconds(200)
        };

        firstClickTimer.Tick += FirstClickTimerTick;

        secondClickTimer = new DispatcherTimer
        {
            Interval = TimeSpan.FromMilliseconds(200)
        };

        secondClickTimer.Tick += SecondClickTimerTick;
    }

    private void FirstClickTimerTick(object sender, EventArgs e)
    {
        firstClickTimer.Stop();

        Debug.WriteLine("FIRST CLICK!");
        first.Visibility = Visibility.Collapsed;
    }

    private void SecondClickTimerTick(object sender, EventArgs e)
    {
        secondClickTimer.Stop();

        if (secondClickCount == 1)
        {
            Debug.WriteLine("SECOND CLICK!");
            second.Background = Brushes.Red;
            first.Visibility = Visibility.Visible;
        }

        if (secondClickCount == 2)
        {
            Debug.WriteLine("SECOND DOUBLE-CLICK!");
            second.Background = Brushes.Yellow;
        }
    }

    private void First_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        e.Handled = true;

        if (!firstClickTimer.IsEnabled)
        {
            firstClickTimer.Start();
        }

        firstClickCount = e.ClickCount;
    }

    private void Second_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        e.Handled = true;

        if (!secondClickTimer.IsEnabled)
        {
            secondClickTimer.Start();
        }

        secondClickCount = e.ClickCount;
    }
}