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.
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.
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
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
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.
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
IsActive property is changed to
true when the
FrameworkElement gets focus, and
false when it loses focus.
Generally speaking, the steps for using an
- Construct an instance of
ActiveAwareCommand, usually as a
public static readonlymember of a view model class.
- In your view model class, expose a property that allows the view to provide an
IActiveAwareimplementation to monitor. This property should register and unregister the
IActiveAwareimplementation with the
- In your view, pass an implementation of
IActiveAwareto the view model when the view loads. Usually this will involve constructing an instance of
FocusActiveAwareAdapterthat wraps the view.
- Bind to the
ActiveAwareCommandfrom 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.