As WPF and Silverlight developers, one of the most powerful tools in our utility belt is command binding.
A quick dive into WPF
WPF has a fairly full-featured command infrastructure, including a rather useful type called the CommandManager, which ensures that our controls are correctly enabled and disabled as the Command to which they are bound changes the answer it gives to the question CanExecute(…).
The WPF RoutedCommand class integrates with CommandManager by implementing its CanExecuteChanged event over the CommandManager's own RequerySuggested event , like this:
This has the effect of raising CanExecuteChanged on our command whenever the CommandManager.RequerySuggested is raised, thus notifying anyone who is bound to that command that they should call CanExecute() again and update their state accordingly.
This leads to the question, when is RequerySuggested raised?
The first, and perhaps most obvious case is when you explicitly call the static method CommandManager.InvalidateRequerySuggested(). You might do this at the end of a complex internal state change to ensure that the UI correctly reflects the result of that change.
You can see this happening in some of the controls that are part of the framework itself. DataGrid explicitly calls InvalidateRequerySuggested() when it changes its internal state – at the end of the AddNewItem() method for example.
If we had to do this manually for every possible change of state, this would get very boring, very quickly. Fortunately, the WPF framework has another trick up its sleeve. In an interactive application, most (though by no means all) application state is mutated by some kind of user-originated input; if we raise the requery event whenever the user clicks something, or types something, or touches something, then we'll catch most of the cases we need. To do this, WPF uses a class called the CommandDevice.
CommandDevice adds a handler for the PostProcessInput event which is exposed by a class called the InputManager. The InputManager looks after all of the keyboard, mouse, stylus and touch devices for WPF, and raises this event for us when input occurs. The CommandDevice responds to this event (amongst other ways) by calling InvalidateRequerySuggested()– thus updating the state of our command bindings whenever the user manipulates the UI, without requiring our manual intervention.
This doesn't just work for RoutedCommand, of course. To augment the view-oriented RoutedCommand class, most developers using the MVVM pattern take advantage of something like the RelayCommand originally popularized by Josh Smith in his MSDN magazine article. This is allows us to define commands that delegate to methods on our view model, rather that the view, with separation of concerns and testability in mind.
Here's endjin's implementation of that class (inherited and tweaked down a long path from Josh Smith via Laurent Bugnion).
(There is a generic version of this too that you'll find in the sample code attached to this post.)
Notice how we're implementing the CanExecuteChanged event handler in exactly the same way as the RoutedCommand.
Things are a bit different in Silverlight. Although ICommand exists, none of the rest of the infrastructure is present. Specifically, you have to build your own command implementation (the RelayCommand pattern is all we really need for our MVVM apps); but what to do about the lack of CommandManager?
Traditionally, the approach people have taken is to implement and manually raise the CanExecuteChanged property on your RelayCommand instances. This is exactly the tedious and error-prone technique I mentioned earlier. A better approach might be to implement our own CommandManager for Silverlight.
We've got two goals in mind:
1) Automatic raising of CanExecuteChanged when we think we might need it
2) Centralizing InvalidateRequerySuggested() rather than forcing you to handle it for each and every command.
Clearly, we can achieve the second goal, just by creating a CommandManager class with the necessary event and method, and implementing our RelayCommand over it in exactly the same way as the WPF implementation (in fact, you can just share the RelayCommand source code unchanged between the two platforms if you do this).
But what about the first goal?
Sadly, we can't integrate into the Silverlight input food-chain in the same way as the WPF implementation does. What other integration points do we have? Probably the best place we can get some automation is in our INotifyPropertyChanged implementation.
If you're reading this, you probably know all about INotifyPropertyChanged. It is the interface that we implement on our view model classes to integrate with the data binding infrastructure in WPF and Silverlight. If not, then this MSDN how-to can get you started.
We can leverage the fact that whenever any property change occurs in which the UI might be interested, the OnPropertyChanged() method will be called; so we call InvalidateRequerySuggested() from in there.
Isn't that going to be a massive performance problem? Every single property change kicking off a UI refresh?
Well – it might have been, but we haven't looked at the implementation of CommandManager.InvalidateRequerySuggested() yet. Here it is:
Current is our internal singleton for the CommandManager (and is an instance of the CommandManager itself). Let's take a look at that event-raising method.
The key pattern here is that we use the DispatcherHelper (a class that wraps the UI Dispatcher) to asynchronously invoke our method that calls all the event handlers (which are managed as weak references to avoid a leak).
This doesn't mean that we're calling the handlers from a background thread – nothing "concurrent" is happening. What actually happens is that we're waiting for our event handling to unwind itself back to the idle state and then call the handlers. This all happens 'in order' – you're just deferring the bit of code that calls the handlers until after we've finished dealing with the current user input. If you hammer away at the keyboard or mouse, this takes priority, and we "lose" a bunch of changes of state, finally catching up with ourselves at the end.
This is particularly powerful if there are lots of knock-on changes from one property change – the UI doesn't flicker enabled/disabled/enabled/disabled if it is temporarily unstable before regaining its equilibrium.
Here's some sample code that illustrates this in action.
Note that this is all internal detail, and is subject to change, but is believed to be correct at the time of writing with .NET 4.0.
Actually, this is not strictly true. In WPF we could specify that we wanted to asynchronously invoke that method at idle priority. In Silverlight, we can't be so specific, but the semantics are sufficiently similar for this to work.