Simple WPF: custom event implementation long press short press

Keywords: C# WPF

preface

It was new before Simple WPF column To record some content in the blogger's WPF learning process. This blog post is followed by the first one Simple WPF: Customize button appearance , implements a custom long and short press event button.

Github project update address: https://github.com/mrchipset/simple-wpf.git

Functional requirements and Implementation

  • Able to respond to long and short press
  • Able to respond to long press release events
  • Ability to bind routing events in xaml files

According to the above requirements, we need to create a C# class of LongPressButton to inherit the Button instead of binding MouseLeftDown, MouseLeftUp and other related routing events in the xaml event parameter, because the Button parent class implements these routing responses in order to realize the function of Click and sets them to have been processed. Therefore, according to the broadcast wit of RoutedEvent, binding the responses of these events on the instance of Button class can not realize the processing of events.

Therefore, it is a little different from the previous blog post. We need to write the code of C# a little, inherit the Button class, and overload its Button to respond to events.

No more nonsense, just look at the following event handling code. Firstly, we overloaded the mouse press and lift events of the inherited parent Button, and introduced a Timer to record the time as the distinction between long press and short press. If the Button is released before the Timer expires, long press monitoring will stop.

public class LongPressButtonEx : Button
{
    private DispatcherTimer _pressDispatcherTimer;

    private void OnDispatcherTimeOut(object sender, EventArgs e)
    {
        _pressDispatcherTimer?.Stop();
        Debug.WriteLine($"Timeout {LongPressTime}");
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        Debug.WriteLine("Button: Mouse down.");
        if (_pressDispatcherTimer == null)
        {
            _pressDispatcherTimer = new DispatcherTimer();
            _pressDispatcherTimer.Tick += OnDispatcherTimeOut;
            _pressDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, LongPressTime);
            _pressDispatcherTimer.Start();
            Debug.WriteLine("Button: Timer started");
        }
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
        Debug.WriteLine("Button: Mouse up.");
        _pressDispatcherTimer?.Stop();
        _pressDispatcherTimer = null;
    }

}

Operation effect

...
# click
Button: Mouse down.
Button: Timer started
Button: Mouse up.
# Long press
Button: Mouse down.
Button: Timer started
Timeout 200
Button: Mouse up.

We have slightly modified this code to add our own routing event LongPressEvent

/// <summary>
/// LongPress Routed Event
/// </summary>
public static readonly RoutedEvent LongPressEvent
    = EventManager.RegisterRoutedEvent("LongPress",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(LongPressButtonEx));

public event RoutedEventHandler LongPress
{
    add => AddHandler(LongPressEvent, value);
    remove => RemoveHandler(LongPressEvent, value);
}

private void OnDispatcherTimeOut(object sender, EventArgs e)
{
    _pressDispatcherTimer?.Stop();
    Debug.WriteLine($"Timeout {LongPressTime}");
    RaiseEvent(new RoutedEventArgs(LongPressEvent));    // raise the long press event
}

Then create a new LongPressButtonEx instance in the Xaml code of the form and handle the long press event

<local:LongPressButtonEx Height="48" Width="256" LongPressTime="200"
        LongPress="LongPressButtonEx_LongPress"
        Click="LongPressButtonEx_Click">
    Click or Long Press Me!
</local:LongPressButtonEx>

Corresponding event handling code

private void LongPressButtonEx_LongPress(object sender, RoutedEventArgs e)
{
    if (sender is LongPressButtonEx btn)
    {
        btn.Content = "Long Pressed";
    }
}

private void LongPressButtonEx_Click(object sender, RoutedEventArgs e)
{
    if (sender is LongPressButtonEx btn)
    {
        btn.Content = "Clicked";
    }
}

Experimental effect

It is found that both Click and LongPress can respond, but Click is programmed when the button is released because the default Click event is responded to when the mouse is released

Now make a slight modification to the default OnClick function of the button control, so that there is no problem with Click

/// <summary>
/// DependencyProperty for IsLongPress 
/// </summary>
public static readonly DependencyProperty IsLongPressProperty
    = DependencyProperty.Register("IsLongPress", typeof(bool),
        typeof(LongPressButtonEx), new PropertyMetadata(false));

public bool IsLongPress
{
    set => SetValue(IsLongPressProperty, value);
    get => (bool)GetValue(IsLongPressProperty);
}

private void OnDispatcherTimeOut(object sender, EventArgs e)
{
    IsLongPress = true;
    _pressDispatcherTimer?.Stop();
    Debug.WriteLine($"Timeout {LongPressTime}");
    RaiseEvent(new RoutedEventArgs(LongPressEvent));    // raise the long press event
}

protected override void OnClick()
{
    if (!IsLongPress)
    {
        base.OnClick();
    }
    else
    {
        RaiseEvent(new RoutedEventArgs(LongPressReleaseEvent));    // raise the long press event
        IsLongPress = false;
    }
}

The modified effect realizes the special effect of long press and short press, which depends on the customized DependencyProperty named IsLongPress.

  See Github warehouse for specific effects

Reference link

  1. ​​​​​​​UIElement.MouseLeftButtonDown Event
  2. User control custom DependencyProperty property property tutorial
  3. Dispatcher timer timer in WPF
  4. How to: create custom routing events
  5. WPF custom routing events with custom parameters
  6. Use WPF Style in another assemble

Posted by Brandito520 on Tue, 23 Nov 2021 05:32:52 -0800