Event for when a Button is held clicked for some time

4k Views Asked by At

I am simulating a handheld device with WPF and MVVM (viewmodel is a state-machine, view is a simulated plastic case with buttons).

The gesture that turns the device on and off is a "long click" in a button. For exemple, during use, if I press "OK" button, it displays some screen, but if I HOLD it clicked for more than three seconds, it should (in a simulated way) turn the device off.

I took a look in RepeatButton, with its Delay and Interval properties, but those seem to fire the same Click event. What I need is to fire a regular Click if I hold the button for less than three seconds, and fire another, different LongClick (possibly once) if I hold it for more than three seconds.

How can I do this, using RepeatButton or even a regular Button?

2

There are 2 best solutions below

7
furkle On

One way you could do this would be to bind to the MouseDown and MouseUp events. Use something like a Stopwatch that gets started on MouseDown and check the amount of time that's elapsed on MouseUp. If it's less than 3 seconds, do your Click() action. If it's more than 3 seconds, do your LongClick() action.

private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    stopwatch = new Stopwatch();
    stopwatch.Start();
}

private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    stopwatch.Stop();

    if (stopwatch.ElapsedMilliseconds >= 3000)
    {
         // do Click()
    }
    else
    {
        // do LongClick
    }
}

And here's a solution for RepeatButton:

private bool isLongClick;
private bool hasAlreadyLongClicked;
private void RepeatButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    isLongClick = false;
    hasAlreadyLongClicked = false;
    stopwatch = new Stopwatch();
    stopwatch.Start();
}

private void RepeatButton_Click(object sender, RoutedEventArgs e)
{
    if (!hasAlreadyLongClicked && stopwatch.ElapsedMilliseconds >= 3000)
    {
        hasAlreadyLongClicked = true;
        isLongClick = true;
        // do your LongClick action
    }
}

private void RepeatButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    if (!isLongClick)
    {
        // do short click action
    }
}

The trick here is that a RepeatButton is basically just a Button that fires Click at every interval. So, if we start the Stopwatch on PreviewMouseDown for the button, we can check the elapsed time on the stopwatch every time the Click event fires, and modify our action based on the result.

0
heltonbiker On

After a weekend's meditation, I devised the following, working solution:

  1. Create two boolean flags in ViewModel: ClickedShort and ClickedLong;
  2. Binding the Command property on button, via commanding, to OK method on ViewModel;
  3. The OK command is like this:

    public void OK()
    {
        if (!ClickedShort)
        {
            HandleShortClick();
            ClickedShort = true;
        }
        else if (!ClickedLong)
        {
            HandleLongClick();
            ClickedLong = true;
        }
    }    
    
  4. Have two CheckBoxes in XAML, with IsChecked properties bound to the boolean flags. These are just relays so that I can send information from View to ViewModel via binding:

        <CheckBox x:Name="ClickedShort" IsChecked="{Binding ClickedShort, Mode=OneWayToSource}" Visibility="Collapsed"/>
        <CheckBox x:Name="ClickedLong" IsChecked="{Binding ClickedLong, Mode=OneWayToSource}" Visibility="Collapsed"/>
    
  5. Have a MouseUp event in OK button to reset those flags to false when I release the button.