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:
1 <Image Source="img.png"> 2 <Image.GestureRecognizers> 3 <TapGestureRecognizer Command="{Binding CloseCommand}" CommandParamter="{Binding .}" /> 4 </Image.GestureRecognizers> 5 </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:
XHTML xmlns:ex="clr-namespace:MyApp.Extensions;assembly=MyApp" ... <Image Source="img.png" ex:Gestures.TapCommand="{ex:Command CloseCommand, Parameter={Binding .}}"/> xmlns:ex="clr-namespace:MyApp.Extensions;assembly=MyApp" ... <Image Source="img.png" ex:Gestures.TapCommand="{ex:Command CloseCommand, Parameter={Binding .}}"/>
Implementation
1 using System; 2 using Xamarin.Forms; 3 using Xamarin.Forms.Xaml; 4 using System.Windows.Input; 5 6 namespace MyApp.Extensions 7 { 8 [ContentProperty("Command")] 9 public class CommandExtension : BindableObject, IMarkupExtension 10 { 11 public string Command { get; set; } 12 13 // Binding source for Command 14 public object Source { get; set; } 15 16 // CommandParameter 17 public object Parameter { get; set; } 18 19 public object ProvideValue(IServiceProvider serviceProvider) 20 { 21 return this; 22 } 23 } 24 }
1 using Xamarin.Forms; 2 using System.Linq; 3 4 namespace MyApp.Extensions 5 { 6 public class Gestures 7 { 8 #region TapCommand 9 10 public static readonly BindableProperty TapCommandProperty = 11 BindableProperty.CreateAttached<Gestures, CommandExtension>( 12 x => (CommandExtension)x.GetValue(Gestures.TapCommandProperty), 13 null, BindingMode.OneWay, propertyChanged: TapCommandChanged); 14 15 #endregion 16 17 private static void TapCommandChanged(BindableObject source, CommandExtension oldVal, 18 CommandExtension newVal) 19 { 20 var tapGesture = new TapGestureRecognizer(); 21 22 // Set command 23 var commandBinding = new Binding(newVal.Command, source: newVal.Source); 24 tapGesture.SetBinding(TapGestureRecognizer.CommandProperty, commandBinding); 25 26 // Set command parameter 27 if (newVal.Parameter is Binding) 28 { 29 tapGesture.SetBinding(TapGestureRecognizer.CommandParameterProperty, 30 newVal.Parameter as Binding); 31 } 32 else 33 { 34 tapGesture.CommandParameter = newVal.Parameter; 35 } 36 37 // Remove old TapGestureRecognizer 38 var view = source as View; 39 var toRemove = view.GestureRecognizers.OfType<TapGestureRecognizer>().FirstOrDefault(); 40 view.GestureRecognizers.Remove(toRemove); 41 42 // Add new one 43 view.GestureRecognizers.Add(tapGesture); 44 } 45 } 46 }