5

I've posted another question of how to 'manually' capture a double-tap by monitoring a timespan between touches on a TouchDown event, but it's quite buggy. Does anyone know of a standard Microsoft way/event of capturing double-tap on a multi-touch screen?

Thanks a lot,

Dan

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
  • 1
    did you try the timer implementation I posted? http://stackoverflow.com/a/9000217/303612 – Dr. Andrew Burnett-Thompson Jan 25 '12 at 10:28
  • Yes I tried this. The problem was that I was opening a window on Double-Tap, but this window was being hidden. I sort of used your concept and incorporated a .Activate() method call and it's all sorted. thanks a lot mate. –  Jan 25 '12 at 10:46

4 Answers4

11

I check the combination of the tap location and a stopwatch, and it works perfect!

private readonly Stopwatch _doubleTapStopwatch = new Stopwatch();
private Point _lastTapLocation;

public event EventHandler DoubleTouchDown;

protected virtual void OnDoubleTouchDown()
{
    if (DoubleTouchDown != null)
        DoubleTouchDown(this, EventArgs.Empty);
}

private bool IsDoubleTap(TouchEventArgs e)
{
    Point currentTapPosition = e.GetTouchPoint(this).Position;
    bool tapsAreCloseInDistance = currentTapPosition.GetDistanceTo(_lastTapLocation) < 40;
    _lastTapLocation = currentTapPosition;

    TimeSpan elapsed = _doubleTapStopwatch.Elapsed;
    _doubleTapStopwatch.Restart();
    bool tapsAreCloseInTime = (elapsed != TimeSpan.Zero && elapsed < TimeSpan.FromSeconds(0.7));

    return tapsAreCloseInDistance && tapsAreCloseInTime;
}

private void OnPreviewTouchDown(object sender, TouchEventArgs e)
{
    if (IsDoubleTap(e))
        OnDoubleTouchDown();
}

It checks in the PreviewTouchDown whether or not it's an DoubleTap.

Jowen
  • 5,203
  • 1
  • 43
  • 41
  • 1
    I Couldn't find `GetDistanceTo` under `System.Windows.Point`, so I've used [this](http://www.csharpdeveloping.net/Snippet/How_to_get_distance_between_two_points) – itsho May 13 '14 at 18:36
  • I also couldn't find `GetDistance` used `Math.Abs(Point.Subtract(currentTapPosition, _lastTapLocation).Length) < 40;` – 00jt Mar 28 '18 at 14:07
3

Jowens answer helped me a lot (if my reputation would let me, I´ll upvote it ;)) BUT I had to adjust it, so it worked with double taps only. The original code does consider any tap above 2 to be a double tap. Changing the _lastTapLocation to a nullable and resetting it when it´s a double tap helped.

private Point? _lastTapLocation;

private bool IsDoubleTap(TouchEventArgs e)
    {
        Point currentTapPosition = e.GetTouchPoint(this).Position;
        bool tapsAreCloseInDistance = false;
        if (_lastTapLocation != null)
        {
            tapsAreCloseInDistance = GetDistanceBetweenPoints(currentTapPosition, (Point)_lastTapLocation) < 70;
        }
        _lastTapLocation = currentTapPosition;

        TimeSpan elapsed = _doubleTapStopwatch.Elapsed;
        _doubleTapStopwatch.Restart();
        bool tapsAreCloseInTime = (elapsed != TimeSpan.Zero && elapsed < TimeSpan.FromSeconds(0.7));

        if (tapsAreCloseInTime && tapsAreCloseInDistance)
        {
            _lastTapLocation = null;
        }
        return tapsAreCloseInDistance && tapsAreCloseInTime;
    }
Ben
  • 925
  • 5
  • 11
1

I think utilizing the StylusSystemGesture event is more appropriate. Here my code.

    public static class ext
{
private static Point? _lastTapLocation;
        private static readonly Stopwatch _DoubleTapStopwatch = new Stopwatch();
        public static bool IsDoubleTap(this StylusSystemGestureEventArgs e, IInputElement iInputElement)
        {
            Point currentTapPosition = e.GetPosition(iInputElement);
            bool tapsAreCloseInDistance = false;
            if (_lastTapLocation != null)
            {
                tapsAreCloseInDistance = GetDistanceBetweenPoints(currentTapPosition, (Point)_lastTapLocation) < 70;
            }
            _lastTapLocation = currentTapPosition;

            TimeSpan elapsed = _DoubleTapStopwatch.Elapsed;
            _DoubleTapStopwatch.Restart();
            bool tapsAreCloseInTime = (elapsed != TimeSpan.Zero && elapsed < TimeSpan.FromSeconds(0.7));

            if (tapsAreCloseInTime && tapsAreCloseInDistance)
            {
                _lastTapLocation = null;
            }
            return tapsAreCloseInDistance && tapsAreCloseInTime;
        }
}

Usage:

 private void UIElement_OnStylusSystemGesture(object sender, StylusSystemGestureEventArgs e)
    {
        if (e.SystemGesture == SystemGesture.Tap)
        {
            if (e.IsDoubleTap(sender as IInputElement))
            {
               // Do your stuff here
            }
        }
    }
Andreas
  • 3,843
  • 3
  • 40
  • 53
1

Adding on to Andreas' answer, You may also be able to remove the Stopwatch-related code.

StylusSystemGestureEventArgs also comes with a Timestamp property, which I believe counts in milliseconds.

So instead of using the Stopwatch to calculate from the TimeSpan, add an int _lastTimestamp field and subtract the timestamps.

public static class ext
{
    private static Point? _lastTapLocation;

    // Stopwatch changed to int
    private int _lastTimestamp = 0;

    public static bool IsDoubleTap(this StylusSystemGestureEventArgs e, IInputElement iInputElement)
    {
        Point currentTapPosition = e.GetPosition(iInputElement);
        bool tapsAreCloseInDistance = false;
        if (_lastTapLocation != null)
        {
            tapsAreCloseInDistance = GetDistanceBetweenPoints(currentTapPosition, (Point)_lastTapLocation) < 70;
        }
        _lastTapLocation = currentTapPosition;

        // This replaces the previous TimeSpan calculation
        bool tapsAreCloseInTime = ((e.Timestamp - _lastTimestamp) < 700);

        if (tapsAreCloseInTime && tapsAreCloseInDistance)
        {
            _lastTapLocation = null;
        }

        _lastTimestamp = e.Timestamp;

        return tapsAreCloseInDistance && tapsAreCloseInTime;
    }
}
Bobby Tait
  • 99
  • 1
  • 8