Xamarin.Tip – Xamarin.Forms Long Press Effect

Here’s a quick and helpful tool to use in your Xamarin.Forms applications! How many times have you wanted to add a long press handler? Seems like something that should be a simple Gesture built into the platform, but we have to fend for ourselves. Luckily the solution is pretty simple using Xamarin.Forms Effects!

Let’s first create our shared Effect in our shared code:

LongPressedEffect.cs

    /// <summary>
    /// Long pressed effect. Used for invoking commands on long press detection cross platform
    /// </summary>
    public class LongPressedEffect : RoutingEffect
    {
        public LongPressedEffect() : base("MyApp.LongPressedEffect")
        {
        }

        public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached("Command", typeof(ICommand), typeof(LongPressedEffect), (object)null);
        public static ICommand GetCommand(BindableObject view)
        {
            return (ICommand)view.GetValue(CommandProperty);
        }

        public static void SetCommand(BindableObject view, ICommand value)
        {
            view.SetValue(CommandProperty, value);
        }


        public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached("CommandParameter", typeof(object), typeof(LongPressedEffect), (object)null);
        public static object GetCommandParameter(BindableObject view)
        {
            return view.GetValue(CommandParameterProperty);
        }

        public static void SetCommandParameter(BindableObject view, object value)
        {
            view.SetValue(CommandParameterProperty, value);
        }
    }

Now we have 2 bindable properties – the Command that we want to bind when the long press is detected and the CommandParameter to pass into the Command.

We can use these in our native Effect implementations to invoke when the press is detected. Let’s create our Android implementation.

AndroidLongPressedEffect.cs

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(AndroidLongPressedEffect), "LongPressedEffect")]
namespace AndroidAppNamespace.Effects
{
    /// <summary>
    /// Android long pressed effect.
    /// </summary>
    public class AndroidLongPressedEffect : PlatformEffect
    {
        private bool _attached;

        /// <summary>
        /// Initializer to avoid linking out
        /// </summary>
        public static void Initialize() { }

        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="T:Yukon.Application.AndroidComponents.Effects.AndroidLongPressedEffect"/> class.
        /// Empty constructor required for the odd Xamarin.Forms reflection constructor search
        /// </summary>
        public AndroidLongPressedEffect()
        {
        }

        /// <summary>
        /// Apply the handler
        /// </summary>
        protected override void OnAttached()
        {
            //because an effect can be detached immediately after attached (happens in listview), only attach the handler one time.
            if (!_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.LongClick += Control_LongClick;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.LongClick += Control_LongClick;
                }
                _attached = true;
            }
        }

        /// <summary>
        /// Invoke the command if there is one
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">E.</param>
        private void Control_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
        {
            Console.WriteLine("Invoking long click command");
            var command = LongPressedEffect.GetCommand(Element);
            command?.Execute(LongPressedEffect.GetCommandParameter(Element));
        }

        /// <summary>
        /// Clean the event handler on detach
        /// </summary>
        protected override void OnDetached()
        {
            if (_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.LongClick -= Control_LongClick;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.LongClick -= Control_LongClick;
                }
                _attached = false;
            }
        }
    }

And now for iOS:

iOSLongPressedEffect.cs

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(iOSLongPressedEffect), "LongPressedEffect")]
namespace iOSNamespace.Effects
{
    /// <summary>
    /// iOS long pressed effect
    /// </summary>
    public class iOSLongPressedEffect : PlatformEffect
    {
        private bool _attached;
        private readonly UILongPressGestureRecognizer _longPressRecognizer;
        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="T:Yukon.Application.iOSComponents.Effects.iOSLongPressedEffect"/> class.
        /// </summary>
        public iOSLongPressedEffect()
        {
            _longPressRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
        }

        /// <summary>
        /// Apply the handler
        /// </summary>
        protected override void OnAttached()
        {
            //because an effect can be detached immediately after attached (happens in listview), only attach the handler one time
            if (!_attached)
            {
                Container.AddGestureRecognizer(_longPressRecognizer);
                _attached = true;
            }
        }

        /// <summary>
        /// Invoke the command if there is one
        /// </summary>
        private void HandleLongClick()
        {
            var command = LongPressedEffect.GetCommand(Element);
            command?.Execute(LongPressedEffect.GetCommandParameter(Element));
        }

        /// <summary>
        /// Clean the event handler on detach
        /// </summary>
        protected override void OnDetached()
        {
            if (_attached)
            {
                Container.RemoveGestureRecognizer(_longPressRecognizer);
                _attached = false;
            }
        }

    }

Now that we have our 2 implementations, let’s use it in our XAML!

MyPage.xaml

<Label Text="Long Press Me!" effects:LongPressedEffect.Command="{Binding ShowAlertCommand}" effects:LongPressedEffect.CommandParameter="{Binding .}">
    <Label.Effects>
        <effects:LongPressedEffect />
    </Label.Effects>
</Label>

Now you can start handling long presses on any control! If you want to add it to a ListView just attach it to either the ViewCell or the internal View of the cell.

 

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

98 thoughts on “Xamarin.Tip – Xamarin.Forms Long Press Effect”

    1. Hi Alex,
      I am trying to implement the above in a test app but have issues with “The name ‘LongPressedEffect’ does not exist in the current context’ in both the Android and iOS cs files.
      I would be grateful and any pointers in the right direction if possible.

      Like

  1. I keep receiving an exception: System.NullReferenceException: Object reference not set to an instance of an object.

    This occurs just after the constructor of the LongPressedEffect object. Anyone else experience this issue?

    Like

      1. Hello, Alex.

        I am just creating a brand-new cross-platform app and adding a basic ListView and your code. I want to be absolutely sure that it is not something on my side.

        Like

  2. Hi Alex,
    I sorted the above issue i had to delete the bin and obj folder in each project and rebuild.
    However I cannot get the binding to work could you please provide a sample on how to do it.
    Thank you.

    Like

  3. I was able to advance so that I am no longer experience the exception. My code compiles and runs. However, when I long press an item in my ListView, nothing happens. No breakpoints are hit, nothing appears in the Output window. Here is my ListView:

    My LongPressedCommand is defined and set.

    Like

      1. I will send to you the XAML code in a few minutes.

        It is interesting to note that the code compiles and executes. I put Breakpoints at the beginning of every method in these classes, as well as at the beginning of my command.

        When I long press, nothing happens. Nothing is printed to the Output window and no Breakpoint is hit.

        Like

      1. That’s what you’re binding your long pressed command to.
        CustomEffects:LongPressedEffect.Command=”{Binding LongPressedCommand}” CustomEffects:LongPressedEffect.CommandParameter=”{Binding .}”

        The Command is going to bind to the Command within the current BindingContext (which is your project list item since you put the effect on the individual cell) which you’ve named LongPressedCommand

        Like

      2. No, I wanted to see both. Your list is bound to a collection of `Project` models. But the `Project` model does not have the LongPressedCommand – the view model does, but you are trying to bind it the command to the LongPressedCommand on the `Project` model which does not exist

        Like

      3. So your options are to either create a command on the project model or change the Binding to point at the parent binding context rather than the individual project item.

        Like

      4. My Project is a regular POCO and knows nothing about the XMAL page or its View Model.
        My bindings are incorrect, in other words. Let me work on that…
        Thank you.

        Like

      5. Try something like this:

        Your listview already has the x:Name of “listView”, so you can change the binding to:
        CustomEffects:LongPressedEffect.Command=”{Binding Path=BindingContext.LongPressedCommand,
        Source={x:Reference listView}}”

        Liked by 1 person

  4. Ray Said> Would you happen to have snippets of code or a working demo of the effect and a ListView?

    Alex if you have the time that would be fantastic, i am trying to get this to work with a button.
    I have had a look on your git-hub and coudnt find anything.

    Like

      1. Testing the effect I have breakpoints on effect. It attaches event, but when doing a long press the event never fires. I guess the LongClick event in Xamarin Forms is not triggering. I’ve tested on all versions of Xamarin Forms 2.4.*.

        Like

  5. Andriod only project, view in PCL project. Snippet of the XAML

    I have switch between the original XF version I had (2.3.4.247) to various versions of 2.4 without any other changes and it stops working

    Like

    1. Have you tried using it outside a listview just to check? I think it may be conflicting with the listview item context actions since on Android it’s invoked with a long press. In 2.4 they added a fix for an Android bug that broke the context actions. That bug is actually the original reason I ever made the effect lol

      Like

    1. It does work if you actually bind the command to anything. I have a video of it too. The problem is your command is not actually bound to anything, so the effect sees the command as null. In your main page change LongPressedEffect.Command=”{Binding}” to LongPressedEffect.Command=”{Binding LongPressed}” since you named your command “LongPressed”.

      Liked by 1 person

      1. Yes!, thanks to Alex and Darren, Thats works! with labels, although with grid, stacklayout, viewcell does not works.. if you know how to make it work please tell me.

        Like

    2. I downloaded this project and after correcting the bind issue that Alex found, I can confirm that the Label does trigger the long-press effect. I also added an Image, for fun. The long-press effect worked for the image!

      However, after adding a ListView and adding the long-press effect to the TextCell, the long-press effect does not trigger. I need the effect on the ListView.

      Like

  6. I’ve tested on various controls on versions of Xamarin Forms from 2.3 – 2.4 and works fine.
    But StackLayout does not work. It attaches as a container to the LongClick event but never fires.
    I am assuming that allow me to attach the event means so should it be reported as a bug?

    Like

  7. Thank you Darren for your Longpressed sample app on GitHub and Alex for the binding fix.
    i am still trying to get this to work with a button (longpress to show\hide a stack layout) and also an Entry ( longpress to clear the entered text).
    i seem to be going round in circles could one of you point me in the right direction please.

    Like

  8. Hi Darren,
    Thank you once again for your help with this i now have it working as i like with a binding Boolean on a stack layout and a binding command sending the button name (i couldn’t work out how to send the button id).
    I know you have no iOS sample so this one maybe Alex can pick up on this if time available.
    When testing with iOS the hidden stacklayout will show but immediately hide again after the button release.
    any ideas?
    Thanks again for your time.

    Like

  9. Hi everyone

    If anybody has an update on how to make it work for a ListView, that would be appreciated.
    Great blog by the way Alex Dunn.!

    Thank you!

    Liked by 1 person

    1. So this is working for me on both OS’s (not all versions of Android) with Xam.Forms 2.5 and I’m using it in production. I’m wondering if you are having issues with a given OS version or if something else is overriding the long press handler like adding ContextActions to your ViewCells. Anything like that going on?

      Liked by 1 person

      1. It’s working beautifully. Thanks a lot for this effect!
        But it’s overriding my TapGestureRecognizer. I’m using it on an .

        Liked by 1 person

      2. It’s working beautifully. Thanks a lot for this effect!
        But it’s overriding my TapGestureRecognizer. I’m using it on an Image tag.

        Liked by 1 person

  10. Hi Alex

    I appreciate the fast response. I’m currently trying to implement this effect on Android and yes, i’m using a ContextAction on my ListView (ListView.ItemSelected). However, even without the context action, the desire effect does not work on Android for the List View. In this example: https://github.com/lepijulien/LongPressEffectOnListView, you can see my implementation of the effect on a Label and a ListView (the effect works on a Label). If you have the time to check it out, please tell me what I’m doing wrong for the implementation of the effect on a ListView.

    Liked by 1 person

  11. I’ve been trying this for a while now and I’ve managed to get it running on Labels, but it doesn’t really work with ListView in my project. Well, it does, just not in combination with ItemTapped or ItemSelected.

    Is it possible to use the LongPress effect AND ItemTapped together? I’ve managed to do it if the LongPress effect is on a Label, but then it only works if I hit the Label and the regular tap only works if I do not hit the Label.

    Are the two mutual exlusive? Do I need to create a TappedAndLongTappedEffect?

    Like

    1. I have it working with both, but it’s inconsistent for Android (some devices and some versions of the OS are only handling the item selected), but iOS item selected and long press on the viewcell or subviews works perfectly

      Like

      1. Thank you for the quick answer: But just to clarify, do you bind the effect on the ViewCell or something within it?

        Like

  12. Both gesture and your fix doesn’t work in tendem. If I add long press as per your code, gesture recognizers which I have added stop working

    Like

  13. Thank you for this example 🙂

    It works great but I had to modify the iOS implementation a bit.
    Because auf the UILongPressGestureRecognizer fired twice I’ve added the if-clause below to the HandleLongClick-Method:

    private void HandleLongClick()
    {
    if(_longPressRecognizer.State == UIGestureRecognizerState.Began)
    {
    var command = LongPressedEffect.GetCommand(Element);
    command?.Execute(LongPressedEffect.GetCommandParameter(Element));
    }
    }

    I had the same issue using native Swift too so I don’t think it’s a Xamarin-Problem

    Like

  14. Hello. I’m trying to implement this and it is not working in my application.

    I’ve tried to run the app examples posted here, like the one who Garren shared, and it is working correctly. I guess that my problem is that i’m not using MVVM in my app and the data binding is failing.

    It is required to have MVVM in my project or am I doing something wrong? I’m just creating the commands on the .cs file of my Page and binding them on the XAML like this: {Binding myCommand}

    Thanks.

    Like

    1. If your BindingContext is set to your page, then it will work. You can also just set it in your C# code directly instead of doing a mix of C# and XAML. Doesn’t require full MVVM

      Like

      1. Yeah I guessed that but still without working.

        As far as I know the binding context is set by default to the .cs of the page, but I’m not sure at all. I even tried to set ‘BindingContext = this” on the constructor but had no effect.

        Is there any other way to set the binding page?

        Like

      2. All right I got this working. The problem was that I need to initialize the command before the ‘InitializeComponent()’. I guess that’s because i’m not implementing the INotifyPropertyChanged.

        However, it is not working in the ViewCell of the listview or any view inside it.

        Like

  15. Hi Alex,
    Thanks for the tip. My issue is. This function is not been called
    private void Control_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
    {
    Console.WriteLine(“Invoking long click command”);
    var command = LongPressedEffect.GetCommand(Element);
    command?.Execute(LongPressedEffect.GetCommandParameter(Element));
    }

    Like

      1. that is not a the case. I have figure that out, it is working with Label but not with ViewCell or Listview.

        Like

  16. Is there any way to increase the longclick time on Android OS?
    I have both click and long click configured on StackLayout. I have multiple such stack layouts inside scroll view. When I scroll, it triggers the long click randomly. I can’t figure out why?

    My code is exactly similar to your code shown above.

    Like

  17. Sir,

    I need add longPressedEffect to my instant added control.
    I use methot below. But I can’t fire.
    Could You advice me ?

    Best Regards…

    NewControl.SetBinding(LongPressedEffect.CommandProperty, new Binding(“LongPressedButton”));
    NewControl.SetBinding(LongPressedEffect.CommandParameterProperty, new Binding(“.”));

    Like

  18. I implemented the long tap on controls that should also support a click event. In my case, I’m using a converter to convert ItemSelected event into a command. This works fine with the long tap on iOS, but on Android, the item selected event doesn’t fire correctly–it takes several presses to get it to work.

    My next solution was to handle the short click gestures similar to the long press gestures, but when I tried to add a Click handler, I saw in Andorid.Views.View it doesn’t have the same method signature as the LongClick does.

    public event System.EventHandler LongClick

    public event System.EventHandler Click

    Or should ContextClick or Touch be used in place of Click to implement this gesture?

    Liked by 1 person

  19. Great tutorial !
    I have an issue that I seems can not rap my head around it .
    I have a collection view with a list of objects .
    I want when long pressed an Item in the list view gets the objects so i can remove it from the list .

    everything is working good except i dont know how to get the long pressed item

    here is my code inside a collecton view :

    Like

  20. I implemented your solution in my project. I soon noticed that I could not use ItemTapped or ItemSelected in my ListView. The only way I found around this problem, was to handle the click event also in the effect. Seems to work fine. Thanks!

    Liked by 1 person

  21. I know I’m late to the party, but I want to love this solution – just not getting any valid Command object.
    LongPressedEffect.GetCommand(Element) always returning null, so never gets executed.

    Using xaml with this part:

    MainPage.cs has:
    public void ShowAlertCommand(object sender)
    {
    Console.WriteLine(“sender:” + sender.ToString());
    DisplayAlert(“Alert”, “Long press clicked”, “OK”);
    }

    I have been unable to find any working sample code online (cross platform ios)
    Maybe someone can help guide me?

    Like

  22. This is what is looking for.. It’s working awesome in android.. in ios command hit twice.. otherwise no problem..What may be the issue? Alex Dunn

    Like

Leave a comment