17. Oktober 2016
Thomas Wilk
0

Swipe-Gesten in WPF

WPF als Grafik-Framework bietet dem .NET Entwickler eine Vielzahl von vorgefertigten Bedienelementen. In vielen Fällen muss man diese Bedienelemente jedoch erweitern um den Anforderungen an die Software gerecht zu werden, wie auch in diesem Fall: Ein Bildbetrachter soll um die Swipe-Gesten „Von-Rechts-nach-Links“ und „Von-Links-nach-Rechts“ erweitert werden. Die Funktionalität zum Bildwechsel ist bereits enthalten und wurde mit Pfeilen an den jeweiligen Außenkanten des Bildes für den Benutzer sichtbar gemacht.

touchgesten-in-wpf@saxonia-systems

Unter Windows RT oder Windows Phone ist die Gesten-Erkennung Standard und wird mit dem jeweiligen Framework mitgeliefert. Die Vermutung liegt also nah, dass diese Funktionalität auch in WPF enthalten ist. Leider ist dies nicht so. Eine kurze Internetrecherche zeigt: WPF stellt leider nur ein rudimentäres Paket zur Erkennung der Position des Fingers und „TouchIn“- und „TouchOut“-Events zur Verfügung. Es bietet keine „highlevel“ Gesten wie Swipe oder Zoom.

In der Hoffnung, dass bereits ein anderer Entwickler auf dieses Problem gestoßen ist, durchsuche ich das Internet.

Die ersten Einträge, die ich durch die Suche „Swipe gesture wpf“ finde,, beziehen sich entweder auf das WinRT Framework oder die Manipulation von Bildern. Weiterhin finde ich noch ein 7+4 stufiges Tutorial von Intel, welches einem mit allerhand Delta-Manipulationsberechnung den Eindruck erweckt dass diese„Swipe“-Gestenerkennung nicht so einfach umsetzbar ist wie anfänglich vermutet.

https://software.intel.com/en-us/articles/touch-gestures

Auf der Suche nach einer einfacheren Variante finde ich sehr viele Informationen zu der Umsetzung mit WinRT. Da die Konzepte hier ein wenig einfacher aussehen, widme ich mich der genauen Implementierung in WinRT und siehe da:

private Point initialpoint;

    private void Grid_ManipulationStarted_1(object sender, ManipulationStartedRoutedEventArgs e)
    {
        initialpoint = e.Position;
    }

    private void Grid_ManipulationDelta_1(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        if (e.IsInertial)
        {
            Point currentpoint = e.Position;
            if (currentpoint.X - initialpoint.X >= 500)//500 is the threshold value, where you want to trigger the swipe right event
            {
                System.Diagnostics.Debug.WriteLine("Swipe Right");
                e.Complete();
            }
        }
    }

Ok, da wird also ein initialer Punkt gesetzt, wenn die GridManipulation gestartet wird, klingt logisch, aber warum jetzt eine GridManipulation starten?!

Die Suche nach einer Lösung im Internet führte noch durch unterschiedliche, zumeist seltsame Eventhandler und noch seltsamere Booleans, wie AlreadySwiped hindurch, bis die Erkenntnis kam:
Ich schreibe es einfach selbst. Interessanterweise war dass die schnellste und logischste Weise dieses Problem anzugehen.

In den folgenden Zeilen stell ich meinen SwipeGestureHelper vor:

public static class SwipeGestureHelper
    {
        /// <summary>
        /// Gesture Directions
        /// </summary>
        public enum GestureDirection
        {
            Unknown,
            Right,
            Left,
            Up,
            Down
        }

        /// <summary>
        /// Find out direction
        /// </summary>
        public static GestureDirection DetectSwipeGestureByPosition(TouchPoint startTouchPosition, TouchPoint endTouchPosition, int minimumOffset)
        {
            var actualXOffset = Math.Abs(endTouchPosition.Position.X - startTouchPosition.Position.X);
            var actualYOffset = Math.Abs(endTouchPosition.Position.Y - startTouchPosition.Position.Y);
            if (actualXOffset > actualYOffset)
            {
                if (!(actualXOffset > minimumOffset)) { return GestureDirection.Unknown; }
                if (startTouchPosition.Position.X < endTouchPosition.Position.X)
                {
                    // Debug.WriteLine("swipped right");
                    return GestureDirection.Right;
                }
                else if (startTouchPosition.Position.X > endTouchPosition.Position.X)
                {
                    // Debug.WriteLine("swipped left ");
                    return GestureDirection.Left;
                }
            }

            if (actualXOffset < actualYOffset)
            {
                if (!(actualYOffset > minimumOffset)) { return GestureDirection.Unknown; }
                if (startTouchPosition.Position.Y < endTouchPosition.Position.Y)
                {
                    // Debug.WriteLine("swipped down");
                    return GestureDirection.Down;
                }
                else if (startTouchPosition.Position.Y > endTouchPosition.Position.Y)
                {
                    // Debug.WriteLine("swipped up");
                    return GestureDirection.Up;                   
                }
            }

            return GestureDirection.Unknown;
        }
    }

Der Helper ist auf die Events „TouchDown“ und „TouchUp“ angewiesen. Diese besitzt nahezu jedes Control innerhalb von WPF. Diese Events liefern „TouchPoints“ die zur Verwendung des Helpers notwendig sind.

Zur Verwendung des Helpers muss man den „TouchDown“-„TouchPoint“ zwischenspeichern. Dieser „TouchDown„-„TouchPoint“ muss dann innerhalb des „TouchUp“-Events zur Verfügung gestellt werden um einen Vergleichspunkt zum „TouchUp„-„TouchPoint“ zu haben.

Daraufhin wird innerhalb des „TouchUp“-Events der Helper entsprechend mit der Methode „DetectSwipeGestureByPosition“ aufrufen. Die Parameter startTouchPosition, endTouchPosition und minimumOffset müssen mitgegeben werden. Der Parameter minimumOffset beschreibt dabei die Strecke die mit dem Finger minimal zurückgelegt bevor die Swipegeste erkannt wird.

Entsprechend der drei Parameter gibt die Methode „DetectSwipeGestureByPosition“ daraufhin ein Enum-Wert aus dem Enum „GestureDirection“ zurück. Die Werte sind „Unknown“, „Right“, „Left“, „Up“, „Down“ und können natürlich noch erweitert werden.

Damit ist das Eingangsproblem gelöst und verrichtet mit einem „minimumOffset“ von 50 entsprechend seine Dienste.

SwipeGestureHelper

Thomas Wilk ist UI/UX-Entwickler. Seine Schwerpunkte liegen im Bereich Windows Universal Apps, ASP.NET und WPF Entwicklung. Sein Hauptaugenmerk liegt dabei immer auf der User Experience und Bedienbarkeit eines Software-Produkts.

Google+ Xing 

TeilenTweet about this on TwitterShare on Facebook0Share on Google+0Share on LinkedIn0