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.
Very nice! Ive been looking for thiS
LikeLike
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.
LikeLike
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?
LikeLike
Mind pasting the stacktrace?
LikeLike
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.
LikeLike
I’ve been able to use it in a listview which is why I want to see the stacktrace to see what the underlying issue might be
LikeLike
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.
LikeLike
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.
LikeLike
Hmm, my XAML was deleted from my posting.
LikeLike
Yeah the WordPress comments kill XML and other detected code. Try DMing it to me on Twitter @Suave_Pirate
LikeLike
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.
LikeLike
Your effect might be getting linked out then
LikeLike
I couldn’t send you a DM via Twitter.
LikeLike
Here is the XAML code:
https://codeshare.io/5wqkVx
LikeLike
I’m not seeing where you added the effect
LikeLike
Yes, sorry. I removed it because it was not working. I forgot to pu it back. Refresh the page, please.
LikeLike
Okay I see it now. I assume your model that is bound to each view cell has a `LongPressedCommand` property? Can you share those models and your page view model as well?
LikeLiked by 1 person
No, I did not add such a property to my Project model. I did not see that we needed to do that.
LikeLike
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
LikeLike
Here is the View Model:
https://codeshare.io/2jkz1A
LikeLike
Sorry! You meant to say “view model”. I just saw “model” as in data entity.
LikeLike
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
LikeLike
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.
LikeLike
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.
LikeLike
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}}”
LikeLiked by 1 person
Not quite, but I’ll keep working on it. Thank you!
LikeLike
Did you try putting the effect on the frame rather than the viewcell?
LikeLike
Nope, the Command is simply not firing. I have updated my XAML code at https://codeshare.io/5wqkVx
Would you happen to have snippets of code or a working demo of the effect and a ListView?
Thank you.
LikeLike
I just tried attaching the effect to the frame. No luck. I will have to move on.
Thank you very much for your time.
LikeLike
I extended ViewCell in order to add SelectedBackgroundColor attribute.
LikeLike
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.
LikeLike
I have it working on a project with Xamarin.Forms 2.3.4.247, I upgrade to 2.4.0.282 and the effect no longer works?
LikeLike
Interesting. I’ll have to test. I’m going to be moving it’s dependency to 2.5 when I put a proper nuget package up so that will be the support going forward
LikeLike
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.*.
LikeLike
Is this on all platforms? or just one in particular. I’m wondering if it is an issue with it being in a listview. Something I’ll investigate
LikeLike
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
LikeLike
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
LikeLike
I’m using it on a StackLayout. I’ve tried on a new View and just a label and still not firing.
LikeLike
Can you please upload a proyect example?, i’m trying and not result. VS 15.5.4, XF 2.5.0.122203, SCL 2.0
LikeLike
Yeah, I’m planning on it, and putting it up on NuGet
LikeLiked by 1 person
I’ve built a clean project with a label using the LongPressedEffect. Latest Xamarin Forms
Still not working for me.
https://github.com/lywyn/LongPressedTestApp
LikeLike
I’ll clone it and play around
LikeLike
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”.
LikeLiked by 1 person
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.
LikeLike
The Xamarin Partner I’m working with solved it. Remove the empty constructor from AndroidLongPressedEffect.
REMOVE this line: public AndroidLongPressedEffect()
LikeLiked by 1 person
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.
LikeLike
It did work on Xamarin FOrms 2.3 for layouts, I had it bound to a StackLayout.
LikeLike
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?
LikeLike
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.
LikeLike
I’ve added entry and button to my test project and they work ok. https://github.com/lywyn/LongPressedTestApp
LikeLike
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.
LikeLike
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!
LikeLiked by 1 person
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?
LikeLiked by 1 person
It’s working beautifully. Thanks a lot for this effect!
But it’s overriding my TapGestureRecognizer. I’m using it on an .
LikeLiked by 1 person
It’s working beautifully. Thanks a lot for this effect!
But it’s overriding my TapGestureRecognizer. I’m using it on an Image tag.
LikeLiked by 1 person
Interesting, I haven’t tried it with the tap gesture recognizer yet, but it does work for me with button clicks and listview item taps
LikeLiked by 1 person
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.
LikeLiked by 1 person
Hi Julien
I tried your example and it didn’t work. Then I replaced TextCell by ViewCell and Label and it worked.
LikeLiked by 2 people
Same, manage to make it work with this solution. Thanks for the solution Tapio!
LikeLike
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?
LikeLike
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
LikeLike
Thank you for the quick answer: But just to clarify, do you bind the effect on the ViewCell or something within it?
LikeLike
I’ve done both and had it work. The view cell or the inner view layout or a subview within it
LikeLike
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
LikeLike
Yeah some versions of Xamarin.Forms works while others don’t since they are reworking how their gestures work underneath the covers.
LikeLike
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
LikeLike
Thanks for adding that insight! I’m sure it will help people who run into it but don’t know what’s going on 👍
LikeLike
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.
LikeLike
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
LikeLike
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?
LikeLike
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.
LikeLike
Hi Im getting this error would you please help me:
Error: ‘effects’ is an undeclared prefix
LikeLike
You need to add the xml namespace at the top of the file. Check out where I added “xmlns:effects=”….””
LikeLike
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));
}
LikeLike
When are you wiring up the event handler?
LikeLike
that is not a the case. I have figure that out, it is working with Label but not with ViewCell or Listview.
LikeLike
why is this an effect instead of a gesturerecognizer?
LikeLike
Yeah probably should be lol
LikeLike
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.
LikeLike
Hi Alex, i tried to impelement long pressed event using effects. Its is working for label and entry. but its not working for Listview. I read all the above conversation. i tried above cases but i was unsuccessful. Please help me and suggest me if i was missing anything. the following is my project.
https://github.com/uday-lucky/LongPressedEffectXamarinForms
LikeLike
Hey!!my issue has solved!! it worked after attaching longpressed effect to the stacklayout inisde my viewcell!! 🙂
LikeLike
Thank You for great solution sir…
LikeLike
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(“.”));
LikeLike
I figure out sir,
the line below solve the problem.
NewControl.Effects.Add(new LongPressedEffect());
Thank You so much
LikeLike
Great work. Thank you. This is an elegant way to capture the long tap on both Android and iOS. I was able to use it with a list view containing labels and image control.
LikeLiked by 1 person
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?
LikeLiked by 1 person
Hey Mike.
Did you find a solution for your problem? I am currently having the same issue and would be very interested.
LikeLike
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 :
LikeLike
…gets the object* so i can remove it from the list .
LikeLike
Silly me , I used Command parameter and did what I want .
LikeLike
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!
LikeLiked by 1 person
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?
LikeLike
Duplicate “ResolutionGroupName” attribute.
Help please.
LikeLike
[assembly: ResolutionGroupName(“your project name”)] must be set once on each platform.
LikeLike
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
LikeLike