Communicating Between Views and ViewModels in Silverlight 4 Apps

Use the ICommand interface to associate control events with ViewModel methods

If you're building Silverlight 4 applications, then you've more than likely heard the term "commanding" used at a talk, in a forum, or in talking with a WPF or Silverlight developer. What is commanding and why should you care about it? In a nutshell, it's a mechanism used in the Model-View-ViewModel (MVVM) pattern for communicating between a View (a Silverlight screen) and a ViewModel (an object containing data consumed by a View).  It normally comes into play when a button is clicked and the click event needs to be routed to a ViewModel method for processing, as Figure 1 shows.

When I first learned about the MVVM pattern (see my blog post at tinyurl.com/DWahlinMVVM for an overview), there wasn't built-in support for commanding. So, I ended up handling button click events in the XAML code-behind file (View.xaml.cs in Figure 1) and then calling the appropriate method on the ViewModel, as Figure 2 shows.

While this approach works, adding code into the code-behind file wasn't what I wanted, given that the goal was to call the ViewModel directly without having a middleman.  What's a developer to do? To answer this question, let's examine the ICommand interface.

Using the ICommand Interface

Silverlight 4 added built-in support for the ICommand interface on any object that derives from ButtonBase (Button, ToggleButton, HyperlinkButton, etc.), letting you remove the event handler shown in Figure 2.  This results in a cleaner solution that ultimately lets you route a button click event directly to a ViewModel method, eliminating the middleman.  Figure 3 shows what the ICommand interface looks like. A button can be associated with an object that implements ICommand through the Button control's Command property.

The CanExecute() method determines if a command that is associated with a button is allowed to be processed or not.  For example, if you're trying to click a button to save customer data, the save button should trigger a commanding call only if all the required data is available. If CanExecute() returns false, the button associated with the command (the object implementing ICommand) will automatically be disabled due to the CanExecuteChanged event firing and notifying the button that the command isn't ready to be executed.  The Execute() method is used to route processing to a specific ViewModel method, such as the UpdatePerson() mentioned in Figure 2, when all of the required data is available and the command should be executed.

Implementing ICommand

Although Silverlight 4 provides support for the ICommand interface, it doesn't provide a built-in class that implements the interface.  As a result, you'll either need to write your own or use one of the many classes already out there.  I ended up using a class named RelayCommand and RelayCommand<T> from Laurent Bugnion's excellent MVVM Light project available at mvvmlight.codeplex.com.  The RelayCommand class (visit tinyurl.com/DWahlinRelayCommand) implements ICommand and makes it easy to bind a Button control's Command property to a ViewModel property and route user actions directly to ViewModel methods for processing.  Here's a breakdown of how it all works.

 

 

Most of us are used to double-clicking a button in the designer to handle the Click event. With commanding, you add a property into your ViewModel that implements ICommand and then bind the property to the button control using its Command property.  Once the ViewModel object instance is created, you'll add code to wire up the ICommand property to the method that should be called when the button is clicked.  In summary, the following steps need to be completed to use commanding in a Silverlight application:

  1. Add an ICommand property into your ViewModel class (I used the RelayCommand class for the sample application since it implements ICommand).
  2. Add code into the ViewModel to wire up the ICommand property to the ViewModel method that will be called when a button is clicked.
  3. Bind the button to the ViewModel's ICommand property using the Command property. 
  4. Optional: Allow the command to be executed (or not) based upon application rules. The sample code has a customized version of the RelayCommand class that provides an IsEnabled property that can be set to true when the command is ready to execute. The stock version of RelayCommand available in MVVM Light allows the command to be executed by default unless you tell it otherwise.

An example of defining an ICommand property (Step 1 above) in a ViewModel is shown next:

public RelayCommand UpdatePersonCommand

\\{

   get;

   private set;

\\}

 

Once the ViewModel is created, you need to wire the ICommand property to the method that is called when the command is executed (step 2 from the previous list). The code in Figure 4 accomplishes this task in the ViewModel's constructor by calling the WireCommands() method.

Finally, the target button control must be bound to the ICommand property defined in the ViewModel using its Command property (step 3 from the previous list):

<Button Command="\\{Binding UpdatePersonCommand\\}" Content="Submit" 
   Height="20" Width="100" HorizontalAlignment="Left" />

As the user clicks the button, the ViewModel's UpdatePerson() method will be called directly. While commanding isn't quite as simple as double-clicking on a button and handling the Click event, it provides a great way to separate concerns, keep code clean, and hook Views directly to ViewModel methods.

Using Button's CommandParameter Property

ButtonBase also exposes a CommandParameter property that can be used to pass a parameter value to a method.  The CommandParameter property can be bound to control values using standard data binding techniques as shown next:

<Button Command="\\{Binding UpdateFirstNameCommand\\}"
   CommandParameter="\\{Binding ElementName=FirstNameTextBox, Path=Text\\}"
   Content="Update" />

In this example, the text value from a text box named FirstNameTextBox is passed when the button is clicked.  Figure 5 shows how the ViewModel command property changes when a parameter needs to be passed to a method using ButtonBase's CommandParameter property. Note that a generic version of RelayCommand is used (available with MVVM Light and in the sample code) so that the data can be passed in a strongly typed manner.

Commanding works very well once the different pieces are in place, but what happens if some of the events firing in your Views are triggered by controls that don't derive from ButtonBase? We'll need to call in reinforcements to handle the situation.

 

 

Using Blend's InvokeCommandAction

Expression Blend 4 offers an InvokeCommandAction that can be used to hook events from controls that don't derive from ButtonBase directly to ViewModel methods.  This is extremely useful in applications that need to communicate between View and ViewModel classes using controls other than Button, HyperlinkButton, etc.  For example, you may create a menu composed of multiple StackPanel controls each containing Image and TextBlock controls.  As the user clicks on one of the menu items, you may want to invoke a ViewModel method.  Using the approach shown earlier, you can't do this since there's no Command property on StackPanel, Image, TextBlock, or any other control that doesn't derive from ButtonBase.  Fortunately, Blend's InvokeCommandAction can be used instead.

Associating InvokeCommandAction with a Silverlight control event can be done directly in XAML or visually in Expression Blend. If you don't have a Blend license, you can get everything you need by downloading the Expression Blend SDK for Silverlight 4 (get it at tinyurl.com/DWahlinBlendSDK), which includes the System.Windows.Interactivity.dll assembly needed to use InvokeCommandAction.

If you are typing the XAML by hand, you'll first want to add a reference to System.Windows.Interactivity.dll (available in the Blend SDK) into your Silverlight project to use the trigger and action features. Once that's done you can add a namespace and assembly reference onto the root XAML tag as shown next:

xmlns:i="clr-namespace:System.Windows.Interactivity;
   assembly=System.Windows.Interactivity"

Once the namespace prefix is added, you can add the XAML shown in Figure 6 within the element that will raise an event that needs to be routed to the ViewModel. The code in Figure 6 handles the MouseLeftButtonUp event of a StackPanel and binds it to an ICommand property in the ViewModel named AddPersonCommand. Any type of event can be associated with a command property using the EventTrigger element's EventName property.

If you have a copy of Expression Blend 4 you can go to the asset window, search for InvokeCommandAction, and then drag and drop it onto a target control, such as StackPanel.  From there you can go to the properties window to select the proper event and bind the Command property to a ViewModel property that supports ICommand. Figure 7 shows what the property window looks like in Blend after the EventName has been picked.

Take Command of Your Silverlight Apps

Commanding is a key part of the MVVM pattern and provides a way to associate control events directly with ViewModel methods.  In this article you've seen how commanding can be implementing using built-in support for ICommand as well as how additional controls and events can be hooked to command properties using InvokeCommandAction. The sample code available with this article demonstrates both techniques and should help jumpstart the process of adding commanding into your Silverlight 4 applications.

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish