

EventToCommand in Xamarin Forms Apps | Anthony Simmon
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

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”
-
Scott 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.
-
Anthony says:
Thank you, Scott. I’m actually working – whenever I can – on something bigger.
-
-
Simon 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.
-
shivam says:
How did you solved this issue? @Simon
-
-
AKH says:
Thanks a lot. It works beautifully.
-
nikun5 says:
The argument is empty, how to solve it?
Leave a Reply Cancel reply
Post navigation
Recommend
-
101
-
64
Change Randomly A View’s Color Hi everyone, here is the third part of the Xamarin Forms Tips series. Today, we will look at how to change a...
-
54
Hot off the press, another PR just got merged into the Xamarin.Forms repository. This time, adding a new feature on a Label, the ability to specify
-
19
#XamarinForms #XamarinCommunityToolkit
-
3
Building beautiful apps with Xamarin.Forms James
-
5
Web apps in Xamarin Forms is one of the most striking features for developers over time. There are many articles and presentations that talk about this topic, but we will focus on the different alternatives that exist today and one or another...
-
8
#XamarinEssentials #Maps #XamarinFormsOpen Native Maps Apps with Xamarin.Essentials and Xamarin.Forms
-
13
#ux #xamarinforms #xamexpertdayImproving...
-
14
.NET Development Addict Home is where the [.net] compiler is. Xamarin.Forms is really cool! It allows you to almost write y...
-
11
Activities Podcast (DevTalk) – Improving the UX of Xamarin Forms Apps February...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK