Commands versus Event Triggers in MVVM

Recently I’ve received some questions from the Simple MVVM Toolkit’s discussion forum on when to use commands versus Blend-style event triggers. For several reasons I tend to favor event triggers over commands in most scenarios, especially for Silverlight applications.

Download the code for this blog post here.

  1. One of the main benefits of commands lies in the CanExecute property of ICommand, so that you can enable and disable a control based on a condition.  ICommand exposes a CanExecuteChanged event which when fired causes a binding to check the CanExecute property.  Unfortunately Silverlight requires you to fire the CanExecuteChanged event manually, which is why implementations of ICommand (such as the DelegateCommand in Simple MVVM Toolkit) also expose a public RaiseCanExecute method.
    • You can easily achieve the same result as CanExecute simply by binding the IsEnabled property of a control to a boolean property on the ViewModel.  Then call NotifyPropertyChanged, passing the property name with a lambda expression, whenever you want the binding to check the value of the boolean property.
  2. Currently commands can only be used by controls that derive from ButtonBase, such as a Button or HyperLink Button.  And it can only be used to respond to the button’s Click event.  Other toolkits have an EventToCommand behavior that allows you to invoke a command from events other than Click and with controls other than buttons.  However, you can accomplish much the same thing with a Blend event trigger, without the need for all the extra code required in the ViewModel for a command.
    • You can use an event trigger from the Blend SDK with a CallMethodAction that allows you to invoke a parameterless method on the ViewModel from any event on any control.  The question then arises on how to invoke methods that have input parameters.  You can usually bypass this need because the method in the ViewModel has access to properties on the ViewModel that are bound to controls in the View.  For example, I could have a method called ShowCustomer.  Instead of adding a customer parameter to the method, I can simply reference a SelectedCustomer property on the ViewModel, which can be bound to the SelectedItem property of a combo box in the View.
  3. There is one scenario where I would want to use commands, which is to call a method in the ViewModel that accepts a parameter with a value that cannot be obtained from a property on the ViewModel.
    • A good example of this would be buttons on a calculator.  In this case you would want to pass in arbitrary values to an Add method, which is wired up to an AddCommand.  The CommandParameter property of the button is your friend here.  Simply specify a number corresponding to the relevant button.  The generic DelegateCommand in Simple MVVM Toolkit is implemented in a way that dynamically calls a Parse method on the generic type argument.

Here is what an event trigger would look like, wiring up the SelectionChanged event of a combo box to a ShowCustomer method on the ViewModel. Simple MVVM Toolkit comes with an XML snippet you can use to insert an event trigger / call method action.  To use it you need to open up a view by right-clicking on it in the Solution Explorer and choosing “Open With …” and selecting XML Editor.  Then you should make sure to add a end element for the combo box control (</ComboBox> instead of />).  Right-click in the empty content of the ComboBox element and select Insert Snippet from the context menu. Then open “My XML Snippets” and drill down until you find the “mvvmtrigger” snippet. Type “SelectionChanged” for the event name and “ShowCustomer” for the method name.

<ComboBox Height="23" ItemsSource="{Binding Customers}" SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
          HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Grid.ColumnSpan="2" DisplayMemberPath="CustomerName">
  <!-- Add reference to Microsoft.Expression.Interactions.dll, System.Windows.Interactivity.dll -->
  <!-- Use mvvmxmlns snippet to add i and ei namespace prefixes -->
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
      <ei:CallMethodAction
              TargetObject="{Binding}"
              MethodName="ShowCustomer"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</ComboBox>

The ShowCustomer method in the ViewModel looks like this.

public void ShowCustomer()
{
    if (SelectedCustomer != null)
    {
        MessageBox.Show(SelectedCustomer.CustomerName);
    }
}

Let’s take this example a bit further by adding a “Show Customer” button to the View that is only enabled when a customer has been selected.  We start by adding a CanShowCustomer boolean property to the ViewModel, so that we can bind the IsEnabled property of the button to it.

public bool CanShowCustomer
{
    get
    {
        return SelectedCustomer != null && SelectedCustomer.CustomerId > 0;
    }
}

Using the mvvmtrigger XML snippet we can add a trigger for the Click event that executes the ShowCustomer method on the ViewModel.  The button will be disabled when CanShowCustomer returns false.

<Button Content="Show Customer" Grid.Column="1" Height="23" Width="100"
        IsEnabled="{Binding CanShowCustomer}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <ei:CallMethodAction
              TargetObject="{Binding}"
              MethodName="ShowCustomer"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

The last thing we need to do is fire NotifyPropertyChanged for CanShowCustomer in the setter for the  SelectedCustomer property.

private Customer selectedCustomer;
public Customer SelectedCustomer
{
    get { return selectedCustomer; }
    set
    {
        selectedCustomer = value;
        NotifyPropertyChanged(m => m.SelectedCustomer);

        // Update bindings for CanShowCustomer
        NotifyPropertyChanged(m => m.CanShowCustomer);
    }
}

These examples illustrate how to wire up View events to ViewModel methods without using commands or the need for an EventToCommand behavior.  It works with all events on all controls and can be used to call parameterless methods on the ViewModel. I would only use commands in the case where you want to add a parameter to the ViewModel method with a hard-coded value passed in from the View via a CommandParameter.  For an example of this approach, take a look at my blog post on ViewModel-driven navigation, where I pass in the name of a page to a Navigate method on the ViewModel.

Enjoy,
Tony

About Tony Sneed

Married with three children.
This entry was posted in Technical and tagged , . Bookmark the permalink.

10 Responses to Commands versus Event Triggers in MVVM

  1. Pingback: Commands versus Event Triggers in MVVM | MS Access, XML and Silverlight | Syngu

  2. DEBAL says:

    HI
    Tonny, pl help me give your suggestion ……
    I have a simple project in Silverlight Simple MVVM toolkit . Now I want to see how Command Binding can use in a button . Suppose , I have datagrid and a Button in name “LoadData” in view page xaml. Now when user click the button some data will be bind in the datagrid immediately . I store some data a MockCustomerServiceAgent class .
    Now though I ‘m using ICommand interface , so I need to make a propety in ViewModel like the below code:
    public class ViewModel : ViewModelDetailBase
    {
    public ViewModel() //default ctor
    {
    }
    ICustomerServiceAgent serviceAgent;
    public ViewModel(ICustomerServiceAgent serviceAgent)
    {
    this.serviceAgent = serviceAgent;
    }
    …………………..
    Now I’m using ICommand interface to create a DelegateCommand Class ,(See , I don’t use trigger / Action , because lots of user of my code may not have Expression Blend 4 and my necessary is to built application with ICommand interface.)
    After Complete the DelegateCommandClass I again come back to ViewModel Class. I write a Property in ViewModel that is :

    Public ICommand LoadButtonCommand {get ; set;}
    this Property will call the DelegateCommand Class . I have another Property in ViewModel and this will use to bind my datagrid datacontext .

    public ObservableCollection FetchCustomer
    {
    get { return _fetch; }
    set
    {
    if (_fetch != value)
    {
    _fetch = value;
    OnPropertyChanged(“FetchCustomer”);
    }
    }
    }
    ………..
    Up to now this is fine . Now to need call the DelegateCommand Class using LoadCommand Property , I have to define it in ViewModel Constructor .
    Public ViewModel()
    {
    LoadCommand = new DelegateCommand(Method1,Method2);
    }
    Other wise How Can I call DelegateCOmmand Class , and if I do this , then The ViewModelLocator folder you have create to supress the load on ViewModel became unuseful . I want to use ViewModelLocator .
    How Can I do that ?
    PL reply me , if anything more pl mail me
    Thanks a lot to read my problem

    • Tony Sneed says:

      Hello Debal,

      I don’t believe using commands is required for compatibility with Blend. In fact, the event trigger / action is from the Blend SDK. :-) However, there’s no problem if you want to use commands — they are perfectly compatible with the Simple MVVM Toolkit. Have you tried inserting commands using the “mvvmcommand” code snippet? It puts all the code you need right in there. You have to wire up the command to a method that either takes no parameters, or one that takes just one parameter, in which case you would use the generic version of DelegateCommand.

      Cheers,
      Tony

  3. DEBAL says:

    Hi Tonny
    Thanks for your reply . I really find it useful using MVVM Commmand Snippet. Please follow my article where I tried my best to describe :

    http://www.dotnetfunda.com/articles/article1671-silverlight-commandbinding-with-simple-mvvm-toolkit.aspx

    Reply me there.

  4. Hello Tony!

    Greeting from Arhcangelsk, Russia. Thanks a lot for ur article, it’s very usefull! I will apply it in my project. Actually, I’ve just began to learn mvvm pattern. And I have a question: are the EventTrigger, CommandToEvent the same method to implement the mvvm pattern? What the difference of them? And what method is better? Thanks for advance!

    • Tony Sneed says:

      Greetings! I had to look up Arhcangelsk — I didn’t know there was a ‘European Russia.’ Cool.

      I’m an advocate of using event triggers with a call method action, in order to invoke a method in the ViewModel from an event fired from the View (for ex, button click, or combo box selected item changed). EventToCommand is for wiring an event to a command in the ViewModel. Generally, I think Commands are unnecessary and require a lot of extra boilerplate code, so I avoid them if possible. So the short answer is, use event triggers with a call method action instead of a command. You can find those when you install the Blend SDK.

      Cheers,
      Tony

  5. Tony cheers!

    This is John once again. I wanna thank U for this article – it helps me a lot. Can U believe – I’ve tried to implement MvvM pattern about 1,5 weeks. And now it works! Hardly shake U hand, friend!

    Sincerrely, John.
    Archangelsk, Russia.

  6. http://andcarinsurancequotes.com says:

    Some truly fantastic work on behalf of the owner of this site, absolutely outstanding articles.

  7. Juanello says:

    Tony!
    I spent better part of half a day trying to figure out how to enable/disable combo boxes based on the state of other combo boxes (using true MVVM form).
    Something simple as binding IsEnable to a boolean property.
    Works like a charm.
    Thanks for pointing me in a simple direction. :-)

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s