AppoMobi.Maui.Gestures
1.11.1
dotnet add package AppoMobi.Maui.Gestures --version 1.11.1
NuGet\Install-Package AppoMobi.Maui.Gestures -Version 1.11.1
<PackageReference Include="AppoMobi.Maui.Gestures" Version="1.11.1" />
<PackageVersion Include="AppoMobi.Maui.Gestures" Version="1.11.1" />
<PackageReference Include="AppoMobi.Maui.Gestures" />
paket add AppoMobi.Maui.Gestures --version 1.11.1
#r "nuget: AppoMobi.Maui.Gestures, 1.11.1"
#:package AppoMobi.Maui.Gestures@1.11.1
#addin nuget:?package=AppoMobi.Maui.Gestures&version=1.11.1
#tool nuget:?package=AppoMobi.Maui.Gestures&version=1.11.1
AppoMobi.Maui.Gestures
Library for .NET MAUI to handle gestures. Can be consumed in XAML and code-behind. A NuGet package with the same name is available.
This library is used by DrawnUI for .NET MAUI.
.NET 8 and above compatible.
What's New
- Added
Manualtouch mode for dynamic gesture control with parent controls, allowing true cooperative gesture handling inside ScrollViews and other containers. - Pointer Data Support: Added rich pointer/mouse event data with
TouchActionEventArgs.Pointerproperty - Windows: Hover mouse/pointer tracking, mouse button detection (up tu 9 buttons), pen pressure, hover tracking
- Android: Hover mouse/pointer tracking, limited mouse button detection
- iOS/MacCatalyst: Hover mouse/pointer tracking, two-fingers scrolling, limited mouse button detection
Features
- ✅ Attachable .NET MAUI effect
- ✅ Multi-touch support (rotation, pinch/zoom)
- ✅ Customizable touch modes for cooperation with parent/child views
- ✅ Rich gesture data: velocity, distance, time, manipulation info
- ✅ Pixel-perfect precision across all platforms
- ✅ Platform-agnostic gesture processing
- ✅ Commands and event handlers support
- ✅ Mouse wheel support on Windows
- ✅ Mouse/touchpad support (limited upon platform)
Installation
- Install the AppoMobi.Maui.Gestures package from NuGet:
dotnet add package AppoMobi.Maui.Gestures
- Initialize the library in
MauiProgram.cs:
builder.UseGestures();
Basic Usage
Gestures are handled by a MAUI Effect. You can attach properties to invoke commands or handlers upon specific gestures.
XAML
<ContentPage xmlns:touch="clr-namespace:AppoMobi.Maui.Gestures;assembly=AppoMobi.Maui.Gestures">
<Label Text="Tap Me!"
touch:TouchEffect.CommandTapped="{Binding TapCommand}" />
<Frame touch:TouchEffect.CommandTapped="{Binding ItemTappedCommand}"
touch:TouchEffect.CommandTappedParameter="{Binding .}"
touch:TouchEffect.CommandLongPressing="{Binding LongPressCommand}">
<Label Text="Long press or tap me!" />
</Frame>
</ContentPage>
Code-Behind
// Attach tap command
TouchEffect.SetCommandTapped(myView, TapCommand);
TouchEffect.SetCommandTappedParameter(myView, itemData);
// Attach long press command
TouchEffect.SetCommandLongPressing(myView, LongPressCommand);
// Force attach effect (for manual processing)
TouchEffect.SetForceAttach(myView, true);
// Set touch mode
TouchEffect.SetShareTouch(myView, TouchHandlingStyle.Lock);
Gestures
The library supports the following gesture types:
| Gesture | Description | Event/Command |
|---|---|---|
| Down | Finger touches screen | Down event, DownCommand |
| Up | Finger lifts from screen | Up event, UpCommand |
| Tapped | Quick tap (with velocity threshold) | Tapped event, CommandTapped |
| Long Pressing | Finger held down for extended time | LongPressing event, CommandLongPressing |
| Panning | Finger moves across screen | Panning event, PanningCommand |
| Pinched | Two-finger pinch/zoom | Pinched event, PinchedCommand |
| Wheel | Mouse wheel (Windows) | Wheel event with delta |
| Rotation | Two-finger rotation | Multi-touch manipulation data |
| Wheel | Mouse wheel (Windows) | Wheel event with delta |
| Pointer | Mouse/touchpad related data |
Touch Handling Modes
Touch handling modes control how gestures interact with parent containers like ScrollView, ListView, etc.
public enum TouchHandlingStyle
{
Default, // Normal behavior
Lock, // Lock all input for this control only
Manual, // Dynamic control via WIllLock property, used by DrawnUI to work in cooperation with native ScrollView
Disabled // Same as InputTransparent = true
}
Not sure what mode to use? Start with Default.
About Manual Mode
Use case: Controls that need dynamic control over parent gesture blocking - the ultimate flexibility! 🎯
Manual mode gives you full control over gesture cooperation by letting you decide at runtime whether to lock or release parent containers using the WIllLock property.
<ScrollView Orientation="Vertical">
<VerticalStackLayout>
<controls:MyCarousel touch:TouchEffect.ShareTouch="Manual"
touch:TouchEffect.ForceAttach="True" />
<Label Text="More content below..." />
</VerticalStackLayout>
</ScrollView>
How It Works:
Manual mode works by:
- Always delivering touch events to your control (so you receive
TouchesEndedfor snapping) - Listening to the
WIllLockstate YOU set dynamically - Blocking or releasing the parent based on your control's needs
The WIllLock Property:
public enum ShareLockState
{
Initial, // Starting state, reset on Up/Down
Locked, // Block parent - you're consuming the gesture
Unlocked // Release parent - you're not consuming
}
Use for custom controls:
- ✅ Horizontal carousels in vertical ScrollViews (with snapping)
- ✅ Custom sliders that need directional control
- ✅ Pan-to-dismiss gestures alongside scrolling
- ✅ Any control that needs to decide "should I handle this?" at runtime
Example - Horizontal Carousel with Dynamic Locking:
public class MyCarousel : ContentView, IGestureListener
{
private PointF _startPoint;
private bool _isHandling = false;
public MyCarousel()
{
// Enable manual mode for dynamic control
TouchEffect.SetShareTouch(this, TouchHandlingStyle.Manual);
TouchEffect.SetForceAttach(this, true);
}
public void OnGestureEvent(TouchActionType type, TouchActionEventArgs args, TouchActionResult action)
{
var effect = TouchEffect.GetFrom(this);
switch (action)
{
case TouchActionResult.Down:
_startPoint = args.Location;
_isHandling = false;
// WIllLock is automatically reset to Initial
break;
case TouchActionResult.Panning:
var deltaX = Math.Abs(args.Distance.Delta.X);
var deltaY = Math.Abs(args.Distance.Delta.Y);
// Decide: horizontal or vertical gesture?
if (!_isHandling)
{
if (deltaX > deltaY && deltaX > 5)
{
// Horizontal pan - WE want control
_isHandling = true;
effect.WIllLock = ShareLockState.Locked;
}
else if (deltaY > 5)
{
// Vertical pan - PARENT should handle
effect.WIllLock = ShareLockState.Unlocked;
return;
}
}
if (_isHandling)
{
// Pan the carousel horizontally
ScrollBy(args.Distance.Delta.X);
}
break;
case TouchActionResult.Up:
if (_isHandling)
{
// IMPORTANT: We ALWAYS get Up event in Manual mode!
// This is perfect for snapping behavior
SnapToNearestItem();
}
// WIllLock is automatically reset to Initial
break;
}
}
}
Mouse and Pen Support
The library provides mouse and pen support with button tracking, hover detection, and pressure sensitivity, where platform allows that.
Platform Support
- Windows: Full pointer support, up to 8 buttons wheel, pen pressure, hover tracking
- Android: Basic support (Left/Right click, hover tracking)
- iOS/MacCatalyst: Basic support (Left click, 2-fingers scrolling, hover tracking, Apple Pencil detection)
Mouse Button Events
When args.Pointer != null, you have access to information:
public class PointerData
{
// Button that triggered this event (for press/release)
public MouseButton Button { get; set; }
// Button number (1-based): 1=Left, 2=Right, 3=Middle, 4+=Extended
public int ButtonNumber { get; set; }
// Button state (Pressed/Released)
public MouseButtonState State { get; set; }
// Detected pressed buttons (flags)
public MouseButtons PressedButtons { get; set; }
// Device type (Mouse/Pen/Touch)
public PointerDeviceType DeviceType { get; set; }
// Pressure (0.0-1.0 for pen, 1.0 for mouse)
public float Pressure { get; set; }
}
Advanced Usage
Manual Gesture Processing
For custom controls that need low-level gesture processing:
<controls:MyCustomControl touch:TouchEffect.ForceAttach="True" />
IGestureListener Interface
Implement this interface in your custom control:
public class MyCustomControl : ContentView, IGestureListener
{
public MyCustomControl()
{
// Attach the effect
TouchEffect.SetForceAttach(this, true);
TouchEffect.SetShareTouch(this, TouchHandlingStyle.Manual);
}
public void OnGestureEvent(
TouchActionType type, // Raw platform gesture type
TouchActionEventArgs args, // Gesture data
TouchActionResult action) // Processed gesture result
{
switch (action)
{
case TouchActionResult.Down:
// First finger down
if (args.NumberOfTouches == 1)
{
_startPoint = args.Location;
}
break;
case TouchActionResult.Panning:
// Finger moving
var deltaX = args.Distance.Delta.X;
var deltaY = args.Distance.Delta.Y;
var velocityX = args.Distance.Velocity.X;
// Check for multi-touch manipulation
if (args.Manipulation != null)
{
var scale = args.Manipulation.Scale;
var rotation = args.Manipulation.Rotation;
}
break;
case TouchActionResult.Tapped:
// Quick tap detected
ExecuteTapAction();
break;
case TouchActionResult.LongPressing:
// Long press detected
ShowContextMenu();
break;
}
}
public bool InputTransparent => false;
}
Multi-Touch Support
When multiple fingers are detected:
public void OnGestureEvent(TouchActionType type, TouchActionEventArgs args, TouchActionResult action)
{
// Check number of touches
if (args.NumberOfTouches > 1)
{
// Multi-touch gesture
if (args.Manipulation != null)
{
// Scale (pinch/zoom)
var scale = args.Manipulation.Scale; // Current frame scale change
var totalScale = args.Manipulation.ScaleTotal; // Total scale from start
// Rotation
var rotation = args.Manipulation.Rotation; // Current frame rotation (degrees)
var totalRotation = args.Manipulation.RotationTotal; // Total rotation from start
// Center point of gesture
var centerX = args.Manipulation.Center.X;
var centerY = args.Manipulation.Center.Y;
// Number of touches involved
var touchCount = args.Manipulation.TouchesCount;
}
}
}
Configuration
Static Properties
Configure global behavior using static properties:
// Tapped velocity threshold (in points, not pixels)
TouchEffect.TappedVelocityThresholdPoints = 200f; // Default: 200
// Long press duration (in milliseconds)
TouchEffect.LongPressTimeMsDefault = 1500; // Default: 1500ms
// Enable logging for debugging
TouchEffect.LogEnabled = true;
// Density for pixel/point conversion (auto-detected)
var density = TouchEffect.Density;
Instance Properties
Configure individual effect instances:
var effect = new TouchEffect
{
TouchMode = TouchHandlingStyle.Manual,
LongPressTimeMs = 2000, // Custom long press time
Draggable = true, // Don't cancel when moved outside
Capture = true // Capture pointer
};
Best Practices
1. Choose the Right Touch Mode
// ✅ Horizontal control in vertical ScrollView with dynamic control
TouchEffect.SetShareTouch(slider, TouchHandlingStyle.Manual);
// ✅ Drawing canvas that needs all gestures
TouchEffect.SetShareTouch(canvas, TouchHandlingStyle.Lock);
// ✅ Simple tap button
TouchEffect.SetShareTouch(button, TouchHandlingStyle.Default);
2. Use Pixel Values Consistently
All gesture data is in pixels for precision. Convert to points when needed:
var density = TouchEffect.Density;
var pointsX = pixelsX / density;
var pointsY = pixelsY / density;
3. Handle Multi-Touch Correctly
Always check NumberOfTouches and Manipulation property:
public void OnGestureEvent(TouchActionType type, TouchActionEventArgs args, TouchActionResult action)
{
// Single touch
if (args.NumberOfTouches == 1)
{
HandleSingleTouch(args);
}
// Multi-touch with manipulation data
else if (args.Manipulation != null)
{
HandleMultiTouch(args.Manipulation);
}
}
Tune Up
Long press duration
Adjust the long press timing:
TouchEffect.LongPressTimeMsDefault = 1000; // 1 second instead of 1.5
Close keyboard (Android)
TouchEffect.CloseKeyboard();
Check Tap Lock
Prevent accidental double-taps with built-in locking:
// Check if locked
if (TouchEffect.CheckLocked("myButton"))
{
return; // Skip this tap
}
// Lock and check in one call (locks for 500ms)
if (TouchEffect.CheckLockAndSet("myButton", ms: 500))
{
return; // Was locked, skip
}
// Not locked, proceed with action
Contributing
The library is actively developed. If you need additional features or find bugs:
- 💬 Start a Discussion
- 🐛 Open an Issue
- 🔧 Submit a Pull Request
Used by DrawnUI for .NET MAUI
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-android34.0 is compatible. net8.0-browser was computed. net8.0-ios was computed. net8.0-ios17.0 is compatible. net8.0-maccatalyst was computed. net8.0-maccatalyst17.0 is compatible. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net8.0-windows10.0.19041 is compatible. net9.0 is compatible. net9.0-android was computed. net9.0-android35.0 is compatible. net9.0-browser was computed. net9.0-ios was computed. net9.0-ios18.0 is compatible. net9.0-maccatalyst was computed. net9.0-maccatalyst18.0 is compatible. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net9.0-windows10.0.19041 is compatible. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- AppoMobi.Specials (>= 9.0.3)
-
net8.0-android34.0
- AppoMobi.Specials (>= 9.0.3)
-
net8.0-ios17.0
- AppoMobi.Specials (>= 9.0.3)
-
net8.0-maccatalyst17.0
- AppoMobi.Specials (>= 9.0.3)
-
net8.0-windows10.0.19041
- AppoMobi.Specials (>= 9.0.3)
-
net9.0
- AppoMobi.Specials (>= 9.0.3)
-
net9.0-android35.0
- AppoMobi.Specials (>= 9.0.3)
-
net9.0-ios18.0
- AppoMobi.Specials (>= 9.0.3)
-
net9.0-maccatalyst18.0
- AppoMobi.Specials (>= 9.0.3)
-
net9.0-windows10.0.19041
- AppoMobi.Specials (>= 9.0.3)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on AppoMobi.Maui.Gestures:
| Package | Downloads |
|---|---|
|
DrawnUi.Maui
Cross-platform rendering engine for .NET MAUI to draw your UI with SkiaSharp |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on AppoMobi.Maui.Gestures:
| Repository | Stars |
|---|---|
|
taublast/DrawnUi
Hardware-accelerated rendering engine .NET MAUI built on top of SkiaSharp 🎨
|
| Version | Downloads | Last Updated |
|---|---|---|
| 1.11.1 | 903 | 10/18/2025 |
| 1.9.7 | 3,312 | 4/28/2025 |
| 1.9.5 | 250 | 4/14/2025 |
| 1.9.4 | 811 | 3/25/2025 |
| 1.9.3.1 | 1,883 | 12/31/2024 |
| 1.9.2.5 | 522 | 12/1/2024 |
| 1.9.2.2 | 161 | 12/1/2024 |
| 1.9.2.1 | 567 | 11/24/2024 |
| 1.9.1.1 | 423 | 11/17/2024 |
| 1.8.1.2 | 1,772 | 7/14/2024 |
| 1.2.2.1 | 902 | 5/29/2024 |
| 1.2.1.1 | 484 | 5/12/2024 |
| 1.0.8.4 | 571 | 4/3/2024 |
| 1.0.8.3 | 236 | 4/3/2024 |
| 1.0.8.2 | 470 | 3/4/2024 |
| 1.0.5.3 | 1,531 | 1/20/2024 |
| 1.0.5.2 | 469 | 1/10/2024 |
| 1.0.5.1 | 357 | 1/10/2024 |
| 1.0.5 | 356 | 1/7/2024 |
| 1.0.4.18 | 285 | 1/4/2024 |
| 1.0.4.10 | 236 | 12/29/2023 |
| 1.0.4.9 | 208 | 12/29/2023 |
| 1.0.4.1-pre | 957 | 11/20/2023 |
| 1.0.3.1-pre | 733 | 10/23/2023 |
| 1.0.1.3 | 910 | 9/7/2023 |
| 1.0.1.2-pre | 420 | 6/25/2023 |
| 1.0.1.1-pre | 243 | 6/17/2023 |