Click here to Skip to main content
15,881,812 members
Articles / Mobile Apps / Windows Phone 7

Apex Part 2: Adding Commands to an MVVM Application

Rate me:
Please Sign up or sign in to vote.
4.97/5 (15 votes)
21 May 2012CPOL8 min read 49.5K   1.1K   26   15
Learn how to add Commands to your MVVM application to add functionality without breaking the separation of View and ViewModel.

The Series

This is part of a series of articles on Apex and MVVM:

  1. Apex Part 1: Create Your First MVVM Application
  2. Apex Part 2: Adding Commands to an MVVM Application

Introduction

Apex is a framework for rapidly building MVVM applications for WPF, Silverlight or Windows Phone 7. In Part 1 of this series, we looked at how to create your first MVVM application. In this article, we'll take that first basic application and add some functionality via the Commanding mechanism.

Video Article

You can follow the article in detail by watching the ten minute video below - it takes only ten minutes to add this functionality to our sample application, but in the video I also describe the concepts that are going on and show some tips and tricks for using Apex.

Image 1

Direct link: http://www.youtube.com/watch?v=wt7nncMNRG8

Before You Begin

This article actually builds upon a very simple application that was written in Part 1. If you are familiar with the basics of MVVM, then you can probably skip it. There is also a ten minute video for it if you just want to get up to speed. If you haven't done so already, you will need to download the Apex SDK to add this sort of functionality to your own applications:

The Apex SDK: http://apex.codeplex.com

The Apex SDK is just the Apex Binaries for WPF, Silverlight and Windows Phone 7, as well as extensions for Visual Studio (such as Item Templates for Views and ViewModels).

The Starting Point

At the end of the last article, we had created a basic Contacts application that looks something like the below:

Image 2

We have a ViewModel for a Contact and a View for a Contact. We also have the main View and ViewModel. However, we've got no functionality - all we are doing is displaying data. What we're going to do is add Commands to our view models to allow us to start implementing business logic.

Commands - the Core Concept

What do we mean by commands? Think of it like this:

In traditional UI development, if we want to provide some functionality on (for example) a button being clicked, we subscribe to a 'clicked' event of a button (it may be an event in Windows Forms, or a Windows Message in Win32 or something similar). The code in the event handler performs the logic. But the logic is tightly bound to the UI. This means it's hard to test it with an automated tool unless the tool is physically pressing buttons.

With MVVM, we bind a command to a button (or other element). When the button is pressed, the framework attempts to call the command. This means that in general, the command code is stored in the ViewModel and is not bound tightly to the UI. Because the command is just code in a plain old C# class, we can write unit tests against it, reuse it with other views and so on.

This may seem a bit of an unnecessary separation of concerns, but as we go through this series, we're going to do some very smart things that would be much more difficult without this separation.

As a bit of a refresher (and to be aligned with the video), we're going to start by actually adding another notifying property to the Contact ViewModel - one for an email address. Why? Well, mostly to cement the learning episode and to remind you to use apexnp! If you've got the SDK type in apexnp and press tab twice - it'll write out your notifying property for you!

In the ContactViewModel key in apexnp and name the property 'EmailAddress' and set the type as 'string'. You should get something that looks like the below:

C#
/// <summary>
/// The NotifyingProperty for the EmailAddress property.
/// </summary>
private NotifyingProperty EmailAddressProperty =
  new NotifyingProperty("EmailAddress", typeof(string), default(string));

/// <summary>
/// Gets or sets EmailAddress.
/// </summary>
/// <value>The value of EmailAddress.</value>
public string EmailAddress
{
    get { return (string)GetValue(EmailAddressProperty); }
    set { SetValue(EmailAddressProperty, value); }
}

This is Apex specific - a NotifyingProperty is designed to look very similar to a DependencyProperty and automatically handles updating the UI via INotifyPropertyChanged.

We can now open the ContactView XAML and add the email address to our grid of controls. The changed section is in bold:

XML
    <!-- The main grid of controls. -->
    <apexControls:PaddedGrid Rows="Auto,Auto,Auto" Columns="*,2*" Padding="4">
 
        <!-- The Name label and textbox. -->
        <Label Grid.Row="0" Grid.Column="0" Content="Name" />
        <TextBox x:Name="textBoxName" Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
 
        <!-- The Birthday label and textbox. -->
        <Label Grid.Row="1" Grid.Column="0" Content="Birthday" />
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Birthday}" />
 
        <!-- The Email Address label and textbox. -->
        <Label Grid.Row="2" Grid.Column="0" Content="Email Address" />
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding EmailAddress}" />
        
    </apexControls:PaddedGrid>

This is a nice refresher of how Notifying Properties work. They're owned by the ViewModel - and the View just binds to them. We're going to move onto Commands now - and they operate analogously, they are owned and implemented by the ViewModel and the View just binds to them.

Updating the UI

First, we'll update the MainView.xaml to have a stack panel to hold some buttons and some buttons themselves. The new code is in bold:

XML
 <!-- The list of contacts. -->
<ListBox Grid.Column="0" ItemsSource="{Binding Contacts}" DisplayMemberPath="Name"
  SelectedItem="{Binding SelectedContact}" />
        
<!-- A contact view - bound to the selected contact. -->
<local:ContactView x:Name="contactView" 
 Grid.Column="1" DataContext="{Binding SelectedContact}" />
        
<!-- A stack panel to hold buttons. -->
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" 
 Orientation="Horizontal" HorizontalAlignment="Right">
            
  <!-- A button to allow a new contact to be added. -->
  <Button Width="120" Margin="4" Content="Add New Contact"  />
 
  <!-- A button to allow a contact to be deleted. -->
  <Button Width="120" Margin="4" Content="Delete Contact" />
</StackPanel>

Running the applications, we have the buttons in place but they don't do anything. First, we'll have to create the actual Commands that they'll bind to.

Creating the Add Contact Command

Navigate to the MainViewModel and at the bottom of the class, key in apexvmc, then press tab twice. This is the code snippet for an Apex ViewModelCommand. All you need to provide for the snippet is the name, which in this case is 'AddContact'. Keying in the name and pressing enter will give you the below:

C#
/// <summary>
/// Performs the AddContact command.
/// </summary>
/// <param name="parameter">The AddContact command parameter.</param>
private void DoAddContactCommand(object parameter)
{
}
 
/// <summary>
/// Gets the AddContact command.
/// </summary>
/// <value>The value of .</value>
public Command AddContactCommand
{
    get;
    private set;
} 

What we have now is a Command object as a property, named AddContactCommand and a function. The View is going to bind to the property - but until we actually create an instance of the object, it won't do anything. So the key step here to connect these two entities is to add the following bold line to the constructor:

C#
/// <summary>
/// Initializes a new instance of the <see cref="MainViewModel"/> class.
/// </summary>
public MainViewModel()
{
    AddContactCommand = new Command(DoAddContactCommand);
}

In this case, we have created the Command object - and provided it with the function to perform when the command is invoked. This function can be an Action or an Action<object> if you'd like to also be able to take a parameter. Now that we have a property of the view model, we can update the definition of the button in the XAML to actually bind to that command - here's how it'll look:

XML
<!-- A button to allow a new contact to be added. -->
<Button Width="120" Margin="4" Content="Add New Contact" 
 Command="{Binding AddContactCommand}" /> 

So we have bound the 'Command' dependency property of the button to the 'AddContactCommand' object of our view model. This means that when the button is pressed, the function DoAddContactCommand will be invoked. Now let's add the functionality - here's what the 'AddContact' command should do:

C#
/// <summary>
/// Performs the AddContact command.
/// </summary>
/// <param name="parameter">The AddContact command parameter.</param>
private void DoAddContactCommand(object parameter)
{
    //  Create the contact.
    ContactViewModel newContact = new ContactViewModel()
    {
        Name = "New Contact"
    };
 
    //  Add to the set of contacts.
    Contacts.Add(newContact);
            
    //  Select the new contact.
    SelectedContact = newContact;
} 

Run the application and feel happy - it all works!

What's key here? The business logic is totally separate from the UI. At no point in the code above do we add an item to a listbox, or select it in a listbox, or update textbox text values. We manipulate the ViewModel properties and the View decides how to present them. Our command contains pure business logic - and this can be bound to by another view in the future - or a view from a different platform (Silverlight, e.g.). We can also unit test this functionality. Typically, unit tests are easy to write for low level code, but if there is a user interface, it is very hard to automate the testing of that - but we can actually test the ViewModel directly and verify it works as we expect.

Creating the Delete Contact Command

Now that you know how the commanding mechanism works, it takes only a few seconds to add the delete contact command. First, we use apexvmc and add the name 'DeleteContact', then create the command instance in the constructor:

C#
/// <summary>
/// Initializes a new instance of the <see cref="MainViewModel"/> class.
/// </summary>
public MainViewModel()
{
    AddContactCommand = new Command(DoAddContactCommand);
    DeleteContactCommand = new Command(DoDeleteContactCommand, false);
}
 
/// <summary>
/// Performs the DeleteContact command.
/// </summary>
/// <param name="parameter">The DeleteContact command parameter.</param>
private void DoDeleteContactCommand(object parameter)
{
    //  Remove the selected contact from the contacts list.
    Contacts.Remove(SelectedContact);
 
    //  Clear the selected contact.
    SelectedContact = null;
}
 
/// <summary>
/// Gets the DeleteContact command.
/// </summary>
public Command DeleteContactCommand
{
    get;
    private set;
} 

Delete is even easier! Notice one difference - we have passed in the value 'false' to the constructor of the command as the second parameter. The second parameter is optional, and determines whether the command can be executed. The default is true. If the command cannot be executed, the user interface will display it as greyed out.

Why disable the delete command? Well, it only makes sense if there is actually a selected contact. So what we can do is initially have it disabled, and then update the SelectedContact property in the MainViewModel with the code in bold below:

C#
/// <summary>
/// Gets or sets SelectedContact.
/// </summary>
/// <value>The value of SelectedContact.</value>
public ContactViewModel SelectedContact
{
    get { return (ContactViewModel)GetValue(SelectedContactProperty); }
    set 
    { 
        //  Set the value of the property.
        SetValue(SelectedContactProperty, value);

        //  If we have a contact selected, we can execute the delete contact command.
        DeleteContactCommand.CanExecute = value != null;
    }
}

'CanExecute' is a property of Command. As soon as we change it, the user interface immediately updates to enable or disable the UI element. So again, we have business logic here that is totally separated from the UI. We've got very basic code for our commands that just does the job - we leave it to the View to decide how to present it.

Manipulating the View from the Command

Let's say we want to manipulate the View from the Command - for example, when the Add New Contact Command is invoked, we want to focus on the Contact Name textbox. How do we do this?

We don't! The ViewModel should never know about the view - because the view can be anything (or even in the case of a unit test, nothing). But in reality, of course, we must handle cases like this. Fortunately, there is a solid and sensible way of doing this. In the constructor of the main view, we add a handler for the Executed event of the command:

C#
public MainView()
{
    InitializeComponent();
 
    //  Add a hander for the executed event of the add contact command. 
    viewModel.AddContactCommand.Executed += new CommandEventHandler(AddContactCommand_Executed);
}
 
void AddContactCommand_Executed(object sender, CommandEventArgs args)
{
    //  Focus the contact name.
    contactView.FocusContactName();
}

'FocusContactName' is just a one-line function of the ContactView that focuses on the appropriate text box. So there we have it - the view can respond to commands being executed. We can even involve the view BEFORE a command executes - look at the code below:

C#
public MainView()
{
    InitializeComponent();
 
    //  Add a hander for the executed event of the add contact command. 
    viewModel.AddContactCommand.Executed += 
                      new CommandEventHandler(AddContactCommand_Executed);
 
    //  Add a handler for the executing event of the delete contact command.
    viewModel.DeleteContactCommand.Executing += 
                      new CancelCommandEventHandler(DeleteContactCommand_Executing);
}
 
void DeleteContactCommand_Executing(object sender, CancelCommandEventArgs args)
{
    //  Cancel the command if the user says they're NOT sure.
    args.Cancel = MessageBox.Show
         ("Are you sure?", "Sure?", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes;
}

The Executing event actually provides a CancelCommandEventArgs that allows the command to be cancelled. In this case, we show a message box asking the user if they are sure they want to continue.

Conclusion

We've now got the View, ViewModel, NotifyingProperty and Command concepts under our belt and are well on the way to becoming MVVM gurus! Please add any comments or questions below - as well as suggestions for the next part of the article (which so far I believe will be adding a Model).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
Follow my blog at www.dwmkerr.com and find out about my charity at www.childrenshomesnepal.org.

Comments and Discussions

 
GeneralMy vote of 5 Pin
hari111r30-Dec-12 22:56
hari111r30-Dec-12 22:56 
GeneralRe: My vote of 5 Pin
Dave Kerr30-Dec-12 23:32
mentorDave Kerr30-Dec-12 23:32 
GeneralMy vote of 5 Pin
SkyRunner13-Aug-12 9:10
SkyRunner13-Aug-12 9:10 
GeneralRe: My vote of 5 Pin
Dave Kerr30-Dec-12 23:31
mentorDave Kerr30-Dec-12 23:31 
GeneralRe: My vote of 5 Pin
SkyRunner31-Dec-12 21:45
SkyRunner31-Dec-12 21:45 
GeneralRe: My vote of 5 Pin
Dave Kerr1-Jan-13 0:19
mentorDave Kerr1-Jan-13 0:19 
GeneralRe: My vote of 5 Pin
SkyRunner4-Jan-13 5:23
SkyRunner4-Jan-13 5:23 
GeneralRe: My vote of 5 Pin
Dave Kerr5-Jan-13 6:51
mentorDave Kerr5-Jan-13 6:51 
GeneralRe: My vote of 5 Pin
SkyRunner9-Jan-13 1:11
SkyRunner9-Jan-13 1:11 
GeneralMy vote of 5 Pin
sonnywu@gmail.com6-Jun-12 13:01
sonnywu@gmail.com6-Jun-12 13:01 
GeneralRe: My vote of 5 Pin
Dave Kerr16-Jun-12 5:04
mentorDave Kerr16-Jun-12 5:04 
GeneralMy vote of 5 Pin
Mohammad A Rahman22-May-12 17:35
Mohammad A Rahman22-May-12 17:35 
GeneralRe: My vote of 5 Pin
Dave Kerr29-May-12 23:04
mentorDave Kerr29-May-12 23:04 
GeneralRe: My vote of 5 Pin
sonnywu@gmail.com14-Jun-12 10:01
sonnywu@gmail.com14-Jun-12 10:01 
GeneralMy vote of 1 Pin
eddy morrison22-May-12 2:12
eddy morrison22-May-12 2:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.