Search code examples
c#mauicontentview

Use pan gesture to move view around the screen


I'm working on a Custom View on .net MAUI.

In this Custom View, I would like to load an Image and be able to move it around the screen using Pan Gesture recognizer.

I was following this tutorial. As first step, I'm just using a Border to test the behaviour.


This is my custom control:

SignatureView.xaml.cs

public partial class SignatureView : ContentView
{
    private double panX;
    private double panY;
    public SignatureView()
    {
        InitializeComponent();
    }

    private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Running:
                double boundsX = Signature.Width;
                double boundsY = Signature.Height;
                Signature.TranslationX = Math.Clamp(panX + e.TotalX, -boundsX, boundsX);
                Signature.TranslationY = Math.Clamp(panY + e.TotalY, -boundsY, boundsY);
                break;

            case GestureStatus.Completed:
                panX = Signature.TranslationX;
                panY = Signature.TranslationY;
                break;
        }
    }
}

SignatureView.xaml

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.Controls.SignatureView">

    <AbsoluteLayout BackgroundColor="White">
        <Border x:Name="Signature" StrokeShape="RoundRectangle 50" BackgroundColor="Blue" HeightRequest="50" WidthRequest="50">
            <Border.GestureRecognizers>
                <PanGestureRecognizer PanUpdated="PanGestureRecognizer_PanUpdated"/>
            </Border.GestureRecognizers>
        </Border>
    </AbsoluteLayout>
<ContentView/>

This is how I'm using this control:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MyApp.Controls"
             x:Class="MyApp.Views.MyPage">

    <ContentPage.Content>
        <controls:SignatureView/>
   </ContentPage.Content>
</ContentPage>

This is the behaviour I'm seeing:

Maui Pan Gesture Recognizer

  1. The view only can move on a small portion of the screen
  2. The view is jumping around while is being moved
  3. Is it possible to limit the view moving only inside its AbsoluteLayout parent? So if its AbsoluteLayout parent has a Padding, the view cannot go outside this Padding?

Am I doing something wrong? How could I smoothly move the view around?


Solution

  • You may make some changes to PanGestureRecognizer_PanUpdated to handle pan gesture for MAUI android. Actually, it needs some calculation when moving the Border to make it always inside the AbsoluteLayout. You may try the following code,

    private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Started:
                break;
    
            case GestureStatus.Running:
    
                var parent = Signature.Parent as AbsoluteLayout;
                
                //control the x-axis direction move
                if (Signature.TranslationX + Signature.Width + e.TotalX <= parent.Width & Signature.TranslationX  + e.TotalX >= 0)
                {
                    Signature.TranslationX += e.TotalX;
                }
                else if (Signature.TranslationX + Signature.Width + e.TotalX > parent.Width)
                {
                    Signature.TranslationX = parent.Width - Signature.Width;
                }
                else
                {
                    Signature.TranslationX = 0;
                }
                
                 //control the y-axis direction move
                if (Signature.TranslationY + Signature.Height + e.TotalY <= parent.Height & Signature.TranslationY + e.TotalY >= 0)
                {
                    Signature.TranslationY += e.TotalY;
                }
                else if(Signature.TranslationY + Signature.Height + e.TotalY > parent.Height)
                {
                    Signature.TranslationY = parent.Height - Signature.Height;
                }
                else
                { 
                    Signature.TranslationY = 0; 
                }
       
                break;
            case GestureStatus.Completed:
                break;
        }
    } 
    

    Futhermore, if you want to set a Padding for AbsoluteLayout, such like,

    public SignatureView()
    {
        InitializeComponent();    
        myAbsoluteLayout.Padding = new Thickness(20, 20, 20, 20);        
    }
    

    Then the calculation might be more complex. Because you have to take the Padding size into consideration when moving. Here is an example to achieve this,

    private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Started:
                break;
    
            case GestureStatus.Running:
    
                var parent = Signature.Parent as AbsoluteLayout;
                //control the x-axis direction move
                if (Signature.TranslationX + Signature.Width  + e.TotalX + parent.Padding.Left + parent.Padding.Right <= parent.Width & 
                     Signature.TranslationX  + e.TotalX - parent.Padding.Left >= 0)
                {
                    Signature.TranslationX += e.TotalX;
                }
                else if (Signature.TranslationX + Signature.Width + e.TotalX + parent.Padding.Left + parent.Padding.Right > parent.Width)
                {
                    Signature.TranslationX = parent.Width - Signature.Width - parent.Padding.Left - parent.Padding.Right;
                }
                else
                {
                    Signature.TranslationX = 0;
                }
                    
                //control the y-axis direction move
                if (Signature.TranslationY + Signature.Height + e.TotalY + parent.Padding.Top + parent.Padding.Bottom <= parent.Height & 
                    Signature.TranslationY + e.TotalY -parent.Padding.Top >= 0)
                {
                    Signature.TranslationY += e.TotalY;
                }
                else if(Signature.TranslationY + Signature.Height + e.TotalY + parent.Padding.Top + parent.Padding.Bottom > parent.Height)
                {
                    Signature.TranslationY = parent.Height - Signature.Height - parent.Padding.Top - parent.Padding.Bottom;
                }
                else
                { 
                    Signature.TranslationY = 0; 
                }
    
                       
                break;
            case GestureStatus.Completed:
    
                break;
        }
    }
    

    Hope it helps!