MR.Gestures Handle all the touch gestures in your Xamarin.Forms mobile apps

Overview

Xamarin Forms is great if you want to develop apps for different mobile platforms, but it is missing a critical functionality which every device with a touch screen needs. It cannot handle the various touch gestures.

MR.Gestures adds Down, Up, Tapping, Tapped, DoupleTapped, LongPressing, LongPressed, Panning, Panned, Swiped, Pinching, Pinched, Rotating and Rotated events to each and every layout, cell and view and to the ContentPage. These events will be raised when the user performs the corresponding touch gesture on the element.

In the EventArgs passed to the handlers you can see exactly what happened.

Getting Started

If you just want to see how it works, then download the free GestureSample app from GitHub. In this app you can see how all the elements use all events. You can change the code however you like, add break points and see what it does. You will find all the examples from here in the GestureSample and many more.

To add MR.Gestures to your own app just follow these simple steps:

And now you can use the new elements from the namespace MR.Gestures.

Code Samples

All the elements which are usually in the Xamarin.Forms namespace can also be found in MR.Gestures. But those have additional event handlers and command properties.

Adding the event handlers works the same way as in standard Xamarin Forms or WPF controls in both XAML and code.

Event Handlers in XAML

To add the event handler in XAML you have to:

  1. Add the namespace MR.Gestures from the dll
  2. Use the element from that namespace instead of Xamarin.Forms
  3. Add the handlers for the gestures you want to listen to

Here on LongPressed the method Red_LongPressed should be called. The method must be defined in the code behind file like this

    void Red_LongPressed(object sender, MR.Gestures.LongPressEventArgs e)
    {
        Console.WriteLine("BoxViewXaml.Red_LongPressed method called");
    }

Event Handlers in Code

Of course you can do the same in code. Either with a lambda expression

        var box1 = new MR.Gestures.BoxView { Color = Color.Red };
        box1.LongPressed += (s, e) => { Console.WriteLine("Code: Red LongPressed"); };

or by assigning the method as a handler.

        box1.LongPressed += Red_LongPressed;

Commands in XAML

But IMHO if you want to write clean MVVM, then the view should be in XAML and the code behind empty. So instead of the event handlers you can also use commands in your view model and bind them to the respective properties.

    <mr:BoxView Color="Green"
		LongPressedCommand="{Binding LongPressedCommand}"
		LongPressedCommandParameter="Green"

The properties for the commands are called like the events, just with Command appended. You can also define a parameter passed to the command with the ..CommandParameter properties. If you suppress the CommandParameter, then the respective event args are passed to your command. In this case a MR.Gestures.LongPressEventArgs object.

For the Tapped, DoubleTapped and LongPressed events you may not need the event args and defining a CommandParameter could make sense. But for the more complicated events you will always need the event args or you won't know what happened. E.g. it does not help to just know that an element has been swiped unless you also know in which direction. The Direction is contained in the SwipeEventArgs.

Commands in Code

You can bind the commands in code too.

        var box2 = new MR.Gestures.BoxView { Color = Color.Green };
        box2.SetBinding(MR.Gestures.BoxView.LongPressedCommandProperty, "LongPressedCommand");
        box2.LongPressedCommandParameter = "Green";

The syntax for binding in code is a bit complicated, therefore my favorite is using commands in XAML. You will also see this, when you look through the code in GestureSample. Although there are samples in all these categories, most of them are written in XAML and bound to commands.

Buy

MR.Gestures is licensed per app name. I.e. if your app has the same name on all three platforms, you only need one license key. If you have different versions of your app (e.g. a free and a pro one) with different names, then you need a separate key for each version.

You can use it for as many developers on as many computers as you like.

The price for one license is EUR 10,00 (+VAT).

The payment process will be handled by MyCommerce. You will be forwarded to their site when you click the Buy button.

Once you purchased a license, instructions how to configure it will be displayed on the MyCommerce page. These instructions can also be found in the email they will send you and in the FAQs.

If you forget to configure the license key properly, then all the events will still be raised, but the properties of the EventArgs will be empty. That may be enough for the tap and long press events, but not for the more complicated ones.

Documentation

Events

You can handle fourteen different events with MR.Gestures.

Event Description
Down One or more fingers came down onto the touch screen.
Up One or more fingers were lifted from the screen.
Tapping A finger came down and up again but it is not sure yet if this may become a multi tap.
Tapped There was no second tap within 250ms so this was a single tap gesture.
DoubleTapped There have been two Tapping events within 250ms and no more came.
LongPressing A finger came down on the screen and did not move. The finger is still down.
LongPressed The finger was released and the gesture is finished.
Panning A finger came down and is moving on the screen.
This is also called a dragging gesture.
Panned The finger left the screen while it was moved slowly or not at all.
Swiped The finger left the screen while it was moved fast.
On Android and Windows Phone this is also called a flick.
Pinching Two fingers hit the screen and are moving towards or away from each other.
Pinched The fingers left the screen.
Rotating Two fingers hit the screen and are rotating on the screen.
Rotated The fingers left the screen.

The -ing event is raised when at least one finger is on the screen and a gesture is in progress. The -ed event is raised, when the gesture is finished and all fingers left the screen.

EventArgs

Your event handlers will receive one of seven different EventArgs objects with all the detailed information what happened.

Raised by Down and Up
Type Property Description
int[] TriggeringTouches The indexes of the fingers which were lowered/raised. Their locations are in Touches.
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen (and in Up the last position before they were lifted).
Rectangle ViewPosition The position of the Sender on the screen.
Raised by Tapping, Tapped and DoubleTapped
Type Property Description
int NumberOfTaps The number of taps in a short period of time (~250ms).
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.
Raised by LongPressing and LongPressed
Type Property Description
long Duration Duration of long press in milliseconds.
int NumberOfTaps The number of taps in a short period of time (~250ms).
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.
Raised by Panning and Panned
Type Property Description
Point DeltaDistance The distance in X/Y that the finger was moved since this event was raised the last time.
Point TotalDistance The distance in X/Y that the finger was moved since the pan gesture began.
Point Velocity The velocity of the finger in X/Y.
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.
Raised by Swiped
Type Property Description
Direction Direction The direction in which the finger moved when it was lifted from the screen. This is one of Left, Right, Up, Down or NotClear.
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.
Raised by Pinching and Pinched
Type Property Description
Point Center The center of the fingers on the screen.
double DeltaScale The relative distance between the two fingers on the screen compared to the last time this event was raised.
double Distance The distance between the first two fingers.
double TotalScale The relative distance between the two fingers on the screen compared to when the gesture started.
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.
Raised by Rotating and Rotated
Type Property Description
double Angle The angle a line from the first to the second finger currently has on the screen.
Point Center The center of the fingers on the screen.
double DeltaAngle The angle the fingers were rotated on the screen compared to the last time this event was raised.
double TotalAngle The angle the fingers were rotated on the screen compared to the start of the gesture.
bool Cancelled Android and iOS sometimes cancel a touch gesture. In this case a *ed event is raised with Cancelled set to true.
bool Handled This flag is meant to be used for event bubbling, but it does not work yet.
int NumberOfTouches The number of fingers on the screen.
IGestureAwareControl Sender The element which raised this event.
Point[] Touches Returns the position of the fingers on the screen.
Rectangle ViewPosition The position of the Sender on the screen.

Compatibility

Some elements need to listen to some touch gestures themselves to work properly. Depending on the exact implementation by Xamarin and the native platforms, these events may be comsumed.
Some of the views are simply too small to be touched. Therefore the gestures are also not forwarded to us.

In Windows Runtime (Windows Phone 8.1 and Windows Store) Microsoft introduced a performance optimization called Direct Manipulation. It is also still used in UWP. This means that the Windows.UI.Xaml.Controls.ScrollViewer handles scrolling and zooming of its contents on its own. It cancels all other gesture handlers when it detects that it should act.
This ScrollViewer is used by the Xamarin.Forms.ListView, Xamarin.Forms.ScrollView and the Xamarin.Forms.TabbedPage. Maybe also others, but those are the ones I tested.
For you, this means, that everything within one of the listed controls can only handle Down, Up, Tapping, Tapped, DoubleTapped, LongPressing and LongPressed. The other events are consumed by the ScrollViewer. I do raise an Up event with Cancelled = true to indicate that I do not receive the current gesture.
More info can be found on Rob Caplans blog and the stackoverflow question I asked, but nobody answered yet.

I do not have a touch device (Windows tablet or touchscreen for the desktop) to test Windows Runtime or Universal Windows Platform apps. The code is exactly the same as for Windows Phone 8.1 runtime. So it should work, but I cannot be 100% sure.
Although I couldn't test those platforms on an actual device, I did test the single touch gestures best I could with the mouse.
When you look at the following table, keep in mind that Pinch and Rotate were not tested on Windows Runtime and Universal Windows Platform apps.

Here are all the elements and how they work in detail on each platform:

Element iOS Android WinPhone
8.0 SL
WinPhone
8.1 RT
Windows
8.1 RT
UWP
ContentPage
AbsoluteLayout
ContentView
Frame
Grid
RelativeLayout
ScrollView
StackLayout
ActivityIndicator
BoxView
Button
DatePicker
Editor
Entry
Image
Label
ListView
Picker
ProgressBar
SearchBar
Slider
Stepper
Switch
TableView
TimePicker
WebView
EntryCell
ImageCell
SwitchCell
TextCell
ViewCell
All events are fully supported.
Some events do not work fully.
No events are raised.

Release Notes

Most versions were just released because Xamarin increased the AssemblyVersion of their WinPhone dll prior to XF 1.3.5. So I needed a new version of MR.Gestures even though there were no changes. Now, every MR.Gestures version should work until they increase their minor version number.

Version Changes
1.4.0 SwipeEventArgs is a PanEventArgs and therefore also contains DeltaDistance, TotalDistance and Velocity
Add controls for AppCompat (MR.Gestures.AppCompat*)
No events are raised if InputTransparent == true (on iOS there are also no events if any container has InputTransparent)
[iOS] The Panned and Swiped events contain the last Touches before the finger was raised
[Windows] The LongPressed event contain the last Touches before the finger was raised
1.3.5 Fixes a bug with LongPressing/LongPressed on some Android devices.
1.3.4 Added an internal constructor which Mono Droid needs to instantiate Java objects.
1.3.3 Worked around an issue when a NullReferenceException was thrown when I read a dependency property.
1.3.2 Worked around an issue when the renderers' Dispose method was called after Element was set to null.
Removed debug messages
1.3.1 Raises Up with Cancelled = true when a finger is dragged off an element
Fix NullReferenceException on iOS
1.3.0 Fix a bug with Tapping/Tapped on Android
1.3.0-pre1 Add support for Universal Windows Platform
Updated to Xamarin.Forms 2.0
1.2.5 Support for multiple fingers in Tapping, Tapped, DoubleTapped, LongPressing and LongPressed on Android.
1.2.4 Reset panning gesture on swipe
[WinRT] fix a bug with elements on TabbedPage / CarouselPage
[WinRT] try/catch around calls to Windows.UI.Input.GestureRecognizer
1.2.3 Updated to Xamarin.Forms 1.5.0.6446
1.2.2 Fixes a bug where the gestures were not properly reset if you handled Panning but not Panned, Pinching without Pinched or Rotating without Rotated.
1.2.1 Fixes a bug on Android. A ScrollView with Orientation="Horizontal" did not scroll.
1.2.0 Much better compatibility with Windows Runtime
Update XF to 1.4.3.6374 and get rid of Xamarin.Forms.Windows
Refactor renderers so that custom renderers can now be written on all platforms without having to inherit from mine
1.2.0-pre1 Add support for WinPhone 8.1 and Windows Store
Drop support for 32 bit iOS apps
Two finger pan simultaneously with pinch and rotate on all platforms
Introduce gesture throttling on all platforms with MR.Gestures.Settings.MinimumDelta* properties
Add Center to all EventArgs
1.1.0 Add the Down and Up events
[Android] Fix a NullReferenceException if a control is disposed in its asynchronous gesture handler
1.0.8 [Android] handle all touch events on all elements additionally to Xamarins handlers
[Android] better resource management (not so big memory leaks when Xamarin does not dispose of elements)
[Android] use DisplayMetrics.Density instead of hardcoded 2 for calculating ViewPosition, Touches and PanEventArgs.DeltaDistance
[Android] don't start pan when lifting one finger after multi touch gesture
1.0.7 Fixed a bug where events on Cells on Android phones were raised too often.
1.0.6 Updated to Xamarin.Forms 1.4.0.6341
1.0.6-pre1 Updated to Xamarin.Forms 1.4.0.6336-pre1
1.0.5 Updated to Xamarin.Forms 1.3.5.6335
1.0.5-pre1 iOS compatibility much better
WinPhone memory leaks fixed with NavigationPage
added Settings.MsUntilTapped
1.0.4 Updated to Xamarin.Forms 1.3.4.6332
1.0.4-pre4 Updated to Xamarin.Forms 1.3.4.6331-pre4
1.0.3 Updated to Xamarin.Forms 1.3.3.6323
1.0.2 Updated to Xamarin.Forms 1.3.2.6316
1.0.1 Updated to Xamarin.Forms 1.3.1.6296
1.0.0 Initial version for Xamarin.Forms 1.3.0.6292

To install any specific version, you need to use the Package Manager Console.

Install-Package MR.Gestures [-Pre] [-version <version>]

If you don't specify a version, then the most recent version will be installed.
If you want to install a prerelease version, then you have to add the -Pre switch.

FAQ

How do I configure my license key?

As your app name can be different on each platform, the license key must be configured in all platform specific projects. The best place for it is in each project immediately after the call to global::Xamarin.Forms.Forms.Init(...).

Android

In your Android project open MainActivity.cs and in the OnCreate method after the call to global::Xamarin.Forms.Forms.Init(...) add the following line:

MR.Gestures.Android.Settings.LicenseKey = "<your license key>";
iOS

In the iOS project this has to be done in AppDelegate.cs, in the FinishedLaunching method. Add this line:

MR.Gestures.iOS.Settings.LicenseKey = "<your license key>";
Windows Phone 8.0 Silverlight

In the Windows Phone 8.0 project that line has to be added to MainPage.xaml.cs:

MR.Gestures.WinPhone.Settings.LicenseKey = "<your license key>";
Windows Phone 8.1 Runtime

In the Windows Phone 8.1 project the right place is in App.xaml.cs in the OnLaunched method:

                rootFrame.CacheSize = 1;
                Xamarin.Forms.Forms.Init(e);
                MR.Gestures.WinRT.Settings.LicenseKey = "<your license key>";
                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
Windows Store

In the Windows 8.1 project you also add the code to the OnLaunched method in App.xaml.cs:

                rootFrame.NavigationFailed += OnNavigationFailed;
                Xamarin.Forms.Forms.Init(e);
                MR.Gestures.WinRT.Settings.LicenseKey = "<your license key>";
				
                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
Universal Windows Platform

UWP is very similar to Windows Runtime. Therefore here you also add the code to the OnLaunched method in App.xaml.cs.
For the .NET Native compilation you also have to tell Xamarin.Forms, which assemblies it should scan for custom controls and renderers. Therefore you also have to change the call to Forms.Init slightly:

                rootFrame.NavigationFailed += OnNavigationFailed;
                var otherAssemblies = new[] {
                    typeof(MR.Gestures.ContentPage).GetTypeInfo().Assembly,
                    typeof(MR.Gestures.UWP.Renderers.PageRenderer).GetTypeInfo().Assembly,
                };
                Xamarin.Forms.Forms.Init(e, otherAssemblies);
                MR.Gestures.UWP.Settings.LicenseKey = "<your license key>";
				
                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)

How do I configure my app name?

The name of the app has to be configured differently on each platform.

Android

In the Android project open MainActivity.cs. The MainActivity class should have a Activity attribute. The Label parameter is the app name.

If you configured your activity manually in the Properties / AndroidManifest.xml file, then you need to change the android:label attribute there.

iOS

In Visual Studio open the iOS project properties and go to the "iOS Application" tab. There you'll find the "Application name".

In Xamarin Studio the "Application name" is the very first textbox when you open the info.plist file.

Windows Phone 8.0 Silverlight

In the Windows Phone 8.0 project open Properties / WMAppManifest.xml and on the "Application UI" tab change the "Display Name". You should also change the "Tile Title". This is the name which will be shown if you add the app to the start page.

Windows Runtime and Universal Windows Platform

On these platforms you need to open the Package.appxmanifest file in your platform project. On the "Application" tab change the "Display Name" and on the "Packaging" tab the "Package display name".

How do I install MR.Gestures?

You can install it with NuGet. The easiest way is to right click your solution and choose "Manage NuGet Packages for Solution...", then search for MR.Gestures and install it.

You could also do this from the Package Manager Console. In the Default Project drop down list select your portable project. Then type

Install-Package MR.Gestures

Then select all your platform projects one after the other and type the same again (or cursor up). This installs the package in all projects. There will be different references added depending on the project type.

Is there a trial version available?

I already released the GestureSample app with complete source code on GitHub. That app shows how to use MR.Gestures with each and every available Element.

If you want to try it in your own app or the final name of your app has not been decided yet, you can simply call your app GestureSample and use the LicenseKey from that app.

My event handler is called, but some event arg properties are not set.

You did not set the LicenseKey properly or it does not match your app name. Please check if you set the correct LicenseKey in all platform specific projects and that your app name matches the key.

I want to swipe with two fingers. How can I handle that gesture?

Currently MR.Gestures only works with the standard number of fingers for the respective gesture. I.e. two fingers for pinch and rotate and one for the others.

The exception is Panning/Panned which works with one or two fingers starting with version 1.2.0-pre1.

More fingers are not handled by the standard gesture handling APIs of the platforms.

I plan to use a different API which is nearer to the wire for the next big version. Then these gestures should also be possible.

I try to move/zoom/rotate an element, but it jumps on the screen.

The Touches coordinates in the EventArgs are always relative to the View which handles the event. If you manipulate the TranslationX, TranslationY, Scale or Rotation(X/Y) properties during a gesture, then the Touches and Delta* values cannot be calculated anymore.

So you always have to listen to the events of a container element and manipulate those properties of a child.

So instead of

        <mr:Image
		PanningCommand="{Binding PanningCommand}"
		TranslationX="{Binding TranslationX}"
		TranslationY="{Binding TranslationY}" />

you should write

        <mr:ContentView PanningCommand="{Binding PanningCommand}">
            <Image
		TranslationX="{Binding TranslationX}"
		TranslationY="{Binding TranslationY}" />
        </mr:ContentView>

The gestures do not work on cells on Windows.

Xamarins Windows renderers (Silverlight, Runtime and UWP) for cells do not provide anything I can hook into to add my functionality. So I had to add that functionality to the renderers for MR.Gestures.ListView and MR.Gestures.TableView.

So if you want to listen to touch gestures on cells, the containing ListView or TableView must also be used from MR.Gestures even if you don't add any gesture listeners on that element itself.

I get "Could not load file or assembly 'MR.Gestures'" or "Could not load type MR.Gestures...".

Please check your references. The portable project needs to reference MR.Gestures. The platform specific projects need references to MR.Gestures.Android, MR.Gestures.iOS or MR.Gestures.WinPhone respectively. In Xamarin Studio your platform projects also need a reference to MR.Gestures. Visual Studio copies it over to the output directory, but XS doesn't do that.

Please also check, if you set your Settings.LicenseKey like it is described below. If this is not set, then the linker thinks you don't need the dll and doesn't include it in the package deployed to your device.

If you don't have a license key yet, you can set it to an empty string in the meantime. This will at least include the dll and enable the events. The EventArgs will be empty, but you can already test if the events are raised.

There seems to be a memory leak on Windows Phone 8.0 Silverlight.

Contrary to iOS, Android and WinRT, Xamarins Windows Phone Silverlight renderers do not implement IDisposable. Therefore I cannot free some resources.

The best event I could find when a page is not needed anymore is NavigationPage.Popped. So I free up some memory when NavigationPage.Popped is raised.

But this means that you have to use a NavigationPage. If your initial page is a simple ContentPage and you open up other pages afterwards with Navigation.PushAsync then I cannot release all resources and the app will leak memory of the MR.Gestures elements on the subpages.

I get the error 'Cannot resolve dependency to assembly Xamarin.Forms.Platform.WP8, Version=1.3.3.0' in my Windows Phone project.

Prior to Xamarin.Forms 1.3.5 they increased the AssemblyVersion of their Windows Phone dll with every build. Therefore also all other dlls must use the same build or you'd get that error.

Starting with 1.3.5 they will only increase the AssemblyVersion when they increase the minor version (the second number). So this problem should not happen very often anymore.

See the Release Notes for which versions are available and how to install them.

Contact

My name is Michael Rumpler and I'm a freelance developer located in Austria. I started with C# in 2004. Although I also coded a lot in JavaScript and a bit Java, C# is my language of choice.

If you have any questions, suggestions, you find bugs or whatever, you can