Binding Events to Command


In the context of commanding, behaviors are a useful approach for connecting a control to a command. In addition, they can also be used to associate commands with controls that were not designed to interact with commands.

I will try to summarise an extract and a reusable code for binding Events to Commands.

Behaviors allow us to add functionality to UI controls like labels, etc without having to subclass them. Behaviors are written in code and added to controls in XAML or code.

First things first.

You need to have 2 classes in your solution.

EventToCommand-1

Code for BehaviorBase.cs

using System;
using Xamarin.Forms;
namespace MyNameSpace
{
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
view raw BehaviorBase.cs hosted with ❤ by GitHub

Code for EventToCommandBehavior.cs 

using System;
using System.Reflection;
using System.Windows.Input;
using Xamarin.Forms;
namespace MyNameSpace
{
public class EventToCommandBehavior : BehaviorBase<View>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
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 Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(View bindable)
{
base.OnDetachingFrom(bindable);
DeregisterEvent(EventName);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}
}

these are totally reusable classes you just need to resolve the namespaces and you are very much done with the setup and can consume them in code.

the consuming part can be done in both the XAML as well as the code part, I will be doing it in the XAML.

Include the declaration for XML namespace on the top of your XAML in which you want to consume the behaviour.

2

XML Namespaces provide a method to avoid element name conflicts. When using prefixes in XML, a namespace for the prefix must be defined. The namespace can be defined by a xmlns attribute in the start tag of an element. The namespace declaration has the following syntax.

xmlns:prefixName="clr-namespace:MyNameSpace/LocationOfYourClasses"

i just added my classes at the root of the project hence
xmlns:local=”clr-namespace:MyNameSpace” but it is a good practice to maintain the hierarchy in a project and keep the things clean.  so i would suggest putting things in a Folder namely Behaviors and prefix it as behaviors then your xmlns syntax would be  xmlns:behaviors=”clr-namespace:MyNameSpace.Behaviors” 

now you can use it for the control that you want,
just figure out which for which “Event” you want to bind your command.

as an example, I will bind the TextChanged event of SearchBar to a command that I wrote in my ViewModel.EventToCommand - 3Voila! You have successfully attached a Command that fires up when that particular event triggers.
Similarly, you can Bind  multiple commands to different events for the same object, (i haven’t tried doing this but it should work, just maintain the 1-to-1 mapping of Events-to-Commands. if anybody tries that do drop a comment about it. 😉 )

Pingback for assistance, your Feedback’s are always a welcome… 🙂

 

Regards,
Aditya Deshpande

One thought on “Binding Events to Command

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s