Better User and Developer Experiences – From Windows Forms to WPF with MVVM: Part 5, Commands

In the last article, I explained how Windows Presentation Foundation improves upon data handling via it’s excellent support for data binding.  However, data is only part of the equation.  In order to be a more effective framework, WPF also provides us a way to more effectively handle our application specific logic.

It does this by providing a unified model for separating the originator of an action from the effect produced by the action, via the Commanding infrastructure.

Commands in WPF are a very powerful abstraction.  MSDN clearly states the goals of the commanding infrastructure:

What makes commands different from a simple event handler
attached to a button or a timer is that commands separate the
semantics and the originator of an action from its logic.
This allows for multiple and disparate sources to invoke the
same command logic, and it allows the command logic to be
customized for different targets.

This follows a simple, but pervasive goal in WPF – separation of logic from presentation.  In the case of commands, the goal is to decouple an “action” from the object on which its working, as well as from the user interface element that triggers the action.

Also MSDN’s Commanding discussion focuses on the RoutedCommand implementations, for our purposes, we’ll use the ICommand interface directly.  ICommand is a simple interface, and it provides two functions:

  1. Provide a means (CanExecute method) to determine whether an action can be run in the current context, as well as a way to track when this changes (CanExecuteChanged event).
  2. Provide a method (Execute) to perform a given action

By providing this in a simple interface, we can make our own class which wraps this functionality into any series of delegates.  In our case, we made an ICommand class called ActionCommand, which lets us do some nice things.

For example, in the DataContext we use for our specific feed information UserControl, we start off by defining an ICommand as a property:

/// <summary>
/// Gets the open feed command.
/// </summary>
/// <value>The open feed command.</value>
public ICommand OpenFeedCommand
{
    get;
    private set;
}

We then initialize this directly using:

this.OpenFeedCommand = new ActionCommand(this.OpenFeed, () => true);

Basically, this provides us a Property, directly on our DataContext, that lets us call an OpenFeed method.  Also, since we can now directly expose the SelectedFeedItem due to Data Binding (as discussed in part 4), we simplify our logic as well.

Now, in our code, we can replace our event handler:

private void ButtonOpenLink_Click(object sender, RoutedEventArgs e)
{
    if (this.Feed != null)
    {
        FeedItem item = this.Feed.Items[this.listBoxFeeds.SelectedIndex];
        if (item != null)
        {
            System.Diagnostics.Process.Start(item.Link.AbsoluteUri);
        }
    }
}

With a dramatically simplified OpenFeed method:

private void OpenFeed()
{
    if (this.SelectedFeedItem != null)
    {
        System.Diagnostics.Process.Start(this.SelectedFeedItem.Link.AbsoluteUri);
    }
}

Our XAML will change, so instead of using the event handler, we switch to specifying the Command for our button directly, using WPF’s DataBinding:

<Button
   Grid.Row="1" Grid.Column="1" Height="23"
   HorizontalAlignment="Right" Margin="0,0,0,0"
   VerticalAlignment="Top" Width="60"
   Command="{Binding OpenFeedCommand}">
Open
</Button>

Note the differences here in approach – we’re accomplishing the same goal (hooking our code into the button being clicked by the user), but in a very different way.

In Windows Forms, and even in WPF when we use the event approach, we’re directly handling the button’s click event, and our logic gets mixed with the fact that we’re using a button in the interface.  If we were to change from a button to a menu, or a hyperlink, or some other control, we’d have to change our code to handle the different type of event, and move our logic across.  The code is always aware of what control is triggering some action.

Now, by using an ICommand, we’re doing something different.  What we are accomplishing here is making two statements in our code:

  1. Our user should be able to call a command to Open a feed (OpenFeedCommand)
  2. The OpenFeedCommand performs this function (launches the browser with the feed’s URI).

Nothing in the code knows how this command will get invoked – it can be from a button’s click (as it is here), but it could just as easily be triggered by any ICommandSource, including a Hyperlink, MenuItem, or even a KeyGesture.

The important point here is that the code does not know, or care, what triggered it.  We could rework the user interface to use completely different controls without changing one line of C# code, because we decoupled the action from what triggered the action by using a Command.

… Continued in Part 6, Templating …

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

7 Responses to “Better User and Developer Experiences – From Windows Forms to WPF with MVVM: Part 5, Commands”
  1. Vicente says:

    Nice post on commanding, short and to the point, as usual :)

Trackbacks

Check out what others are saying about this post...
  1. […] This post was mentioned on Twitter by Reed Copsey, Jr., Jim Straatman. Jim Straatman said: RT @ReedCopsey: Blogging about moving from WinForms to #WPF with #MVVM in #csharp – Part 5, Commands, is online http://bit.ly/7C0ApM […]

    [WORDPRESS HASHCASH] The comment’s server IP (208.74.66.43) doesn’t match the comment’s URL host IP (74.112.128.10) and so is spam.

  2. […] Now that I’ve covered some of the basic technological advances in WPF, mainly Data Binding, Commands, and Templating, it’s time to bring everything together, and demonstrate how this improves our […]

  3. […] to design, easy to maintain, and clear to understand.  By taking advantage of Data Binding, Commands, and Templating, we can rethink the way we build our applications, and design them using the […]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!