While creating Xamarin.Forms applications, definitely you are going to need TapGestureRecognizer often. Implementing it in XAML many times may end up with a lot of unnecessary code. Let’s take a look at that simple clickable Image:
<Image Source="img.png"> <Image.GestureRecognizers> <TapGestureRecognizer Command="{Binding CloseCommand}" CommandParamter="{Binding .}" /> </Image.GestureRecognizers> </Image>
This is a lot of lines, especially when you have to add many clickable controls. However, we can do it better and put everything into a single line using our custom attached property:
xmlns:ex="clr-namespace:MyApp.Extensions;assembly=MyApp" ... <Image Source="img.png" ex:Gestures.TapCommand="{ex:Command CloseCommand, Parameter={Binding .}}"/>
Implementation
using System; using Xamarin.Forms; using Xamarin.Forms.Xaml; using System.Windows.Input; namespace MyApp.Extensions { [ContentProperty("Command")] public class CommandExtension : BindableObject, IMarkupExtension { public string Command { get; set; } // Binding source for Command public object Source { get; set; } // CommandParameter public object Parameter { get; set; } public object ProvideValue(IServiceProvider serviceProvider) { return this; } } }
using Xamarin.Forms; using System.Linq; namespace MyApp.Extensions { public class Gestures { #region TapCommand public static readonly BindableProperty TapCommandProperty = BindableProperty.CreateAttached<Gestures, CommandExtension>( x => (CommandExtension)x.GetValue(Gestures.TapCommandProperty), null, BindingMode.OneWay, propertyChanged: TapCommandChanged); #endregion private static void TapCommandChanged(BindableObject source, CommandExtension oldVal, CommandExtension newVal) { var tapGesture = new TapGestureRecognizer(); // Set command var commandBinding = new Binding(newVal.Command, source: newVal.Source); tapGesture.SetBinding(TapGestureRecognizer.CommandProperty, commandBinding); // Set command parameter if (newVal.Parameter is Binding) { tapGesture.SetBinding(TapGestureRecognizer.CommandParameterProperty, newVal.Parameter as Binding); } else { tapGesture.CommandParameter = newVal.Parameter; } // Remove old TapGestureRecognizer var view = source as View; var toRemove = view.GestureRecognizers.OfType<TapGestureRecognizer>().FirstOrDefault(); view.GestureRecognizers.Remove(toRemove); // Add new one view.GestureRecognizers.Add(tapGesture); } } }