Thursday, May 07, 2009

MVVM Infrastructure: ActiveAwareCommand

This post is part of a short series I am doing on MVVM infrastructure. In this series, I will share some thinking and code that has helped to produce cleaner, simpler MVVM code in my applications.

Related posts:

  1. POCOs versus DependencyObjects
  2. ViewModel
  3. DelegateCommand
  4. ActiveAwareCommand

In my last post I discussed the DelegateCommand, which allows you to associate a WPF command directly with logic in your view model. In this post I am going to talk about the ActiveAwareCommand, which does exactly the same thing, but it enables you to widen the scope from which the command is used.

The DelegateCommand works great when the command source (such as a Button) has a one-to-one relationship with the view model. For example, recall the customers example from the previous post (see right). The Add and Delete buttons are defined in the CustomersView. Since the CustomersView has a one-to-one relationship with the CustomersViewModel, the Buttons also have a one-to-one relationship.

But suppose we wanted to change the Add and Delete buttons to MenuItems, and re-use those MenuItems across multiple views. Essentially, we want to change the one-to-one relationship between command source and view model to a one-to-many relationship. With DelegateCommands, the MenuItems would be intimately tied to a particular view. Therefore, the best effort would result with multiple Add and Delete entries in the Menu – one for each view that supports adding and deleting items. We’d have to distinguish the MenuItems with unique headers, such as “Add Customer” and “Add Order”. Obviously, that would result in a horrible user experience.

ActiveAwareCommand provides a solution to this little dilemma by maintaining a list of potential targets for the command, and only interacting with the active target (if there is one). Each potential target is an implementation of IActiveAware, and the ActiveAwareCommand uses the IsActiveChanged event on this interface to track the active target. The ActiveAwareCommand belongs to a single view model but maps to any number of command sources, thus fulfilling the one-to-many relationship we seek.

The IActiveAware interface gives you the freedom to use whatever logic you like to determine whether the potential target is active. However, it is likely that you will most often use focus to activate an item. To that end, the FocusActiveAwareAdapter wraps a FrameworkElement and gives you an implementation of IActiveAware whose IsActive property is changed to true when the FrameworkElement gets focus, and false when it loses focus.

Generally speaking, the steps for using an ActiveAwareCommand are:

  1. Construct an instance of ActiveAwareCommand, usually as a public static readonly member of a view model class.
  2. In your view model class, expose a property that allows the view to provide an IActiveAware implementation to monitor. This property should register and unregister the IActiveAware implementation with the ActiveAwareCommand.
  3. In your view, pass an implementation of IActiveAware to the view model when the view loads. Usually this will involve constructing an instance of FocusActiveAwareAdapter that wraps the view.
  4. Bind to the ActiveAwareCommand from a shared piece of screen real estate, such as the main menu or application ribbon.

To demonstrate the use of ActiveAwareCommand, I put together another TMNT-related sample project (I’m totally scoping the 2003 era series, like, start to finish at the moment, dudes). In it, the user is able to edit the details of any number of characters in separate tabs. Each character can be saved independently of the others from both the main menu and a tool bar button. Since an ActiveAwareCommand is used for the save functionality, the command is correctly routed to the active tab without any need for manual redirection by the views.

I may have gone a bit overboard with the sample project. I initially started out writing a simpler example, but I found myself wanting something a bit more substantial and real-world. Anyway, feel free to ignore most of the code and just concentrate on the parts pertinent to this post.

I think that about wraps up my series on MVVM infrastructure – for now at least. I hope it has been of some use. I’ve got some other things in mind that I want to blog about, but I’m on leave in Australia through to June. Until then…

† Prism has a similar interface, but it is used for different purposes. Same goes for Prism’s CompositeCommand, which is used to broadcast a command to multiple handlers, as opposed to unicasting only to the active handler.

3 comments:

Michael Hedgpeth said...

Instead of creating a static SaveCommand, why not have a MainWindow that has a SaveCommand which the others register through (possibly through an injected dependency)? That would make the design more testable (but I'm not sure you're into that).

Glenn Block said...

Hey Kent

How is this different than IActiveAware in Prism? It seems to have a similar function?

Kent Boogaart said...

Sorry for the delayed response - I've just returned to the UK after a holiday in Oz.

@Michael: sure, I see no reason why you can't do it that way. The important thing is that the same ActiveAwareCommand instance be used for all views that can handle that command.

@Glenn: you're right that it provides similar behavior (I admit I didn't look into Prism's implementation enough at the time and thought it had more to do with region management). That said, the biggest difference between the two is that Prism places the onus on the command handler (the VM) to check whether the command is active. My implementation won't even communicate with the command handler unless it is active.