John
John

Reputation: 6278

C# wpf grid click event without two callbacks

Right now I am detecting clicks in my grid by using the MouseLeftButtonUp event.

However, whenever I open a OpenFileDialog and select a file. The mouse up event goes through the dialog box and registers a mouse up event on my grid when it shouldn't.

How can I detect a valid mouse click (mouse down and mouse up) on my grids without having to create multiple callbacks to track if my element was actually clicked and not happened to mouse up while over it?

Example:

System.Windows.Forms.OpenFileDialog o;


o = new System.Windows.Forms.OpenFileDialog();

if (o.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{

}

When I double click a file in the dialog and my grid window is behind it. The mouse up event is called.

Upvotes: 1

Views: 1472

Answers (1)

walterlv
walterlv

Reputation: 2376

There is no easy way to handle a Click event without two callbacks on a Grid. But we can write some helper code to fulfill it.

This is how you can use my helper code:

<Grid Background="Transparent">
    <local:RoutedEventExtension.Event>
        <local:ClickEvent Click="Grid_Click"></local:ClickEvent>
    </local:RoutedEventExtension.Event>
</Grid>

XAML above.

private void Grid_Click(object sender, EventArgs e)
{
    // Write your event handler code here.
}

C# above.

This is my helper code:

public abstract class RoutedEventExtension
{
    public static readonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached(
        "Event", typeof(RoutedEventExtension), typeof(RoutedEventExtension),
        new PropertyMetadata(null, OnEventChanged));

    public static void SetEvent(DependencyObject element, RoutedEventExtension value)
    {
        element.SetValue(EventProperty, value);
    }

    public static RoutedEventExtension GetEvent(DependencyObject element)
    {
        return (RoutedEventExtension) element.GetValue(EventProperty);
    }

    private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is IInputElement element))
        {
            throw new InvalidOperationException("RoutedEventExtension can only be attached on an IInputElement.");
        }

        var oldValue = (RoutedEventExtension) e.OldValue;
        var newValue = (RoutedEventExtension) e.NewValue;

        oldValue?.Detach();
        newValue.Attach(element);
    }

    protected IInputElement Target { get; private set; }

    private void Attach(IInputElement target)
    {
        Target = target;
        OnAttached();
    }

    private void Detach()
    {
        try
        {
            OnDetaching();
        }
        finally
        {
            Target = null;
        }
    }

    protected abstract void OnAttached();

    protected abstract void OnDetaching();
}

public sealed class ClickEvent : RoutedEventExtension
{
    public event EventHandler Click;

    protected override void OnAttached()
    {
        Target.MouseLeftButtonDown += OnMouseLeftButtonDown;
        Target.MouseLeftButtonUp += OnMouseLeftButtonUp;
        Target.LostMouseCapture += OnLostMouseCapture;
    }

    protected override void OnDetaching()
    {
        Target.MouseLeftButtonDown -= OnMouseLeftButtonDown;
        Target.MouseLeftButtonUp -= OnMouseLeftButtonUp;
        Target.LostMouseCapture -= OnLostMouseCapture;
    }

    private bool _isMouseDown;

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        _isMouseDown = true;
        Mouse.Capture(Target);
    }

    private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!_isMouseDown)
        {
            return;
        }

        Mouse.Capture(null);
        OnClick();
    }

    private void OnLostMouseCapture(object sender, MouseEventArgs e)
    {
        _isMouseDown = false;
    }

    private void OnClick()
    {
        Click?.Invoke(this, EventArgs.Empty);
    }
}

The helper code consists of two class. One to provide a common way to attach any event on an IInputElement, the other to provide a basic Click implementation.

You can write your own other event implementation yourself by inheriting from RoutedEventExtension.

Upvotes: 3

Related Questions