diff --git a/docs/TOC.yml b/docs/TOC.yml index f9683535..f0f5bc84 100644 --- a/docs/TOC.yml +++ b/docs/TOC.yml @@ -103,6 +103,18 @@ href: fundamentals/data-binding/commanding.md - name: Compiled bindings href: fundamentals/data-binding/compiled-bindings.md + - name: Gestures + items: + - name: Drag and drop + href: fundamentals/gestures/drag-and-drop.md + - name: Pan + href: fundamentals/gestures/pan.md + - name: Pinch + href: fundamentals/gestures/pinch.md + - name: Swipe + href: fundamentals/gestures/swipe.md + - name: Tap + href: fundamentals/gestures/tap.md - name: Properties items: - name: Bindable properties diff --git a/docs/fundamentals/gestures/drag-and-drop.md b/docs/fundamentals/gestures/drag-and-drop.md new file mode 100644 index 00000000..be4c57ea --- /dev/null +++ b/docs/fundamentals/gestures/drag-and-drop.md @@ -0,0 +1,295 @@ +--- +title: "Recognize a drag and drop gesture" +description: "This article explains how to recognize drag and drop gestures with .NET MAUI." +ms.date: 02/22/2022 +--- + +# Recognize a drag and drop gesture + +A .NET Multi-platform App UI (.NET MAUI) drag and drop gesture recognizer enables items, and their associated data packages, to be dragged from one onscreen location to another location using a continuous gesture. Drag and drop can take place in a single application, or it can start in one application and end in another. + +[!INCLUDE [docs under construction](~/includes/preview-note.md)] + +The *drag source*, which is the element on which the drag gesture is initiated, can provide data to be transferred by populating a data package object. When the drag source is released, drop occurs. The *drop target*, which is the element under the drag source, then processes the data package. + +> [!IMPORTANT] +> On iOS a minimum platform of iOS 11 is required. + +The process for enabling drag and drop in an app is as follows: + +1. Enable drag on an element by adding a `DragGestureRecognizer` object to its `GestureRecognizers` collection. For more information, see [Enable drag](#enable-drag). +1. [optional] Build a data package. .NET MAUI automatically populates the data package for image and text controls, but for other content you'll need to construct your own data package. For more information, see [Build a data package](#build-a-data-package). +1. Enable drop on an element by adding a `DropGestureRecognizer` object its `GestureRecognizers` collection. For more information, see [Enable drop](#enable-drop). +1. [optional] Handle the `DropGestureRecognizer.DragOver` event to indicate the type of operation allowed by the drop target. For more information, see [Handle the DragOver event](#handle-the-dragover-event). +1. [optional] Process the data package to receive the dropped content. .NET MAUI will automatically retrieve image and text data from the data package, but for other content you'll need to process the data package. For more information, see [Process the data package](#process-the-data-package). + + + +## Enable drag + +In .NET MAUI, drag gesture recognition is provided by the `DragGestureRecognizer` class. This class defines the following properties: + +- `CanDrag`, of type `bool`, which indicates whether the element the gesture recognizer is attached to can be a drag source. The default value of this property is `true`. +- `DragStartingCommand`, of type `ICommand`, which is executed when a drag gesture is first recognized. +- `DragStartingCommandParameter`, of type `object`, which is the parameter that's passed to the `DragStartingCommand`. +- `DropCompletedCommand`, of type `ICommand`, which is executed when the drag source is dropped. +- `DropCompletedCommandParameter`, of type `object`, which is the parameter that's passed to the `DropCompletedCommand`. + +These properties are backed by `BindableProperty` objects, which means that they can be targets of data bindings, and styled. + +The `DragGestureRecognizer` class also defines `DragStarting` and `DropCompleted` events that fire provided that the `CanDrag` property is `true`. When a `DragGestureRecognizer` object detects a drag gesture, it executes the `DragStartingCommand` and invokes the `DragStarting` event. Then, when the `DragGestureRecognizer` object detects the completion of a drop gesture, it executes the `DropCompletedCommand` and invokes the `DropCompleted` event. + +The `DragStartingEventArgs` object that accompanies the `DragStarting` event defines the following properties: + +- `Handled`, of type `bool`, indicates whether the event handler has handled the event or whether .NET MAUI should continue its own processing. +- `Cancel`, of type `bool`, indicates whether the event should be canceled. +- `Data`, of type `DataPackage`, indicates the data package that accompanies the drag source. This is a read-only property. + + + +The following XAML example shows a `DragGestureRecognizer` attached to an `Image`: + +```xaml + + + + + +``` + +In this example, a drag gesture can be initiated on the `Image`. + +> [!TIP] +> A drag gesture is initiated with a long-press followed by a drag. + +## Build a data package + +.NET MAUI will automatically build a data package for you, when a drag is initiated, for the following controls: + +- Text controls. Text values can be dragged from `CheckBox`, `DatePicker`, `Editor`, `Entry`, `Label`, `RadioButton`, `Switch`, and `TimePicker` objects. +- Image controls. Images can be dragged from `Button`, `Image`, and `ImageButton` controls. + +The following table shows the properties that are read, and any conversion that's attempted, when a drag is initiated on a text control: + +| Control | Property | Conversion | +| --- | --- | --- | +| `CheckBox` | `IsChecked` | `bool` converted to a `string`. | +| `DatePicker` | `Date` | `DateTime` converted to a `string`. | +| `Editor` | `Text` || +| `Entry` | `Text` || +| `Label` | `Text` || +| `RadioButton` | `IsChecked` | `bool` converted to a `string`. | +| `Switch` | `IsToggled` | `bool` converted to a `string`. | +| `TimePicker` | `Time` | `TimeSpan` converted to a `string`. | + +For content other than text and images, you'll need to build a data package yourself. + +Data packages are represented by the `DataPackage` class, which defines the following properties: + +- `Properties`, of type `DataPackagePropertySet`, which is a collection of properties that comprise the data contained in the `DataPackage`. This property is a read-only property. +- `Image`, of type `ImageSource`, which is the image contained in the `DataPackage`. +- `Text`, of type `string`, which is the text contained in the `DataPackage`. +- `View`, of type `DataPackageView`, which is a read-only version of the `DataPackage`. + +The `DataPackagePropertySet` class represents a property bag stored as a `Dictionary`. For information about the `DataPackageView` class, see [Process the data package](#process-the-data-package). + +### Store image or text data + +Image or text data can be associated with a drag source by storing the data in the `DataPackage.Image` or `DataPackage.Text` property. This can be accomplished in the handler for the `DragStarting` event. + +The following XAML example shows a `DragGestureRecognizer` that registers a handler for the `DragStarting` event: + +```xaml + + + + + + + + +``` + +In this example, the `DragGestureRecognizer` is attached to a `Path` object. The `DragStarting` event is fired when a drag gesture is detected on the `Path`, which executes the `OnDragStarting` event handler: + +```csharp +void OnDragStarting(object sender, DragStartingEventArgs e) +{ + e.Data.Text = "My text data goes here"; +} +``` + +The `DragStartingEventArgs` object that accompanies the `DragStarting` event has a `Data` property, of type `DataPackage`. In this example, the `Text` property of the `DataPackage` object is set to a `string`. The `DataPackage` can then be accessed on drop, to retrieve the `string`. + +### Store data in the property bag + +Any data, including images and text, can be associated with a drag source by storing the data in the `DataPackage.Properties` collection. This can be accomplished in the handler for the `DragStarting` event. + +The following XAML example shows a `DragGestureRecognizer` that registers a handler for the `DragStarting` event: + +```xaml + + + + + +``` + +In this example, the `DragGestureRecognizer` is attached to a `Rectangle` object. The `DragStarting` event is fired when a drag gesture is detected on the `Rectangle`, which executes the `OnDragStarting` event handler: + +```csharp +void OnDragStarting(object sender, DragStartingEventArgs e) +{ + Shape shape = (sender as Element).Parent as Shape; + e.Data.Properties.Add("Square", new Square(shape.Width, shape.Height)); +} +``` + +The `DragStartingEventArgs` object that accompanies the `DragStarting` event has a `Data` property, of type `DataPackage`. The `Properties` collection of the `DataPackage` object, which is a `Dictionary` collection, can be modified to store any required data. In this example, the `Properties` dictionary is modified to store a `Square` object, that represents the size of the `Rectangle`, against a "Square" key. + +## Enable drop + +In .NET MAUI, drop gesture recognition is provided by the `DropGestureRecognizer` class. This class defines the following properties: + +- `AllowDrop`, of type `bool`, which indicates whether the element the gesture recognizer is attached to can be a drop target. The default value of this property is `true`. +- `DragOverCommand`, of type `ICommand`, which is executed when the drag source is dragged over the drop target. +- `DragOverCommandParameter`, of type `object`, which is the parameter that's passed to the `DragOverCommand`. +- `DragLeaveCommand`, of type `ICommand`, which is executed when the drag source is dragged off the drop target. +- `DragLeaveCommandParameter`, of type `object`, which is the parameter that's passed to the `DragLeaveCommand`. +- `DropCommand`, of type `ICommand`, which is executed when the drag source is dropped over the drop target. +- `DropCommandParameter`, of type `object`, which is the parameter that's passed to the `DropCommand`. + +These properties are backed by `BindableProperty` objects, which means that they can be targets of data bindings, and styled. + +The `DropGestureRecognizer` class also defines `DragOver`, `DragLeave`, and `Drop` events that fire provided that the `AllowDrop` property is `true`. When a `DropGestureRecognizer` recognizes a drag source over the drop target, it executes the `DragOverCommand` and invokes the `DragOver` event. Then, if the drag source is dragged off the drop target, the `DropGestureRecognizer` executes the `DragLeaveCommand` and invokes the `DragLeave` event. Finally, when the `DropGestureRecognizer` recognizes a drop gesture over the drop target, it executes the `DropCommand` and invokes the `Drop` event. + +The `DragEventArgs` class, that accompanies the `DragOver` and `DragLeave` events, defines the following properties: + +- `Data`, of type `DataPackage`, which contains the data associated with the drag source. This property is read-only. +- `AcceptedOperation`, of type `DataPackageOperation`, that specifies which operations are allowed by the drop target. + +For information about the `DataPackageOperation` enumeration, see [Handle the DragOver event](#handle-the-dragover-event). + +The `DropEventArgs` class that accompanies the `Drop` event defines the following properties: + +- `Data`, of type `DataPackageView`, which is a read-only version of the data package. +- `Handled`, of type `bool`, indicates whether the event handler has handled the event or whether .NET MAUI should continue its own processing. + +The following XAML example shows a `DropGestureRecognizer` attached to an `Image`: + +```xaml + + + + + +``` + +In this example, when a drag source is dropped on the `Image` drop target, the drag source will be copied to the drop target, provided that the drag source is an `ImageSource`. This occurs because .NET MAUI automatically copies dragged images, and text, to compatible drop targets. + +## Handle the DragOver event + +The `DropGestureRecognizer.DragOver` event can be optionally handled to indicate which type of operations are allowed by the drop target. This can be accomplished by setting the `AcceptedOperation` property, of type `DataPackageOperation`, of the `DragEventArgs` object that accompanies the `DragOver` event. + +The `DataPackageOperation` enumeration defines the following members: + +- `None`, indicates that no action will be performed. +- `Copy`, indicates that the drag source content will be copied to the drop target. + +> [!IMPORTANT] +> When a `DragEventArgs` object is created, the `AcceptedOperation` property defaults to `DataPackageOperation.Copy`. + +The following XAML example shows a `DropGestureRecognizer` that registers a handler for the `DragOver` event: + +```xaml + + + + + +``` + +In this example, the `DropGestureRecognizer` is attached to an `Image` object. The `DragOver` event is fired when a drag source is dragged over the drop target, but hasn't been dropped, which executes the `OnDragOver` event handler: + +```csharp +void OnDragOver(object sender, DragEventArgs e) +{ + e.AcceptedOperation = DataPackageOperation.None; +} +``` + +In this example, the `AcceptedOperation` property of the `DragEventArgs` object is set to `DataPackageOperation.None`. This ensures that no action is taken when a drag source is dropped over the drop target. + +## Process the data package + +The `Drop` event is fired when a drag source is released over a drop target. When this occurs, .NET MAUI will automatically attempt to retrieve data from the data package, when a drag source is dropped onto the following controls: + +- Text controls. Text values can be dropped onto `CheckBox`, `DatePicker`, `Editor`, `Entry`, `Label`, `RadioButton`, `Switch`, and `TimePicker` objects. +- Image controls. Images can be dropped onto `Button`, `Image`, and `ImageButton` controls. + +The following table shows the properties that are set, and any conversion that's attempted, when a text-based drag source is dropped on a text control: + +| Control | Property | Conversion | +| --- | --- | --- | +| `CheckBox` | `IsChecked` | `string` is converted to a `bool`. | +| `DatePicker` | `Date` | `string` is converted to a `DateTime`. | +| `Editor` | `Text` || +| `Entry` | `Text` || +| `Label` | `Text` || +| `RadioButton` | `IsChecked` | `string` is converted to a `bool`. | +| `Switch` | `IsToggled` | `string` is converted to a `bool`. | +| `TimePicker` | `Time` | `string` is converted to a `TimeSpan`. | + +For content other than text and images, you'll need to process the data package yourself. + +The `DropEventArgs` class that accompanies the `Drop` event defines a `Data` property, of type `DataPackageView`. This property represents a read-only version of the data package. + +### Retrieve image or text data + +Image or text data can be retrieved from a data package in the handler for the `Drop` event, using methods defined in the `DataPackageView` class. + +The `DataPackageView` class includes `GetImageAsync` and `GetTextAsync` methods. The `GetImageAsync` method retrieves an image from the data package, that was stored in the `DataPackage.Image` property, and returns `Task`. Similarly, the `GetTextAsync` method retrieves text from the data package, that was stored in the `DataPackage.Text` property, and returns `Task`. + +The following example shows a `Drop` event handler that retrieves text from the data package for a `Path`: + +```csharp +async void OnDrop(object sender, DropEventArgs e) +{ + string text = await e.Data.GetTextAsync(); + + // Perform logic to take action based on the text value. +} +``` + +In this example, text data is retrieved from the data package using the `GetTextAsync` method. An action based on the text value can then be taken. + +### Retrieve data from the property bag + +Any data can be retrieved from a data package in the handler for the `Drop` event, by accessing the `Properties` collection of the data package. + +The `DataPackageView` class defines a `Properties` property, of type `DataPackagePropertySetView`. The `DataPackagePropertySetView` class represents a read-only property bag stored as a `Dictionary`. + +The following example shows a `Drop` event handler that retrieves data from the property bag of a data package for a `Rectangle`: + +```csharp +void OnDrop(object sender, DropEventArgs e) +{ + Square square = (Square)e.Data.Properties["Square"]; + + // Perform logic to take action based on retrieved value. +} +``` + +In this example, the `Square` object is retrieved from the property bag of the data package, by specifying the "Square" dictionary key. An action based on the retrieved value can then be taken. diff --git a/docs/fundamentals/gestures/pan.md b/docs/fundamentals/gestures/pan.md new file mode 100644 index 00000000..9b716786 --- /dev/null +++ b/docs/fundamentals/gestures/pan.md @@ -0,0 +1,109 @@ +--- +title: "Recognize a pan gesture" +description: "This article explains how to use a .NET MAUI pan gesture to horizontally and vertically pan an image, so that all of the image content can be viewed when it's being displayed in a viewport smaller than the image dimensions." +ms.date: 02/22/2022 +--- + +# Recognize a pan gesture + +A .NET Multi-platform App UI (.NET MAUI) pan gesture recognizer detects the movement of fingers around the screen and can be used to apply that movement to content. A typical scenario for the pan gesture is to horizontally and vertically pan an image, so that all of the image content can be viewed when it's being displayed in a viewport smaller than the image dimensions. This is accomplished by moving the image within the viewport. + +[!INCLUDE [docs under construction](~/includes/preview-note.md)] + +In .NET MAUI, pan gesture recognition is provided by the `PanGestureRecognizer` class. This class defines the `TouchPoints` property, of type `int`, which represents the number of touch points in the gesture. The default value of this property is 1. This property is backed by a `BindableProperty` object, which means that it can be the target of data bindings, and styled. + +The `PanGestureRecognizer` class also defines a `PanUpdated` event that's raised when the detected pan gesture changes. The `PanUpdatedEventArgs` object that accompanies this event defines the following properties: + +- `GestureId`, of type `int`, which represents the id of the gesture that raised the event. +- `StatusType`, of type `GestureStatus`, which indicates if the event has fired for a newly started gesture, a running gesture, a completed gesture, or a canceled gesture. +- `TotalX`, of type `double`, which indicates the total change in the X direction since the beginning of the gesture. +- `TotalY`, of type `double`, which indicates the total change in the Y direction since the beginning of the gesture. + +## Create a PanGestureRecognizer + +To make a `View` recognize a pan gesture, create a `PanGestureRecognizer` object, handle the `PanUpdated` event, and add the new gesture recognizer to the `GestureRecognizers` collection on the view. The following code example shows a `PanGestureRecognizer` attached to an `Image`: + +```xaml + + + + + +``` + +The code for the `OnPanUpdated` event handler should be added to the code-behind file: + +```csharp +void OnPanUpdated(object sender, PanUpdatedEventArgs e) +{ + // Handle the pan +} +``` + +The equivalent C# code is: + +```csharp +PanGestureRecognizer panGesture = new PanGestureRecognizer(); +panGesture.PanUpdated += (s, e) => +{ + // Handle the pan +}; +image.GestureRecognizers.Add(panGesture); +``` + +## Create a pan container + +Freeform panning is typically suited to navigating within images and maps. The `PanContainer` class, which is shown in the following example, is a generalized helper class that performs freeform panning: + +```csharp +public class PanContainer : ContentView +{ + double x, y; + + public PanContainer() + { + // Set PanGestureRecognizer.TouchPoints to control the + // number of touch points needed to pan + PanGestureRecognizer panGesture = new PanGestureRecognizer(); + panGesture.PanUpdated += OnPanUpdated; + GestureRecognizers.Add(panGesture); + } + + void OnPanUpdated(object sender, PanUpdatedEventArgs e) + { + switch (e.StatusType) + { + case GestureStatus.Running: + // Translate and ensure we don't pan beyond the wrapped user interface element bounds. + Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - DeviceDisplay.MainDisplayInfo.Width)); + Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - DeviceDisplay.MainDisplayInfo.Height)); + break; + + case GestureStatus.Completed: + // Store the translation applied during the pan + x = Content.TranslationX; + y = Content.TranslationY; + break; + } + } +} +``` + +In this example, the `OnPanUpdated` method updates the viewable content of the wrapped view, based on the user's pan gesture. This is achieved by using the values of the `TotalX` and `TotalY` properties of the `PanUpdatedEventArgs` instance to calculate the direction and distance of the pan. The `DeviceDisplay.MainDisplayInfo.Width` and `DeviceDisplay.MainDisplayInfo.Height` properties provide the screen width and screen height values of the device. The wrapped user element is then panned by setting its `TranslationX` and `TranslationY` properties to the calculated values. When panning content in an element that does not occupy the full screen, the height and width of the viewport can be obtained from the element's `Height` and `Width` properties. + +The `PanContainer` class can be wrapped around a `View` so that a recognized pan gesture will pan the wrapped view. The following XAML example shows the `PanContainer` wrapping an `Image`: + +```xaml + + + + + + + +``` + +In this example, when the `Image` receives a pan gesture, the displayed image will be panned. diff --git a/docs/fundamentals/gestures/pinch.md b/docs/fundamentals/gestures/pinch.md new file mode 100644 index 00000000..60db2731 --- /dev/null +++ b/docs/fundamentals/gestures/pinch.md @@ -0,0 +1,138 @@ +--- +title: "Recognize a pinch gesture" +description: "This article explains how to use the pinch gesture to perform interactive zoom of an image in .NET MAUI, at the pinch location." +ms.date: 02/21/2022 +--- + +# Recognize a pinch gesture + +A .NET Multi-platform App UI (.NET MAUI) pinch gesture recognizer is used for performing interactive zoom. A common scenario for the pinch gesture is to perform interactive zoom of an image at the pinch location. This is accomplished by scaling the content of the viewport. + +[!INCLUDE [docs under construction](~/includes/preview-note.md)] + +In .NET MAUI, pinch gesture recognition is provided by the `PinchGestureRecognizer` class, which defines a `PinchUpdated` event that's raised when the detected pinch gesture changes. The `PinchGestureUpdatedEventArgs` object that accompanies the `PinchUpdated` event defines the following properties: + +- `Scale`, of type `double`, which indicates the relative size of the pinch gesture since the last update was received. +- `ScaleOrigin`, of type `Point`, which indicates the updated origin of the pinch's gesture. +- `Status`, of type `GestureStatus`, which indicates if the event has fired for a newly started gesture, a running gesture, a completed gesture, or a canceled gesture. + +## Create a PinchGestureRecognizer + +To make a `View` recognize a pinch gesture, create a `PinchGestureRecognizer` object, handle the `PinchUpdated` event, and add the new gesture recognizer to the `GestureRecognizers` collection on the view. The following code example shows a `PinchGestureRecognizer` attached to an `Image`: + +```xaml + + + + + +``` + +The code for the `OnPinchUpdated` event handler should be added to the code-behind file: + +```csharp +void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e) +{ + // Handle the pinch +} +``` + +The equivalent C# code is: + +```csharp +PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer(); +pinchGesture.PinchUpdated += (s, e) => +{ + // Handle the pinch +}; +image.GestureRecognizers.Add(pinchGesture); +``` + +## Create a pinch container + +The `PinchToZoomContainer` class, which is shown in the following example, is a generalized helper class that can be used to interactively zoom a `View`: + +```csharp +public class PinchToZoomContainer : ContentView +{ + double currentScale = 1; + double startScale = 1; + double xOffset = 0; + double yOffset = 0; + + public PinchToZoomContainer() + { + PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer(); + pinchGesture.PinchUpdated += OnPinchUpdated; + GestureRecognizers.Add(pinchGesture); + } + + void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e) + { + if (e.Status == GestureStatus.Started) + { + // Store the current scale factor applied to the wrapped user interface element, + // and zero the components for the center point of the translate transform. + startScale = Content.Scale; + Content.AnchorX = 0; + Content.AnchorY = 0; + } + if (e.Status == GestureStatus.Running) + { + // Calculate the scale factor to be applied. + currentScale += (e.Scale - 1) * startScale; + currentScale = Math.Max(1, currentScale); + + // The ScaleOrigin is in relative coordinates to the wrapped user interface element, + // so get the X pixel coordinate. + double renderedX = Content.X + xOffset; + double deltaX = renderedX / Width; + double deltaWidth = Width / (Content.Width * startScale); + double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth; + + // The ScaleOrigin is in relative coordinates to the wrapped user interface element, + // so get the Y pixel coordinate. + double renderedY = Content.Y + yOffset; + double deltaY = renderedY / Height; + double deltaHeight = Height / (Content.Height * startScale); + double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight; + + // Calculate the transformed element pixel coordinates. + double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale); + double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale); + + // Apply translation based on the change in origin. + Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0); + Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0); + + // Apply scale factor + Content.Scale = currentScale; + } + if (e.Status == GestureStatus.Completed) + { + // Store the translation delta's of the wrapped user interface element. + xOffset = Content.TranslationX; + yOffset = Content.TranslationY; + } + } +} +``` + +In this example, the `OnPinchUpdated` method updates the zoom level of the wrapped view, based on the user's pinch gesture. This is achieved by using the values of the `Scale`, `ScaleOrigin` and `Status` properties of the `PinchGestureUpdatedEventArgs` object to calculate the scale factor to be applied at the origin of the pinch gesture. The wrapped view is then zoomed at the origin of the pinch gesture by setting its `TranslationX`, `TranslationY`, and `Scale` properties to the calculated values. + +The `PinchToZoomContainer` class can be wrapped around a `View` so that a recognized pinch gesture will zoom the wrapped view. The following XAML example shows the `PinchToZoomContainer` wrapping an `Image`: + +```xaml + + + + + + + +``` + +In this example, when the `Image` receives a pinch gesture, the displayed image will be zoomed-in or out. diff --git a/docs/fundamentals/gestures/swipe.md b/docs/fundamentals/gestures/swipe.md new file mode 100644 index 00000000..2dadabd4 --- /dev/null +++ b/docs/fundamentals/gestures/swipe.md @@ -0,0 +1,174 @@ +--- +title: "Recognize a swipe gesture" +description: "This article explains how to recognize a swipe gesture occurring on a view in .NET MAUI." +ms.date: 02/22/2022 +--- + +# Recognize a swipe gesture + +A .NET Multi-platform App UI (.NET MAUI) swipe gesture recognizer detects when a finger is moved across the screen in a horizontal or vertical direction, and is often used to initiate navigation through content. + +[!INCLUDE [docs under construction](~/includes/preview-note.md)] + +In .NET MAUI, drag gesture recognition is provided by the `SwipeGestureRecognizer` class. This class defines the following properties: + +- `Command`, of type `ICommand`, which is executed when a swipe gesture is recognized. +- `CommandParameter`, of type `object`, which is the parameter that's passed to the `Command`. +- `Direction`, of type `SwipeDirection`, which defines the direction +- `Threshold`, of type `uint`, which represents the minimum swipe distance that must be achieved for a swipe to be recognized, in device-independent units. The default value of this property is 100, which means that any swipes that are less than 100 device-independent units will be ignored. + +These properties are backed by `BindableProperty` objects, which means that they can be targets of data bindings, and styled. + +The `SwipeGestureRecognizer` also defines a `Swiped` event that's raised when a swipe is recognized. The `SwipedEventArgs` object that accompanies the `Swiped` event defines the following properties: + +- `Direction`, of type `SwipeDirection`, indicates the direction of the swipe gesture. +- `Parameter`, of type `object`, indicates the value passed by the `CommandParameter` property, if defined. + +## Create a SwipeGestureRecognizer + +To make a `View` recognize a swipe gesture, create a `SwipeGestureRecognizer` object, set the `Direction` property to a `SwipeDirection` enumeration value (`Left`, `Right`, `Up`, or `Down`), optionally set the `Threshold` property, handle the `Swiped` event, and add the new gesture recognizer to the `GestureRecognizers` collection on the view. The following example shows a `SwipeGestureRecognizer` attached to a `BoxView`: + +```xaml + + + + + +``` + +The equivalent C# code is: + +```csharp +BoxView boxView = new BoxView { Color = Colors.Teal, ... }; +SwipeGestureRecognizer leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left }; +leftSwipeGesture.Swiped += OnSwiped; + +boxView.GestureRecognizers.Add(leftSwipeGesture); +``` + +## Recognize the swipe direction + +The `SwipeGestureRecognizer.Direction` property can be set to a single value from the `SwipeDirection` enumeration, or multiple values. This enables the `Swiped` event to be raised in response to a swipe in more than one direction.However, the constraint is that a single `SwipeGestureRecognizer` can only recognize swipes that occur on the same axis. Therefore, swipes that occur on the horizontal axis can be recognized by setting the `Direction` property to `Left` and `Right`: + +```xaml + +``` + +Similarly, swipes that occur on the vertical axis can be recognized by setting the `Direction` property to `Up` and `Down`: + +```csharp +SwipeGestureRecognizer swipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Up | SwipeDirection.Down }; +``` + +Alternatively, a `SwipeGestureRecognizer` for each swipe direction can be created to recognize swipes in every direction: + +```xaml + + + + + + + + +``` + +The equivalent C# code is: + +```csharp +BoxView boxView = new BoxView { Color = Colors.Teal, ... }; +SwipeGestureRecognizer leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left }; +leftSwipeGesture.Swiped += OnSwiped; +SwipeGestureRecognizer rightSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Right }; +rightSwipeGesture.Swiped += OnSwiped; +SwipeGestureRecognizer upSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Up }; +upSwipeGesture.Swiped += OnSwiped; +SwipeGestureRecognizer downSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Down }; +downSwipeGesture.Swiped += OnSwiped; + +boxView.GestureRecognizers.Add(leftSwipeGesture); +boxView.GestureRecognizers.Add(rightSwipeGesture); +boxView.GestureRecognizers.Add(upSwipeGesture); +boxView.GestureRecognizers.Add(downSwipeGesture); +``` + +## Respond to a swipe + +A recognized swipe can be responded to by a handler for the `Swiped` event: + +```csharp +void OnSwiped(object sender, SwipedEventArgs e) +{ + switch (e.Direction) + { + case SwipeDirection.Left: + // Handle the swipe + break; + case SwipeDirection.Right: + // Handle the swipe + break; + case SwipeDirection.Up: + // Handle the swipe + break; + case SwipeDirection.Down: + // Handle the swipe + break; + } +} +``` + +The `SwipedEventArgs` can be examined to determine the direction of the swipe, with custom logic responding to the swipe as required. The direction of the swipe can be obtained from the `Direction` property of the event arguments, which will be set to one of the values of the `SwipeDirection` enumeration. In addition, the event arguments also have a `Parameter` property that will be set to the value of the `CommandParameter` property, if defined. + +## Create a swipe container + +The `SwipeContainer` class, which is shown in the following example, is a generalized swipe recognition class that be wrapped around a `View` to perform swipe gesture recognition: + +```csharp +public class SwipeContainer : ContentView +{ + public event EventHandler Swipe; + + public SwipeContainer() + { + GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Left)); + GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Right)); + GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Up)); + GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Down)); + } + + SwipeGestureRecognizer GetSwipeGestureRecognizer(SwipeDirection direction) + { + SwipeGestureRecognizer swipe = new SwipeGestureRecognizer { Direction = direction }; + swipe.Swiped += (sender, e) => Swipe?.Invoke(this, e); + return swipe; + } +} +``` + +The `SwipeContainer` class creates `SwipeGestureRecognizer` objects for all four swipe directions, and attaches `Swipe` event handlers. These event handlers invoke the `Swipe` event defined by the `SwipeContainer`. + +The following XAML code example shows the `SwipeContainer` class wrapping a `BoxView`: + +```xaml + + + + + +``` + +In this example, when the `BoxView` receives a swipe gesture, the `Swiped` event in the `SwipeGestureRecognizer` is raised. This is handled by the `SwipeContainer` class, which raises its own `Swipe` event. This `Swipe` event is handled on the page. The `SwipedEventArgs` can then be examined to determine the direction of the swipe, with custom logic responding to the swipe as required. + +The equivalent C# code is: + +```csharp +BoxView boxView = new BoxView { Color = Colors.Teal, ... }; +SwipeContainer swipeContainer = new SwipeContainer { Content = boxView, ... }; +swipeContainer.Swipe += (sender, e) => +{ + // Handle the swipe +}; + +StackLayout stackLayout = new StackLayout(); +stackLayout.Add(swipeContainer); +``` diff --git a/docs/fundamentals/gestures/tap.md b/docs/fundamentals/gestures/tap.md new file mode 100644 index 00000000..96fe0faa --- /dev/null +++ b/docs/fundamentals/gestures/tap.md @@ -0,0 +1,56 @@ +--- +title: "Recognize a tap gesture" +description: "This article explains how to recognize a tap gesture in a .NET MAUI app." +ms.date: 02/23/2022 +--- + +# Recognize a tap gesture + +A .NET Multi-platform App UI (.NET MAUI) tap gesture recognizer is used for tap detection and is implemented with the `TapGestureRecognizer` class. + +[!INCLUDE [docs under construction](~/includes/preview-note.md)] + +In .NET MAUI, drag gesture recognition is provided by the `SwipeGestureRecognizer` class. This class defines the following properties: + +- `Command`, of type `ICommand`, which is executed when a tap is recognized. +- `CommandParameter`, of type `object`, which is the parameter that's passed to the `Command`. +- `NumberOfTapsRequired`, of type `int`, which represents the number of taps required to recognize a tap gesture. The default value of this property is 1. + +These properties are backed by `BindableProperty` objects, which means that they can be targets of data bindings, and styled. + +The `TapGestureRecognizer` class also defines a `Tapped` event that's raised when a tap is recognized. The `TappedEventArgs` object that accompanies the `Tapped` event defines a `Parameter` property, of type `object`, that indicates the value passed by the `CommandParameter` property, if defined. + +## Create a TapGestureRecognizer + +To make a `View` recognize a tap gesture, create a `TapGestureRecognizer` object, handle the `Tapped` event, and add the new gesture recognizer to the `GestureRecognizers` collection on the view. The following code example shows a `TapGestureRecognizer` attached to an `Image`: + +```xaml + + + + + +``` + +The code for the `OnTapGestureRecognizerTapped` event handler should be added to the code-behind file: + +```csharp +void OnTapGestureRecognizerTapped(object sender, EventArgs args) +{ + // Handle the tap +} +``` + +The equivalent C# code is: + +```csharp +TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer(); +tapGestureRecognizer.Tapped += (s, e) => +{ + // Handle the tap +}; +image.GestureRecognizers.Add(tapGestureRecognizer); +``` + +By default the `Image` will respond to single taps. When the `NumberOfTapsRequired` property is set above one, the event handler will only be executed if the taps occur within a set period of time. If the second (or subsequent) taps do not occur within that period they are effectively ignored.