Download Source Code

Task-It Series

This post is part of a series of blog posts and videos about the Task-It (task management) application that I have been building with Silverlight 4 and Telerik's RadControls for Silverlight 4. For a full index of these resources, please go here. One of the posts listed in the index provides a full source download for the application.

Intro to Commands

In recent blog posts, like my MVVM post, I used Commands to invoke actions, like Saving a record. In this rather simplistic sample I will talk about the basics of Commands, and in my next post will go deeper into the subject.

What is a Command?

I remember the first time a UI designer used the word "command" I wasn't really sure what she was referring to. I later realized that it is just a term that is used to represent some UI control that can invoke an action, like a Button, HyperlinkButton, RadMenuItem, RadRadioButton, etc.

In Microsoft Word there are two controls you can click to Save the document, one in the Quick Access toolbar, and one is in the menu that pops down when you click that circle in the upper left (whatever that's called). When these two controls are clicked, naturally we want to invoke the same action.

Saving in Microsoft Word

Why should we use Commands?

I'm sure you're familiar with the code behind approach of handling events. For example, if you had a Button and a RadMenuItem that both invoked an action that saved a record to a database, you could add an event handler to each of the control's Click event. One problem, however, is that you would need two separate methods because the Button's Click event passes RoutedEventArgs as its second parameter, while the RadMenuItem passes RadRoutedEventArgs.

With commands, you can bind both the Button and the RadMenuItem to the same Command instance. An additional benefit is that Commands not only invoke an action, but also determine whether or not the UI control should be enabled.

The code

In the code provided for download above, MainPage.xaml simply loads a view called CommandTest, which resides in the Views folder. This view is tied to a view model class called CommandTestViewModel, which resides in the ViewModels folder.

In CommandTest.xaml we see that we have a Button and a RadMenuItem that are both bound to a SaveCommand (which lives in the view model):

<Button Content="Save" Command="{Binding SaveCommand}"/>
<telerikNavigation:RadMenuItem Header="Save" Command="{Binding SaveCommand}"/>

In the code-behind, CommandTest.xaml.cs, we set our DataContext to an instance of the view model:

DataContext = new CommandTestViewModel();

Now let's look at the SaveCommand:

private RelayCommand _saveCommand;
public RelayCommand SaveCommand
    get { return _saveCommand ?? (_saveCommand = new RelayCommand(OnSave, IsSaveEnabled)); }

First of all, unlike my previous example I am using the RelayCommand class from the MVVM Light Toolkit. In the past I've experimented with the DelegateCommand class in the Prism framework, the DelegateCommand class in the Silverlight.FX framework, and I've even just written my own DelegateCommand class and included it in my Silverlight project. All of these approaches work just fine.

The reason I am now using the RelayCommand is because the MVVM Light Toolkit also includes a very simple method of publishing and subscribing to events (I will blog about that soon), and because I want to have as few frameworks as possible in my app. The less frameworks, the less dll's, and the quicker my app loads. I like to keep things as clean and simple as possible.

Notice that the RelayCommand takes two arguments. The first is a function that is called when the Command is invoked. The second is the one that determines whether or not the Command should be enabled. Now let's take a look at the first method:

public void OnSave()
    // Clear the saved message
    SavedMessage = "";
    // Disable the Button/RadMenuItem
    _isSaveEnabled = false;
    // Simulate a Save operation that takes 5 seconds to complete
    // This could be a WCF RIA Services call to an Update method
    var bw = new BackgroundWorker();
    bw.DoWork += ((obj, args) => Thread.Sleep(5000));
    bw.RunWorkerCompleted += WorkCompleted;

The first thing we do is clear the text message that displays in a TextBlock at the bottom of the page. We'll see in a minute how this TextBlock will display "Saved" in red when the save action is complete.

The next thing is that we set _isSaveEnabled to false and raise an event on the SaveCommand that tells it that something about the state has change. This will effectively disable our Button/RadMenuItem.

The last thing is that I am calling a method to perform the Save. Don't be intimidated by the BackgroundWorker part, I am simply using this to simulate a 5 second delay before the operation completes. Just pretend that we are making some call to the database that saves some changes and it takes 5 seconds to complete. After it completes, the WorkCompleted method will be called, so let's take a look at what that does:

private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
    // Save has the Button/RadMenuItem
    _isSaveEnabled = true;
    // Show the saved message
    SavedMessage = "Saved";

The first thing is that we re-enable our Button/RadMenuItem, and then we display the "Saved" message at the bottom of the page.

Now, run the code and click either the Button or the RadMenuItem. Since they are tied to the same Command, they will do the same action. For the sake of example, click  the RadMenuItem, and notice how both it and the Button become disabled for 5 seconds:

After clicking Save

Of course, after you click the Save RadMenuItem, the RadMenu will close, so you will have to mouse over it again quickly to see it in the disabled state.

After 5 seconds, the Button and RadMenuItem will become enabled again, and the Saved message will appear at the bottom:

After save complete

Hopefully you can see the benefits of using Commands, and the fact that our view model is aware of everything that is happening in our UI, which allows us to write proper unit tests against the view model.

Related Posts


Comments are disabled in preview mode.