MR.Gestures adds events and Commands for handling touch and mouse gestures to all the .NET MAUI elements.
With version 5 and the end of support for Xamarin.Forms I released it as open source under the MIT license.
To get started you
- install the NuGet package to your MAUI project,
- initialize it with a call to
ConfigureMRGestures()
in yourMauiProgram
- and then use the elements from
MR.Gestures
instead ofMicrosoft.Maui.Controls
.
The official website with a more detailed description is at www.mrgestures.com.
The GestureSample app demos how all the elements are used and what you get in the EventArgs
.
If you want to develop MR.Gestures yourself or want to know, how everything works, then read on. The rest of this page describes how MR.Gestures works and what the most important classes are for.
MR.Gestures adds various properties and events to each and every element.
The proper way to do this would be to add them to some base class like Element
. But as an external library I cannot do this.
I also couldn't add attached properties because I needed to override some methods of the native controls.
C# also cannot inherit from two base classes.
So I needed my own class which inherits from the respective MAUI class and adds all the properties and events which MR.Gesture needs - 18 events and 36 properties in every class.
Of course I did not want to add the functionality in every class. The functionality is in GestureHandler
. All the controls hold an instance of that class and forward their calls to the GestureHandler
.
But even defining and forwarding those 18 x (1 event + 2 properties) in each class is a lot of code. So I wrote a T4 template to do that. T4 could generate any kind of files long before source generators were introduced in Roslyn. Unfortunately it never got much love from Microsoft. There was no intellisense in VS, so writing them is a lot of trial-and-error.
At the time of this writing the T4 file Controls.tt
is 186 lines long and generates Controls.cs
with 29,894 lines.
MAUI Handlers translate the shared MAUI element to the respective native user control. It's similar to the renderers in Xamarin.Forms. Again, this is much repetitive code and again it will be generated by T4 templates.
File | Generates | Description |
---|---|---|
Handlers.json |
defines all the data I need. It is a list of all handler names (some of which are used for multiple elements) and their respective native control on each platform. This file is read by all T4 templates in the Handlers folder. |
|
Handlers.tt |
Handlers.cs |
This contains the part of the handler which is the same on every platform. It maps the event and command property names to the method HandlerHelper.MapPropertyChanged . |
Handlers.Android.tt |
Handlers.Android.cs |
The Android handlers are a bit complicated. To add my gesture handling, I need to override DispatchTouchEvent and DispatchGenericMotionEvent in every native view. Therefore I need to define such a native view for each element and use that instead of the MAUI view. |
Handlers.iOS.tt |
Handlers.iOS.cs |
This is currently not used. |
Handlers.Windows.tt |
Handlers.Windows.cs |
This is currently not used. |
The other files in the Handlers
folder are for cases where the MAUI team did not adhere to their own naming conventions or did not migrate the Xamarin.Forms renderers to MAUI handlers.
In these cases the T4 templates could not be used.
The PlatformSpecific
folder contains a folder for each platform and within those
PlatformSpecific/Android/AndroidGestureHandler.cs
PlatformSpecific/iOS/iOSGestureHandler.cs
PlatformSpecific/Windows/WinUIGestureHandler.cs
are the most important files.
They each define static methods
AddInstance
RemoveInstance
OnElementPropertyChanged
The static methods are called from the handlers. They create instances of the respective *GestureHandler
classes which do everything they need to do to handle the touch gestures on the native platform.
Each PlatformSpecific/
platform folder has a subfolder EventArgs
. These inherit from the respective shared EventArgs
and define constructors which convert the data from the native gesture objects to MR.Gesture.*EventArgs
.
E.g.
namespace MR.Gestures.Android.EventArgs;
public class AndroidPanEventArgs : MR.Gestures.PanEventArgs
{
public AndroidPanEventArgs(MotionEvent previous, MotionEvent current, PanEventArgs prevArgs, global::Android.Views.View view)
When the AndroidGestureHandler
or one of its helper classes recognizes a pan gesture, then it creates a new AndroidPanEventArgs
and sends it to IGestureListener
.
IGestureListener
is implemented by multiple classes which use a Chain of Responsibility pattern.
Class | Description |
---|---|
GestureThrottler |
Some gestures can be configured to only raise when a certain minimum value is reached. E.g. Settings.MinimumDeltaDistance defines a minimum distance. If the delta distance between two pan gestures is lower than this value, then the event will not be raised. If it is above, then the gesture will be forwarded to its inner IGestureListener . |
GestureFilter |
This only looks at the elements InputTransparent property. Îf it is True , then no events are raised. If it is False , then the gesture will be forwarded to its inner IGestureListener . |
GestureHandler |
This class finally calls the event handler and/or command in the respective element. |