How to make long press gesture in Xamarin Forms?

2020-01-31 02:34发布

Could you please let me know how can I recognize long press gesture in Xamarin Forms application?

A few days before I used TapGestureRecognizer

TapGestureRecognizer imageTap = new TapGestureRecognizer();
imageTap.Tapped += (sender, args) => this.OnClickImage;
image.GestureRecognizers.Add(imageTap);

But I don't know how to make long press gesture according to this thread from xamarin forum

It should looks something like this, but it does not work.

var dumpParam = new RelayGesture((g, x) => DisplayAlert("Title", "Hello message", "Cancel"));

book.Cover.SetValue(Gestures.InterestsProperty, new GestureCollection() {
    new GestureInterest
        {
            GestureType = GestureType.LongPress
            GestureCommand = // what should I set?
            GestureParameter = dumpParam
        }
 });

How to set my custom handler method?

6条回答
劫难
2楼-- · 2020-01-31 02:47

Make use of XLabs.Forms nuget package, which make long press and other gesture in PCL code only. Use of XLabs.Forms package will reduce the need of custom rendering in individual platforms... Add XAML code in .xaml file and attached event handler in .xaml.cs file.. It is working fine in Android..

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="MultiImage.Page1"             
         xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
         xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms">
<ContentPage.Content>
    <lc:GesturesContentView ExcludeChildren="False" GestureRecognized="GesturesContentView_GestureRecognized">
         <lb:Gestures.Interests>
                <lb:GestureCollection>
                    <lb:GestureInterest GestureType="SingleTap"/>
                    <lb:GestureInterest GestureType="LongPress"/>
                    <lb:GestureInterest GestureType="DoubleTap"/>
                </lb:GestureCollection>
          </lb:Gestures.Interests>
          <Image Source="Myimage.png" Aspect="AspectFit" HeightRequest="100"/>
        </lc:GesturesContentView>
</ContentPage.Content>

C# backend code:

private void GesturesContentView_GestureRecognized(object sender, GestureResult e)
{           
    switch (e.GestureType)
    {
        case GestureType.LongPress:
            //Add code here
            break;
        case GestureType.SingleTap:
            // Add code here                    
            break;
        case GestureType.DoubleTap:
            // Add code here
            break;
        default:
            break;
    }
查看更多
Viruses.
3楼-- · 2020-01-31 03:05

You can do it cross platform way by attaching the below behavior, as long as it is Xamarin.Forms.Button or a sub-type of it.

using System;
using System.Threading;
using System.Windows.Input;
using Xamarin.Forms;

namespace App.Controls.Behaviors
{
    public class LongPressBehavior : Behavior<Button>
    {
        private readonly object _syncObject = new object();
        private const int Duration = 1000;

        //timer to track long press
        private Timer _timer;
        //the timeout value for long press
        private readonly int _duration;
        //whether the button was released after press
        private volatile bool _isReleased;

        /// <summary>
        /// Occurs when the associated button is long pressed.
        /// </summary>
        public event EventHandler LongPressed;

        public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),
            typeof(ICommand), typeof(LongPressBehavior), default(ICommand));

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(LongPressBehavior));

        /// <summary>
        /// Gets or sets the command parameter.
        /// </summary>
        public object CommandParameter
        {
            get => GetValue(CommandParameterProperty);
            set => SetValue(CommandParameterProperty, value);
        }

        /// <summary>
        /// Gets or sets the command.
        /// </summary>
        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

        protected override void OnAttachedTo(Button button)
        {
            base.OnAttachedTo(button);
            this.BindingContext = button.BindingContext;
            button.Pressed += Button_Pressed;
            button.Released += Button_Released;
        }

        protected override void OnDetachingFrom(Button button)
        {
            base.OnDetachingFrom(button);
            this.BindingContext = null;
            button.Pressed -= Button_Pressed;
            button.Released -= Button_Released;
        }

        /// <summary>
        /// DeInitializes and disposes the timer.
        /// </summary>
        private void DeInitializeTimer()
        {
            lock (_syncObject)
            {
                if (_timer == null)
                {
                    return;
                }
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
                _timer.Dispose();
                _timer = null;
                Debug.WriteLine("Timer disposed...");
            }
        }

        /// <summary>
        /// Initializes the timer.
        /// </summary>
        private void InitializeTimer()
        {
            lock (_syncObject)
            {
                _timer = new Timer(Timer_Elapsed, null, _duration, Timeout.Infinite);
            }
        }

        private void Button_Pressed(object sender, EventArgs e)
        {
            _isReleased = false;
            InitializeTimer();
        }

        private void Button_Released(object sender, EventArgs e)
        {
            _isReleased = true;
            DeInitializeTimer();
        }

        protected virtual void OnLongPressed()
        {
            var handler = LongPressed;
            handler?.Invoke(this, EventArgs.Empty);
            if (Command != null && Command.CanExecute(CommandParameter))
            {
                Command.Execute(CommandParameter);
            }
        }

        public LongPressBehavior()
        {
            _isReleased = true;
            _duration = Duration;
        }

        public LongPressBehavior(int duration) : this()
        {
            _duration = duration;
        }

        private void Timer_Elapsed(object state)
        {
            DeInitializeTimer();
            if (_isReleased)
            {
                return;
            }
            Device.BeginInvokeOnMainThread(OnLongPressed);
        }
    }
}

In the XAML UI:

 <Button x:Name="MyButton" Text="Long Press Me!">
   <Button.Behaviors>
     <behaviors:LongPressBehavior LongPressed="MyButton_LongPressed"/>
   </Button.Behaviors>
 </Button>

XAML UI with Command Binding:

 <Button x:Name="MyButton" Text="Long Press Me!">
   <Button.Behaviors>
     <behaviors:LongPressBehavior Command="{Binding CommandInViewModel}"/>
   </Button.Behaviors>
 </Button>
查看更多
Evening l夕情丶
4楼-- · 2020-01-31 03:05

I recently came across this problem and found a useful post on the topic https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/

This makes use of the RoutingEffect and goes through an example of how to create both iOS and Android implementation. The simplicity of this allows you to attach it to any view in your app without recreating code.

查看更多
我只想做你的唯一
5楼-- · 2020-01-31 03:09
//To Add Programatically:

StackLayout _Containter = new StackLayout();
StackLayout _StackLayout = new StackLayout();
 _StackLayout.Children.Add(new Label(){Text="Execute Me"});

GesturesContentView Gv = new GesturesContentView();
_StackLayout.SetValue(XLabs.Forms.Behaviors.Gestures.InterestsProperty, new GestureCollection() {
                      new GestureInterest() { GestureType = GestureType.SingleTap },
                      new GestureInterest() { GestureType = GestureType.LongPress },
                      new GestureInterest() { GestureType = GestureType.DoubleTap }
            });
Gv.GestureRecognized += Gv_GestureRecognized;
Gv.ExcludeChildren = false;
    Gv.Content = _StackLayout;
_Containter.Children.Add(Gv);
查看更多
爷的心禁止访问
6楼-- · 2020-01-31 03:11

Surfing the internet I found the solution. There are few steps which you should reproduce.

1) Inherit the control you need the gestures on (i.e. if you want to add gesture to Xamarin.Forms.Image, create you own ImageWithLongPressGesture class).

public class ImageWithLongPressGesture : Xamarin.Forms.Image
{
    public EventHandler LongPressActivated;

    public void HandleLongPress(object sender, EventArgs e)
    {
        //Handle LongPressActivated Event
    }
}

2) Expose public events for the needed gestures.

3) Create a Renderer for each platform.

4) In the Renderer, handle the gestures and bubble them to your control.

[assembly: ExportRenderer(typeof(ImageWithLongPressGesture), typeof(LongPressGestureRecognizerImageRenderer))]
namespace App1.Droid.DroidRenderers
{
    public class LongPressGestureRecognizerImageRenderer : ImageRenderer
    {
        ImageWithLongPressGesture view;

        public LongPressGestureRecognizerImageRenderer()
        {
            this.LongClick += (sender, args) => {
                Toast.MakeText(this.Context, "Long press is activated.", ToastLength.Short).Show();
            };
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);

            if(e.NewElement != null)
            {
                view = e.NewElement as ImageWithLongPressGesture;
            }
        }
    }
}

This solution is a hybrid of answer on xamarin forms forum and Touch and Gestures presentation by Telerik.

查看更多
劫难
7楼-- · 2020-01-31 03:14

In order to get this to work properly on iOS, you need to use XLabs.Forms.XFormsAppiOS.Init(); in your AppDelegate.cs file just before the LoadApplication(new App()); statement.

查看更多
登录 后发表回答