4

EventToCommand in Xamarin Forms Apps | Anthony Simmon

 3 years ago
source link: https://anthonysimmon.com/eventtocommand-in-xamarin-forms-apps/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

EventToCommand in Xamarin Forms Apps

Xamarin-Forms-EventToCommand.png

This component is a part of Pillar, a lightweight MVVM framework that I made for Xamarin.Forms 1.x and 2.x. Please check it out on Nuget or GitHub.

An EventToCommand behavior can be used to bind any event on a visual element to an ICommand. Typically, this element is used in XAML to connect the attached element to a command located in a ViewModel.

When I started playing with Xamarin Forms, I found myself in a situation where I had to bind the ItemTapped event of the ListView to a command. I managed to do so by using the Behaviors from the Cavalli Corrado’s nuget package.

But since Xamarin Forms officially supports Behavior in version 1.3, I wanted to write my own.

EventToCommand Behavior usage

Here is an example of how I can bind the ItemTapped event of the ListView to a Command which takes as parameter the BindingContext of the tapped item, using my EventToCommand.

The ListView ViewModel has a “SayHelloCommand”:

public class HomeViewModel : ViewModelBase { public ObservableCollection<PersonViewModel> People { get; set; } public RelayCommand<PersonViewModel> SayHelloCommand { get; set; } public HomeViewModel() { People = new ObservableCollection<PersonViewModel> { new PersonViewModel("John"), new PersonViewModel("Mike"), new PersonViewModel("Jane") }; SayHelloCommand = new RelayCommand<PersonViewModel>(SayHello); } public void SayHello(PersonViewModel person) { Debug.WriteLine("Hello {0}!", person.Name); } }

I need a converter to extract the tapped BindingContext from the ItemTappedEventArgs :

public class ItemTappedEventArgsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var eventArgs = value as ItemTappedEventArgs; if (eventArgs == null) throw new ArgumentException("Expected TappedEventArgs as value", "value"); return eventArgs.Item; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }

And finally, here is the View :

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:b="clr-namespace:HelloEventToCommand.Behaviors;assembly=HelloEventToCommand" xmlns:c="clr-namespace:HelloEventToCommand.Converters;assembly=HelloEventToCommand" x:Class="HelloEventToCommand.Views.HomeView"> <ContentPage.Resources> <ResourceDictionary> <c:ItemTappedEventArgsConverter x:Key="ItemTappedConverter" /> </ResourceDictionary> </ContentPage.Resources> <ListView ItemsSource="{Binding People}"> <ListView.Behaviors> <b:EventToCommandBehavior EventName="ItemTapped" Command="{Binding SayHelloCommand}" EventArgsConverter="{StaticResource ItemTappedConverter}" /> </ListView.Behaviors> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding Name}"/> </DataTemplate> </ListView.ItemTemplate> </ListView> </ContentPage>

How it’s done

public class EventToCommandBehavior : BindableBehavior<View> { public static readonly BindableProperty EventNameProperty = BindableProperty.Create<EventToCommandBehavior, string>(p => p.EventName, null); public static readonly BindableProperty CommandProperty = BindableProperty.Create<EventToCommandBehavior, ICommand>(p => p.Command, null); public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create<EventToCommandBehavior, object>(p => p.CommandParameter, null); public static readonly BindableProperty EventArgsConverterProperty = BindableProperty.Create<EventToCommandBehavior, IValueConverter>(p => p.EventArgsConverter, null); public static readonly BindableProperty EventArgsConverterParameterProperty = BindableProperty.Create<EventToCommandBehavior, object>(p => p.EventArgsConverterParameter, null); private Delegate _handler; private EventInfo _eventInfo; public string EventName { get { return (string)GetValue(EventNameProperty); } set { SetValue(EventNameProperty, value); } } public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } public IValueConverter EventArgsConverter { get { return (IValueConverter)GetValue(EventArgsConverterProperty); } set { SetValue(EventArgsConverterProperty, value); } } public object EventArgsConverterParameter { get { return GetValue(EventArgsConverterParameterProperty); } set { SetValue(EventArgsConverterParameterProperty, value); } } protected override void OnAttachedTo(View visualElement) { base.OnAttachedTo(visualElement); var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray(); if (events.Any()) { _eventInfo = events.FirstOrDefault(e => e.Name == EventName); if (_eventInfo == null) throw new ArgumentException(String.Format("EventToCommand: Can't find any event named '{0}' on attached type", EventName)); AddEventHandler(_eventInfo, AssociatedObject, OnFired); } } protected override void OnDetachingFrom(View view) { if (_handler != null) _eventInfo.RemoveEventHandler(AssociatedObject, _handler); base.OnDetachingFrom(view); } private void AddEventHandler(EventInfo eventInfo, object item, Action<object, EventArgs> action) { var eventParameters = eventInfo.EventHandlerType .GetRuntimeMethods().First(m => m.Name == "Invoke") .GetParameters() .Select(p => Expression.Parameter(p.ParameterType)) .ToArray(); var actionInvoke = action.GetType() .GetRuntimeMethods().First(m => m.Name == "Invoke"); _handler = Expression.Lambda( eventInfo.EventHandlerType, Expression.Call(Expression.Constant(action), actionInvoke, eventParameters[0], eventParameters[1]), eventParameters ) .Compile(); eventInfo.AddEventHandler(item, _handler); } private void OnFired(object sender, EventArgs eventArgs) { if (Command == null) return; var parameter = CommandParameter; if (eventArgs != null && eventArgs != EventArgs.Empty) { parameter = eventArgs; if (EventArgsConverter != null) { parameter = EventArgsConverter.Convert(eventArgs, typeof(object), EventArgsConverterParameter, CultureInfo.CurrentUICulture); } } if (Command.CanExecute(parameter)) { Command.Execute(parameter); } } }

Where BindableBehavior is a BindingContext-aware Behavior , made by Jonathan Yates :

public class BindableBehavior<T> : Behavior<T> where T : BindableObject { public T AssociatedObject { get; private set; } protected override void OnAttachedTo(T visualElement) { base.OnAttachedTo(visualElement); AssociatedObject = visualElement; if (visualElement.BindingContext != null) BindingContext = visualElement.BindingContext; visualElement.BindingContextChanged += OnBindingContextChanged; } private void OnBindingContextChanged(object sender, EventArgs e) { OnBindingContextChanged(); } protected override void OnDetachingFrom(T view) { view.BindingContextChanged -= OnBindingContextChanged; } protected override void OnBindingContextChanged() { base.OnBindingContextChanged(); BindingContext = AssociatedObject.BindingContext; } }

As you can see, the EventToCommandBehavior can use an EventArgsConverter which is an IValueConverter.
It is very useful in some cases, for example when you need to pass an argument taken from an EventArgs to the Command.

Posted on September 20, 2015March 2, 2016Categories Mobile Development, ProgrammingTags .NET, Behaviors, C#, Xamarin, Xamarin Forms, XAML

6 thoughts on “EventToCommand in Xamarin Forms Apps”

  1. b13c32bc3200f9cc2a644a4c7579ddcd?s=42&d=retro&r=gScott says:

    Anthony, thank you very much for sharing your snippet here. I had tried using Corcav.Behaviors, but had problems getting it working on Xamarin Form 2.0 with UWP. I am successfully using your approach to implement EventToCommand Behaviors with Forms 2.0 on a Universal Windows Platform app!

    This is a nice contribution and I look forward to any additions you might make.

    1. 3652bc2aecf09d547c69367ff6b1a7a6?s=42&d=retro&r=gAnthony says:

      Thank you, Scott. I’m actually working – whenever I can – on something bigger.

  2. a16b220d60d434ad94298e3ceae5aec1?s=42&d=retro&r=gSimon says:

    This doesn’t work for me. The event wiring all works, but the ICommand Command object in the behavior class is always null when the event fires. It looks like the Command=”{Binding BlahDeBlah}” in the XAML is just not binding. Setting a breakpoint on the setter of the Behavior Command property confirms this, as it is never hit. I have tried both ItemSelected and ItemTapped events, and both give same symptoms.

    1. 52db15126120e575ab471b046d46fbab?s=42&d=retro&r=gshivam says:

      How did you solved this issue? @Simon

  3. c4e0bc9a2c9ca23dd0da4bfea416be9b?s=42&d=retro&r=gAKH says:

    Thanks a lot. It works beautifully.

  4. 8c5e77e313a8e3730dea4f9363aaf5be?s=42&d=retro&r=gnikun5 says:

    The argument is empty, how to solve it?

Leave a Reply Cancel reply

Post navigation


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK