
Introduction
In WPF applications following the MVVM pattern, it’s not easy to control the View from a ViewModel class. Theoretically it should not be possible at all. But as you know, in practice this doesn’t always work. So similar to the Command pattern letting a View control its ViewModel, I’m propsing the ViewCommand pattern that works in the opposite direction, while maintaining the loose coupling of View and ViewModel.
Background
The Model-View-ViewModel (MVVM) design pattern is a great way to separate concerns of UI and business logic in WPF applications. It is based on the general principle that the View layer defines and handles anything UI-related while the ViewModel layer implements the business logic and prepares model data to be used in a view. This makes the view replaceable, for example by a dummy when running test cases on the application to verify the business logic implementation.
For this to work, the ViewModel must not know anything about the View it is presented in. There could even be multiple views presenting the same ViewModel or parts of it at the same time. Conversely that means that the ViewModel cannot control the view in any way. It merely provides the data for displaying and accepts any changed data for validation and processing.
Now sometimes when data is modified from a ViewModel class, it is advisable to somehow influence its presentation over the capabilities of style data binding. A colour highlighting can be provided through a property that a style can bind to. The visibility of some UI parts can also be controlled through such a property. But if a new item is added to a list, it should be made visible by scrolling to its position. If a new text input becomes visible, it should be focused so that the user can continue typing right away. All of these actions are not states that could be represented by a state variable, but rather like events that are raised in the view. One-time actions, not time-span states. While some suggest using a state variable for such tasks and reacting on changing their value, this solution has the issue that nobody will reset the state when it no longer matches reality and when coming back to the View, it will still be set and perform that action again.
This is where the ViewCommand pattern comes in. Much like the Command mechanism used to start actions in the ViewModel from the View, it can be used to start actions in the View from the ViewModel.
Using the code
For this connection between the View and ViewModel to be set up, each view must welcome any new DataContext that comes in. This DataContext is the ViewModel that may want to send commands to the view that is currently displaying it. There’s a shortcut method that does the DependencyProperty metadata overriding for you with just a single memorisable call (marked with the arrow):
public partial class TextItemView : UserControl
{
static TextItemView()
{
ViewCommandManager.SetupMetadata<TextItemView>(); }
public TextItemView()
{
InitializeComponent();
}
}
In this example, TextItemView is the view that displays an item of something with a text input control.
Next is the definition of the commands that a view wants to offer. If there are controls in the view that may need to be focused, then an appropriate implementation could look like this:
public partial class TextItemView : UserControl
{
[ViewCommand]
public void FocusText()
{
MyTextBox.Focus();
}
}
Following is the XAML code of our view, really nothing special here:
<UserControl
x:Class="MyNamespace.View.TextItemView"
(The usual XML namespace declarations)>
<Grid>
<TextBox
Name="MyTextBox"
Text="{Binding SomeText}"/>
</Grid>
</UserControl>
To enable a ViewModel class as a source of ViewCommand invocations, and to allow a view to register with it, the ViewModel class must implement the IViewCommandSource interface. This just adds a ViewCommandManager property that can be used by views to register and by the ViewModel itself to invoke commands on the view(s). This is what a basic ViewModel class could look like:
class TextItemViewModel : ViewModelBase, IViewCommandSource
{
private ViewCommandManager viewCommandManager = new ViewCommandManager();
public ViewCommandManager ViewCommandManager
{
get { return viewCommandManager; }
}
private string someText;
public string SomeText
{
get { return someText; }
set
{
if (value != someText)
{
someText = value;
OnPropertyChanged("SomeText");
Somewhere.Else.FileModified = true;
}
}
}
}
Now everything is set up to use the whole ViewCommand thing.
Finally, calling a command on a view can be done through the ViewCommandManager.Invoke method. Just pass it the name of the command as first argument and it will try its best to call the command method on every registered view instance. Just like the Command pattern uses data binding to fetch the command object from the data context, which in turn uses reflection to look up the property, this class also uses reflection to find the command method in the view class. So there’s no additional connection setup required. Just add the methods in the view and call them through the ViewCommandManager class. Here’s an example (spot the bug!), this time from a parent ViewModel that is managing a collection of TextItemViewModels.
private void OnAddText()
{
TextItemViewModel newVM = new TextItemViewModel(this);
TextItemVMs.Add(newVM);
Somewhere.Else.FileModified = true;
newVM.ViewCommandManager.Invoke("FocusText");
}
But... this still contains a major issue. Did you find it?
The OnAddText method just created a new instance of the ViewModel and added it to an ObservableCollection<TextItemViewModel> for the parent view to pick it up and display it in some ItemsControl. A template directs it to use our TextItemView control for each instance in the list. So as soon as the new item is added to the collection, a new view instance will be created and assigned the respective ViewModel instance. But this is also done on the UI thread and only after the thread is free from the OnAddText method. This means that at the time the new ViewModel is added to the collection, the view may not yet exist and thus newVM isn’t assigned as its DataContext yet and consequently the view isn’t registered with the ViewModel yet. Invoking a command now would not do anything because there is no view registered.
To overcome this issue, the command invocation needs to be delayed until this association is completed. The current Dispatcher can be used to invoke methods asynchronously at a given priority. Our priority is Loaded which comes after DataBind and Render, when the view is created, with data context, and registered in the ViewModel. For our focus action, it also makes sure that the control to be focused is already on the screen. To make this common scenario easier to use, there’s the InvokeLoaded method that does that for you:
private void OnAddText()
{
TextItemViewModel newVM = new TextItemViewModel(this);
TextItemVMs.Add(newVM);
newVM.ViewCommandManager.InvokeLoaded("FocusText"); }
In other situations where the addressed view already exists, using the normal (synchronous) Invoke method is perfectly fine though.
Also note that if you use the same ViewModel instance in multiple Views, doing something like setting the UI focus won’t work correctly because the second view will steal the focus from the first one. Other actions like scrolling a list or starting an animation will work on every registered view.
Implementation
The implementation of the ViewCommand pattern is actually pretty simple. It’s contained in the file ViewCommand.cs in the sample application and consists of three types. First, and most important, is the ViewCommandManager class. It provides static methods for the DependencyProperty metadata overriding. This allows to monitor changes to the DataContext property which means that another ViewModel instance is now presented by the view. This uses the next group of members in the ViewCommandManager class: RegisterView and DeregisterView. Obviously a ViewModel should only be able to control the views that are currently displaying them. This, by the way, makes a notable difference to the MVVM Light Messenger class that was also suggested as a solution to the above described focusing problem. A ViewModel can simply issue a command, and the ViewCommandManager will automatically find the currently connected views from the list it keeps. Last is the Invoke methods, that actually send the command to the views. You can also pass any number of arguments to the Invoke methods, but you must ensure that number and types match the ViewCommand method in the View class.
Also in this file is the interface IViewCommandSource that makes ViewModel classes recognisable by the automatic registration (only ViewModel classes implementing this interface can be registered and thus send ViewCommands anywhere), and the ViewCommandAttribute class that is used to mark ViewCommand methods in the View class. This attribute is used to ensure that only those methods in a view can be called through this mechanism that are explicitly declared as such.
Worth mentioning is that the ViewCommandManager class keeps weak references to the registered views. This means that no hard reference to the views is held, which could lead to memory leaks if views are unloaded from the UI and not used anymore, but would still be kept referenced from a ViewModel as connected View. Those views can be garbage-collected and free their memory without the need to explicitly deregister from their ViewModel on unloading. Views that no longer exist will simply be skipped when invoking command in views.
Since the ViewCommandManager property in a ViewModel is public, it can be accessed from other ViewModels in the hierarchy like regular data properties. Just like in the example above, the parent ViewModel that creates a new sub-ViewModel can invoke a ViewCommand on that child instance.
Room for improvements
Just like normal Data Binding will generate a Trace message when it cannot find a property, the Invoke method could also do this to help the developer finding problems with the used commands. Two types of errors could be detected: Either when the requested command method is not found in the View class, or when no views are currently registered at all. The latter may be the case when trying to invoke a ViewCommand on a view that wasn’t created yet.
Notice
Because of the use of reflection and no type-binding to the View class, you won’t get any IntelliSense support when selecting a ViewCommand. Code refactoring will also ignore the method names where you call them. And in case you use code obfuscation to protect your code in closed-source applications, you must exclude the ViewCommand methods from renaming or they cannot be found at runtime. (The ViewCommandAttribute may help you in specifying the affected methods.) But none of these points make a difference to the DataBinding that is already used in WPF and XAML.
The ViewCommandManager instance methods are not threadsafe. They will also not perform any cross-thread marshalling of commands to the UI thread. Should this be necessary (does anybody use ViewModels in separate threads?), appropriate locking and dispatching would need to be implemented.
History
2013-02-28 First version.