Binding UI Events from View to commands in ViewModel in Silverlight 4
In previous two posts we covered wiring up the Views and ViewModels and Blendability and showing ModalDialogs in MVVM way.
Today we will touch another problem that people starting with MVVM very often fail to address properly:
Handling the UI Events of the View in the ViewModel while avoiding placing any logic in code behind of the View.
So our design goals for this post are:
- We want to be able to wire-up UI Events to the commands in ViewModel via DataBinding in Xaml without any code behind in the View
- View should not be aware of the ViewModel’s type or any other details of its existence – View should just use Silverlight’s DataBinding to access its ViewModel public properties (its DataContext) regardless of what is actually set to be there
- We want to be able to handle any event that occurs in View (that includes not only Button clicks but also Mouse events, Drag and Drop events, Loaded events etc).
When i was trying to solve this problem, i made a mistake.
I coupled the View with ViewModel, made the View aware of the ViewModel via interface of the ViewModel.
View held the instance of ViewModel in its DataContext.
And then when some event fires in the View i would call a ViewModel method in code behind of the View (accessing it via local variable holding the ViewModel instance casted to interface that ViewModel is implementing).
While this approach works for all events in the View because you can create event handler for any View event, it has many drawbacks.
Main problems are:
- View has to know about the ViewModel and it has to have a instance of the ViewModel injected somehow
- we have to create event handlers in the View code behind .cs file for each event (this can be really boring, and im lazy to do repetitive code so that was really hard for me).
I didn’t like this solution at all and I immediately abandoned it.
Then i tried searching for possible solutions on the web and found the magical Expression Blend Samples.
Microsoft introduced Behaviors in Silverlight 3 with Blend 3 to help UI designers to have a flexible new way to add interactivity to applications.
Behaviors allow interactivity to be added to elements directly on the design surface in XAML without having to write additional code.
I will not go into details on Behaviors since there are tons of resources on the web on the subject, you can read the original Blend 3 Behaviors announcement to get more info.
The great thing about Behaviors is that they introduced Triggers and very handy EventTrigger and TriggerAction classes that are perfect match for our MVVM events scenario.
As you can read on the MSDN link EventTrigger ‘Represents a trigger that applies a set of actions … in response to an event’.
So we can create a EventTrigger for some UI event in our View that will fire a custom TriggerAction that will call a method on our ViewModel with some parameters.
On the mentioned Expression Blend Samples page there is already implemented basic class for this – its called InvokeDataCommand and we will just customize it and extend (generalize it) so it serves our needs.
In the end we want to accomplish something like this in our View’s Xaml:
<Interactivity:Interaction.Triggers> <Interactivity:EventTrigger EventName="MouseMove"> <TriggerActions:MapMouseEventToCommand Command="{Binding Path=ShowMousePositionCommand}" /> </Interactivity:EventTrigger> </Interactivity:Interaction.Triggers>
So this is instructing our View to call the command called ShowMousePositionCommand on its ViewModel when mouse pointer is moved over some control.
So lets get started!
First we need to create a command class that we will be able to call from our Events.
Silverlight has only partial support for commanding.
Until Silverlight 4 ICommand interface was available – but without any real support or use.
In Silverlight 4 this interface is supported by Button type controls in again partial way: in ButtonBase class from which all button-like controls inherit, there are two properties: Command and CommandParameter that you can point to a command that implements ICommand interface and pass it some parameter.
This can be done via DataBinding and when you click the button this command will be executed with the specified parameter. No code solution, pure XAML!
The problem is that Silverlight 4 Beta does not offer any implementation of the ICommand interface so we have to do this by ourselves.
Here is the ICommand interface:
namespace System.Windows.Input { public interface ICommand { bool CanExecute(object parameter); void Execute(object parameter); abstract event EventHandler CanExecuteChanged; } }
So it basically defines a contract: Any command that will implement this interface we we will be able to query it if we can ececute it (CanExecute) and execute it with parameter via its Execute method, also there is an event CanExecuteChanged so that anyone can subscribe to it in order to be notified if CanExecute status of command has changed (this is used by UI controls so that buttons become disabled if command is disabled etc).
But before we implement this lets make a slight modification and create another interface IDelegateCommand that will implement ICommand interface and just add one method RaiseCanExecuteChanged();
This new method is there so that we can invoke it from code if we know that status of our command has changed – so by invoking it we can notify the UI of this change.
So here is the new IDelegateCommand interface:
public interface IDelegateCommand : ICommand { void RaiseCanExecuteChanged(); }
And here is our generic version of DelegateCommand class that implements IDelegateCommand and ICommand:
public class DelegateCommand<T> : IDelegateCommand { private readonly Action<T> executeAction; private readonly Func<T, bool> canExecuteAction; public DelegateCommand(Action<T> executeAction, Func<T, bool> canExecuteAction) { this.executeAction = executeAction; this.canExecuteAction = canExecuteAction; } public DelegateCommand(Action<T> executeAction) : this(executeAction, null) { } /// <summary> /// Defines the method that determines whether the command can execute in its current state. /// </summary> /// <returns> /// true if this command can be executed; otherwise, false. /// </returns> /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null. </param> public bool CanExecute(object parameter) { if (canExecuteAction != null) { return canExecuteAction((T) parameter); } return true; } /// <summary> /// Defines the method to be called when the command is invoked. /// </summary> /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null. </param> public void Execute(object parameter) { if (CanExecute(parameter)) { executeAction((T) parameter); } } protected void OnCanExecuteChanged(object sender, EventArgs args) { var handler = this.CanExecuteChanged; if (handler != null) { handler(sender, args); } } public void RaiseCanExecuteChanged() { this.OnCanExecuteChanged(this, EventArgs.Empty); } public event EventHandler CanExecuteChanged; }
Its simple generic class that allows us to create commands on our ViewModels that will be triggered when needed.
I wont go into much details of this class since there are a lot of posts on this on the web. Be sure to check out Prism DelegateCommand version since my implementation is mostly based on their code.
Now that we have a way of specifying the commands that we will trigger let’s see how we will trigger them.
Back to our Behaviors: We will create base generic class that will inherit from TriggerAction<FrameworkElement> and we will use this class to build specific TriggerActions for different UI events:
public abstract class MapEventToCommandBase<TEventArgsType> : TriggerAction<FrameworkElement> where TEventArgsType : EventArgs { public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(IDelegateCommand), typeof(MapEventToCommandBase<TEventArgsType>), new PropertyMetadata(null, OnCommandPropertyChanged)); public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(MapEventToCommandBase<TEventArgsType>), new PropertyMetadata(null, OnCommandParameterPropertyChanged)); private static void OnCommandParameterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var invokeCommand = d as MapEventToCommand; if (invokeCommand != null) { invokeCommand.SetValue(CommandParameterProperty, e.NewValue); } } private static void OnCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var invokeCommand = d as MapEventToCommand; if (invokeCommand != null) { invokeCommand.SetValue(CommandProperty, e.NewValue); } } protected override void Invoke(object parameter) { if (this.Command == null) { return; } if (this.Command.CanExecute(parameter)) { var eventInfo = new EventInformation<TEventArgsType> { EventArgs = parameter as TEventArgsType, Sender = this.AssociatedObject, CommandArgument = GetValue(CommandParameterProperty) }; this.Command.Execute(eventInfo); } } public IDelegateCommand Command { get { return (IDelegateCommand)base.GetValue(CommandProperty); } set { base.SetValue(CommandProperty, value); } } public object CommandParameter { get { return base.GetValue(CommandParameterProperty); } set { base.SetValue(CommandParameterProperty, value); } } }
Our MapEventToCommandBase class simply adds Command and CommandParameter DependencyProperties so we can set via DataBinding the command and (optionally) parameter for the event.
It has one type parameter TEventArgsType that it uses so it can send the appropriate EventArgs from the Event that occurred to the command we are calling.
That is why TEventArgsType has a constraint that it has to inherit from EventArgs since every UI event sends some information in a class that inherits from EventArgs or sends EventArgs.Empty.
The meat of this class is the method Invoke that overrides the method in its base class TriggerAction.
This method is called from the Silverlight runtime when some event occurs in the UI and the parameter sent is the instance of class that inherits from EventArgs and carries the information specific to that event (MouseEventArgs, DragEventArgs, KeybardEventArgs etc).
What we do in this method is that we take that event information we received and pack it into new class that we will send to our command.
Its this part of code:
if (this.Command.CanExecute(parameter)) { var eventInfo = new EventInformation<TEventArgsType> { EventArgs = parameter as TEventArgsType, Sender = this.AssociatedObject, CommandArgument = GetValue(CommandParameterProperty) }; this.Command.Execute(eventInfo); }
So we construct the instance of generic EventInformation class of certain EventArgs inheritor type and we include there the sender of the event and the optional argument we sent from XAML binding.
Here is how the generic EventInformation class looks like:
public class EventInformation<TEventArgsType> where TEventArgsType : EventArgs { public object Sender { get; set; } public TEventArgsType EventArgs { get; set; } public object CommandArgument { get; set; } }
It allows us to easily create strongly typed class for each of possible events that can occur in the UI.
For RoutedEvents like Loaded event the RoutedEventArgs will be passed there.
Lets continue. Since our MapEventToCommandBase is abstract and generic class we need to inherit it to be able to actually use it in Xaml so here are some examples of concrete MapSOMEKINDOFEventToCommand implementations:
First MapMouseEventToCommand:
public class MapMouseEventToCommand : MapEventToCommandBase<MouseEventArgs> { }
So as you see there is nothing we need to implement here, just to specify the type of the event we want to handle.
If Xaml would support generics we could make the MapEventToCommandBase not to be abstract and use it directly but until Xaml learns to accept generics this is the best way i could figure out.
Then there is the MapKeyboardEventToCommand:
public class MapKeyboardEventToCommand : MapEventToCommandBase<KeyEventArgs> { }
So we can do this for any type of event that we need (see the sample Visual Studio 2010 Project for more examples)
If we don’t need the specific EventArgs we can also use the MapEventToCommand class that uses EventArgs as its type so it can be used with ANY event.
The drawback is that in the command we will receive the EventInformation<EventArgs> so we need to cast the EventArgs property of that class to some specific EventArgs inheritor class so we lose type safety.
So now that all is in place lets see how we can use our new magic classes from Xaml in Views.
First the simplest event: Loaded.
In Xaml of our View we must place this code:
<Interactivity:Interaction.Triggers> <Interactivity:EventTrigger EventName="Loaded"> <TriggerActions:MapEventToCommand Command="{Binding Path=LoadedCommand}" CommandParameter="Shell view loaded at {0:d/M/yyyy HH:mm:ss:fff tt}" /> </Interactivity:EventTrigger> </Interactivity:Interaction.Triggers>
So we are creating EventTrigger for event with name “Loaded” and for this event we are setting TriggerAction to our MapEventToCommand. This event will trigger when control is loaded and since we use DataBinding to set the Command property, when the event occurs, LoadedCommand on our DataContext will be invoked with the string parameter we hard coded in CommandParameter (we could used DataBinding there also but i skipped this for now to keep things simple).
Next, in our ViewModel we have to define the actual LoadedCommand command.
Important note: DO NOT FORGET to always use OnPropertyChanged in the command’s property setter if you want all of this to work properly:
private ICommand loadedCommand; public ICommand LoadedCommand { get { return loadedCommand; } private set { loadedCommand = value; this.OnPropertyChanged("LoadedCommand"); } }
Another note: we are defining our command as ICommand property and later we will set them with DelegateCommands instances but this is off course perfectly fine since DelegateCommand implements IDelegateCommand that implements ICommand interface.
And in the constructor of ViewModel with single lambda we added the actual code that will run when command is executed (we just set some string property called LoadedTime to the current DateTime formated by the format text given in the parameter).
Since the View is bound to this LoadedTime property it’s shown on the control when its changed, triggered by INotifyPropertyChanged:
this.LoadedCommand = new DelegateCommand<EventInformation<EventArgs>> (p => { LoadedTime = string.Format(p.CommandArgument.ToString(), DateTime.Now); });
Lets see how we would map a MouseMove event:
<Interactivity:Interaction.Triggers> <Interactivity:EventTrigger EventName="MouseMove"> <TriggerActions:MapMouseEventToCommand Command="{Binding Path=ShowMousePositionCommand}" /> </Interactivity:EventTrigger> </Interactivity:Interaction.Triggers>
So we are doing the same thing here only we are using MapMouseEventToCommand in stead of the basic MapEventToCommand – we are doing this so we get strongly typed MouseEventArgs from the original event passed as EventArgs parameter in the EventInformation class that will be passed to the command.
Here is the command definition from our ViewModel:
private ICommand showMousePositionCommand; public ICommand ShowMousePositionCommand { get { return showMousePositionCommand; } set { showMousePositionCommand = value; this.OnPropertyChanged("ShowMousePositionCommand"); } }
And here is part of the ViewModel constructor where we actually define the code triggered inside the command:
this.ShowMousePositionCommand = new DelegateCommand<EventInformation<MouseEventArgs>>( p => { this.MousePosition = string.Format("x:{0} y:{1}", p.EventArgs.GetPosition(null).X, p.EventArgs.GetPosition(null).Y); });
What happens here is that when our command is invoked (this is triggered when user moves his mouse pointer in our View) we use the received MouseEventArgs (from EventArgs property) of the EventInformation instance to get the current mouse position and we create a string representation of it and we set a public property on our ViewModel.
And our View is again data-bound to this property – so it will get notification that its value has changed and re-display it on screen so we will have realtime coordinates of our mouse pointer displayed in the view as we move the pointer.
Check out the demo application to see this in action.
So we managed to wire up events from View to the ViewModel in Xaml via DataBinding.
Here is how our View’s backend code .cs file looks like:
using System.Windows.Controls; namespace EventCommands.Views { public partial class ShellView : UserControl { public ShellView() { InitializeComponent(); } } }
As you can see we added zero logic to our View – its only code generated by the Visual Studio when we created the UserControl for the, so its clean design, decoupled View from the ViewModel, and we can later change our Views and our ViewModels won’t event notice 😉
For those that do not have Silverlight 4 installed yet, here is the screen shot of the running demo application showing all the code we have written for this experiment:
If you run the application you will see two independent instances of the small sample EventsWidget for showing Events triggered on View.
Currently im demonstrating MouseMove event, DragAndDrop event, KeyDown event and also Loaded event.
Other UI events can be easily added and are completely supported by the given code.
I deliberately added two instances of same widget on the main page so its clear that data binding works independently and that each control can has its own ViewModel and DataBinds events to Commands in its own ViewModel.
In same way the main control (ShellView) has its own ViewModel and its own Events DataBound to commands in its own ViewModel etc.
You can download the sample application Visual Studio 2010 solution here.
Hi,
This sounds like a more complicated implementation of the EventToCommand trigger available in the MVVM Light toolkit. For instance, I am not sure why you need multiple command types. Just use a generic command like the RelayCommand (also available in MVVM Light) to map the command to a method, and pass the event args to it if you need it.
http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
http://blog.galasoft.ch/archive/2009/11/13/bug-correction-in-messenger-and-new-feature-in-eventtocommand-mvvm.aspx
http://www.galasoft.ch/mvvm/getstarted/
http://mvvmlight.codeplex.com/
Cheers,
Laurent
True, but I wanted to have strongly typed commands so that is why I introduced multiple classes and different EventInformation classes (i introduced another layer of abstraction and therefore introduced more complexity).
But you can always use the generic EventToCommand that works with EventArgs if you do not want to receive strongly typed versions.
Thanks,
Slobo
Actually, you can have the best of both worlds: By using a RelayCommand, you get the typed event args information. For example RelayCommand.
Also I am not sure that an additional layer of abstraction should introduce more complexity… isn’t the goal to make MVVM simpler to understand? 😉
This last comment was tagged “tongue in cheek” but the blog swallowed the tag I had added 😉
Yes the goal is the simplicity, but its the most difficult goal 🙂
I struggled a lot with the different approaches for the passing of the EventArgs information to the ViewModel and never could find the one that is really good so i settled for second best.
I will definitely look up the RelayCommand implementation to see if i can reuse it here.
My goal was not to use one specific MVVM framework but to create new one that fixes all the flaws in existing frameworks and reuses good things, while keeping the simplicity.
That means i have a long way to go from here 😀
Thanks for feedback!
“My goal was not to use one specific MVVM framework but to create new one that fixes all the flaws in existing frameworks and reuses good things, while keeping the simplicity.” What I love the most about you is your humility 😉
Obviously every maker of MVVM framework is striving to attain this goal. Unfortunately, we also have limited tools to our disposal (the .NET framework is not perfect, and we have to bend it sometimes to get it to do what we want). Also, I do not believe in “one framework to rule them all” because the scenarios in using MVVM are very diverse. This alone explains the great number of MVVM framework available. I am not sure that one more is going to solve all the issue. Rather, I have been working with the various teams at Microsoft (Silverlight, WPF, Cider and Blend especially) to bring changes in the tools and the MSFT frameworks that will help to solve some of the difficulties. As I said often, MVVM Light is a transient framework, and nothing makes me happier than removing a feature because it has been obsoleted by a new version of Silverlight or WPF.
That said, I will definitely keep an eye on your blog and shamelessly steal (oops, I mean “inspire myself of”) any good idea on there 😉
Cheers,
Laurent
Great post. Nice discussion :). Thanks
Hey,
I can’t compile the code.
The namespace “Framework.Abstractions.Silverlight.Commands” and “Framework.Implementors.Silverlight.EventMapping” seems to be missing. Does something need to be installed?
to @foobar and @webz:
I have rebuilt the solution in Visual Studio 2010 RC and uploaded it instead of the old version (for Beta2).
Download again and it will work in RC. (if VS complains about missing references just use Rebuild Solution and it will recompile all the projects).
Same goes for the online demo app, its updated to use latest Silverlight 4 version from the RC.
Thanks for notifying me that it was not working!
Cheers!
I can get your sample app to compile and run just fine. When I try and replicate your implementation in a brand new VS2010 silverlight application, it all compiles, but when I run it, none of the EventTriggers seem to be firing. Any ideas?
Well send me the zipped solution that does not work to webmaster [ a T ] roboblob.com and i will try to find whats the problem.
Good article! Did you know MS made WPF just to tease the Silverlight developers with functionality they would need to make it easy and fun to do Silverlight?
This is exactly what i was looking for. I know that the MVVM Light Toolkit makes this immensely easy, but if you need a solution that is out-of-the-box and not third party based….this is very sweet. Thank you so much for publishing this.
Glad you liked it.
Please post some links if you have some examples of your work or if you reuse ideas from this blog on some project etc it would nice to see that.
Thanks for your feedback!
Great post thank you very much, just what I was looking for.
I did find an error however and thought you would like to know.
When I tried to run a command that implemented a canExecuteAction I encountered a casting error. This is probably better explained in code. I have included my fix as well. Here is the Pastie.
http://pastie.org/1259256
Hi Aubrey,
thank you very much for your feedback and for pointing that issue out i will check it and update the code and sample if needed.
THANK YOU !!!
Your project runs perfectly. The best resource that i found.
Thank you very much.
Hi,
Can you make this work with Prism 4?
Thanks 🙂
Hi Stephane,
this actually works with Prism 4, just add the dll’s from this project to your Prism 4 project and use the same XAML and code and it will work without any problem.
I’m trying to learn wpf and MVVM and i’m getting tired of all the additional code you need to wire up commands ,events etc to the viewmodel.
here
http://marlongrech.wordpress.com/2008/12/04/attachedcommandbehavior-aka-acb/#comment-4596
is an example that requires 4 classes and 300 lines of code just so you can wire up an event from the view to the view model.
Why do i need to include the MVVM light toolkit or BLEND sdk to do what takes one line in code behind…where or where is the benefit other than adhering to the MVVM dogma ?
Any command, method , event in the view should be able to be bound in a single line to a corresponding method, property etc in the view model without creating a mini project.
Yes very often i ask myself the same question. Why i need to add so much code for such simple functionality like handling ui event etc.
MVVM is also often called ‘programming for masochists’ 🙂
But seriously there are many reasons why this is good. Its hard to create all this infrastructure but once its there you can just use it
but it gives you separation from UI, testability, you can easily replace parts of functionality because its decoupled etc.
But if you feel this is not your cup of tea simply skip it and do what many programmers do: just write it…
But don’t forget: usually when you ‘just code’ without plan or patterns later you pay the price when you need to maintain this.
Good luck!
I think you’d have to wait until .NET 4.5. Then you can make a spiffy cover-all markup extension for events that will create a binding to some ViewModel command.
(There won’t be any built-in extensions, for some reason…)
Which was sort of inspiring! Totally unforeseen. Now I understand what I am going to do tomorrow 🙂
your code is very good,but i have a question hot to determaine sepecific control events,for example loastfocus for a textbox
Sorry, could you please upload your samples once again?
Thanks for letting me know, i accidentally deleted all demos and sample code, so now i need to dig them out and upload again 🙂
Already found all code here:
http://code.google.com/p/mvvmframework/source/browse/trunk/trunk/src/MvvmFramework/MvvmFramework/PresentationModel/BindingEventsToCommands
Found it when looked for missing MapEventToCommand class implementation.
Thanks anyway, great work!
Hi Anton,
I’m glad you found that since i’m still digging my folders in search for old code samples…
Good luck in your MVVM adventures 🙂
Sorry, one more question.
What if I bind more than one command to one event? Is it possible?