Xamarin.Forms - EventToCommand Behavior in MVVM ViewModel

Introduction

 
Xamarin.Forms code runs on multiple platforms - each of which has its own filesystem. This means that reading and writing files is most easily done using the native file APIs on each platform. Alternatively, embedded resources are a simpler solution to distribute data files with an app.
 

EventToCommand Behavior

 
EventToCommandBehavior class is a reusable Xamarin.Forms custom behavior that executes a command in response to any event firing.
 
The following behavior properties must be set to use the EventToCommand behavior:
  1. EventName – the name of the event the behavior listens to(Ex: Appearing).
  2. Command – the ICommand to be executed. The behavior expects to find the ICommand instance on the BindingContext of the attached control, which may be inherited from a parent element
Prerequisites
  • Visual Studio 2017 or later (Windows or Mac)

Setting up a Xamarin.Forms Project

 
Start by creating a new Xamarin.Forms project. You will learn more by going through the steps yourself.
 
Create a new or existing Xamarin forms(.Net standard) Project, with Android, and iOS Platform.
 
 

Create BehaviourBase Class

 
Now, create a Behaviour Base class Derived from Behavior and BindableObject
 
BehavourBase.cs
  1. public class BehaviorBase<T>: Behavior<T> where T : BindableObject  
  2.     {  
  3.         public T AssociatedObject { getprivate set; }  
  4.         protected override void OnAttachedTo(T bindable)  
  5.         {  
  6.             base.OnAttachedTo(bindable);  
  7.             AssociatedObject = bindable;  
  8.   
  9.             if (bindable.BindingContext != null)  
  10.                 BindingContext = bindable.BindingContext;  
  11.   
  12.             bindable.BindingContextChanged += OnBindingContextChanged;  
  13.         }  
  14.         protected override void OnDetachingFrom(T bindable)  
  15.         {  
  16.             base.OnDetachingFrom(bindable);  
  17.             bindable.BindingContextChanged -= OnBindingContextChanged;  
  18.             AssociatedObject = null;  
  19.         }  
  20.   
  21.         void OnBindingContextChanged(object sender, EventArgs e)  
  22.         {  
  23.             OnBindingContextChanged();  
  24.         }  
  25.   
  26.         protected override void OnBindingContextChanged()  
  27.         {  
  28.             base.OnBindingContextChanged();  
  29.             BindingContext = AssociatedObject.BindingContext;  
  30.         }  
  31.     }  

Create EventToCommand Behaviour

 
Here, create an EventToCommand Behaviour Class to convert your events to Command and also implement bindable properties.
  1. EventName
  2. Command
EventToCommandBehaviour.cs
  1. public class EventToCommandBehavior: BehaviorBase<VisualElement>  
  2.     {  
  3.         Delegate eventHandler;  
  4.         public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName"typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);  
  5.         public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command"typeof(ICommand), typeof(EventToCommandBehavior), null);  
  6.   
  7.         public string EventName  
  8.         {  
  9.             get { return (string)GetValue(EventNameProperty); }  
  10.             set { SetValue(EventNameProperty, value); }  
  11.         }  
  12.         public ICommand Command  
  13.         {  
  14.             get { return (ICommand)GetValue(CommandProperty); }  
  15.             set { SetValue(CommandProperty, value); }  
  16.         }  
  17.   
  18.         protected override void OnAttachedTo(VisualElement bindable)  
  19.         {  
  20.             base.OnAttachedTo(bindable);  
  21.             RegisterEvent(EventName);  
  22.         }  
  23.   
  24.         protected override void OnDetachingFrom(VisualElement bindable)  
  25.         {  
  26.             DeregisterEvent(EventName);  
  27.             base.OnDetachingFrom(bindable);  
  28.         }  
  29.         static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)  
  30.         {  
  31.             var behavior = (EventToCommandBehavior)bindable;  
  32.   
  33.             if (behavior.AssociatedObject == nullreturn;  
  34.   
  35.             string oldEventName = (string)oldValue;  
  36.             string newEventName = (string)newValue;  
  37.   
  38.             behavior.DeregisterEvent(oldEventName);  
  39.             behavior.RegisterEvent(newEventName);  
  40.         }  
  41.   
  42.         void RegisterEvent(string name)  
  43.         {  
  44.             if (string.IsNullOrWhiteSpace(name)) return;  
  45.   
  46.             EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);  
  47.   
  48.             if (eventInfo == null)  
  49.                 throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));  
  50.   
  51.             MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");  
  52.             eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);  
  53.             eventInfo.AddEventHandler(AssociatedObject, eventHandler);  
  54.         }  
  55.   
  56.         void DeregisterEvent(string name)  
  57.         {  
  58.             if (string.IsNullOrWhiteSpace(name) || eventHandler == null)  
  59.                 return;  
  60.   
  61.             EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);  
  62.   
  63.             if (eventInfo == null)  
  64.                 throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));  
  65.   
  66.             eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);  
  67.             eventHandler = null;  
  68.         }  
  69.   
  70.         void OnEvent(object sender, object eventArgs)  
  71.         {  
  72.             if (Command == nullreturn;  
  73.   
  74.             object resolvedParameter;  
  75.   
  76.                 resolvedParameter = eventArgs;  
  77.   
  78.             if (Command.CanExecute(resolvedParameter))  
  79.                 Command.Execute(resolvedParameter);  
  80.         }  
  81.   
  82.     }  

BaseViewModel

 
In this step, I create a common BaseViewModel for reusable purposes.
 
Note:

I use RelayCommand Instead of Command. You can just use Command.
 
Refresh
 
Refresh command indicates to Appearing Command. Whatever you want to implement with page load time you can use RefrehCommad.
 
CleanUp
 
Cleanup command indicates to OnDisapearing. This CleanUpCommand works when your page's disappearing time is called.
 
BaseViewModel.cs
  1. public class BaseViewModel : ViewModelBase  
  2.     {  
  3.         #region Fields  
  4.   
  5.   
  6.         private RelayCommand _refreshCommand;  
  7.         private RelayCommand _cleanupCommand;  
  8.         #endregion  
  9.  
  10.         #region Properties  
  11.   
  12.           
  13.         public RelayCommand RefreshCommand  
  14.         {  
  15.             get  
  16.             {  
  17.                 return _refreshCommand ?? (_refreshCommand = new RelayCommand(this.Refresh));  
  18.             }  
  19.         }  
  20.   
  21.         public RelayCommand CleanupCommand  
  22.         {  
  23.             get  
  24.             {  
  25.                 return _cleanupCommand ?? (_cleanupCommand = new RelayCommand(this.Cleanup));  
  26.             }  
  27.         }  
  28.  
  29.         #endregion  
  30.  
  31.         #region Methods  
  32.   
  33.         public virtual void Refresh()  
  34.         {  
  35.   
  36.         }  
  37.         public override void Cleanup()  
  38.         {  
  39.             base.Cleanup();  
  40.         }  
  41.  
  42.         #endregion  
  43.   
  44.   
  45.     }  
MainPageViewModel
 
Now, use the Refresh and CleanUp command from BaseViewMmodel in your Pages.
 
MainPageViewModel.cs
  1. public class MainPageViewModel:BaseViewModel  
  2.     {  
  3.         #region Fields  
  4.   
  5.         private ObservableCollection<Person> _persons;  
  6.     
  7.         #endregion  
  8.  
  9.         #region Constructor  
  10.   
  11.         public MainPageViewModel()  
  12.         {  
  13.   
  14.         }  
  15.  
  16.         #endregion  
  17.  
  18.         #region Properties  
  19.   
  20.         public ObservableCollection<Person> Persons  
  21.         {  
  22.             get { return _persons; }  
  23.             set { Set(() => Persons, ref _persons, value); }  
  24.         }  
  25.  
  26.  
  27.         #endregion  
  28.  
  29.         #region Methods  
  30.   
  31.         public override void Refresh()  
  32.         {  
  33.             base.Refresh();  
  34.   
  35.             this.Persons = new ObservableCollection<Person>();  
  36.             this.Persons.Add(new Person { PersonId = 1, Name = "Smith", Age = 22 });  
  37.             this.Persons.Add(new Person { PersonId = 2, Name = "Delpin", Age = 23 });  
  38.             this.Persons.Add(new Person { PersonId = 1, Name = "Raj", Age = 20 });  
  39.             this.Persons.Add(new Person { PersonId = 1, Name = "John", Age = 25 });  
  40.         }  
  41.   
  42.         public override void Cleanup()  
  43.         {  
  44.             this.Persons = null;  
  45.             base.Cleanup();  
  46.         }  
  47.  
  48.         #endregion  
  49.     }  

Setting up the User Interface

 
Here, Implement EventToCommandBehaviour in your View.
 
Add Namespace
  1. xmlns:behaviors="clr-namespace:XamarinStudy.Common.Behaviors"  
Add Behaviour
  1. <ContentPage.Behaviors>  
  2.         <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding RefreshCommand}"/>  
  3.         <behaviors:EventToCommandBehavior EventName="Disappearing" Command="{Binding CleanupCommand}"/>  
  4.     </ContentPage.Behaviors>   
MainPage.Xaml
  1. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"    
  2.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"    
  3.              xmlns:d="http://xamarin.com/schemas/2014/forms/design"    
  4.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
  5.              mc:Ignorable="d"    
  6.              xmlns:behaviors="clr-namespace:XamarinStudy.Common.Behaviors"    
  7.              x:Class="XamarinStudy.MainPage" Title="Bindable Layout">    
  8.     
  9.     <ContentPage.Behaviors>    
  10.         <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding RefreshCommand}"/>    
  11.         <behaviors:EventToCommandBehavior EventName="Disappearing" Command="{Binding CleanupCommand}"/>    
  12.     </ContentPage.Behaviors>     
  13.     <StackLayout x:Name="PersonList" BindableLayout.ItemsSource="{Binding Persons}"  HorizontalOptions="FillAndExpand">    
  14.         <BindableLayout.ItemTemplate>    
  15.             <DataTemplate>    
  16.                 <StackLayout Spacing="10" >    
  17.                     <Label Margin="20,0" Text="{Binding Name}"/>    
  18.                     <Label Margin="20,0" Text="{Binding Age}"/>    
  19.                     <BoxView HeightRequest="1" BackgroundColor="Black"/>    
  20.                     <StackLayout.GestureRecognizers>    
  21.                         <TapGestureRecognizer Command="{Binding BindingContext.SelectPersonCommand,Source={x:Reference PersonList}}" CommandParameter="{Binding PersonId}"/>    
  22.                     </StackLayout.GestureRecognizers>    
  23.                 </StackLayout>    
  24.                     
  25.             </DataTemplate>    
  26.         </BindableLayout.ItemTemplate>    
  27.     </StackLayout>    
  28.     
  29. </ContentPage>     
Click the "Play" button to try it out.
 
I hope you understood how to use Appearing and Disappearing in MVVM ViewModel using EventToCommand Behavior in Xamarin.Forms.
 
Thanks for reading. Please share your comments and feedback. Happy Coding :)