Introduction
The Model-View-ViewModel (MVVM) pattern has become so popular in the WPF community that, to many developers, it is the way WPF is done. It has the benefits of simplicity and directness and, if you already know Model-View-Controller, it is easy to learn.
Commands are straightforward and elegant in MVVM. A command is encapsulated in an ICommand
object, which is exposed as a property in the view model. Binding to such a command is as simple as this:
<Button Command=”MyCommand”>Click Me</Button>
That approach works very well if the button and its command binding can be defined in XAML at design-time. But what if the button, and the specification of which command it will be bound to, are loaded dynamically at run time?
This article explains the problems involved in implementing dynamic commands and offers a solution to the problem. I don't offer the solution as necessarily being a best practice. In fact, one reason for publishing this article is to solicit comments on other ways to solve the problem. If a better solution emerges from comments to the article, I will update the article (with credit, of course) to implement that solution.
Background
Let’s say you are creating an application that financial planners will use to manage their clients’ accounts. The main window contains a ListBox
that displays alerts for an account. Each alert contains a hyperlink that allows the planner to open a window so that she/he can take whatever action is required. For example, if a client’s account has a negative balance, the planner may need to open a window to review the account, to see what happened.
The hyperlinks are generated at run time by the application, and each hyperlink will need to invoke a different ICommand
from those available in the application. What that means is that we can't bind our hyperlinks at design-time.
The issue that we will address is “How do we implement the hyperlink feature, since we can't do design-time binding of their commands?”
The Demo App
The demo application models the ListBox
that is described in the usage scenario and shown at the top of this article. The main window for the demo app, MainWindow.xaml, is located in the View folder.
To keep things simple, the demo assumes that hyperlinks can invoke one of two commands; CompleteTransaction
, or ReviewAccount
. The ICommand
classes for these commands are in the ViewModel/Commands folder.
The domain model for the demo app (in the Domain folder) consists of a single object—an Alert
. An alert
object has three string
properties:
AlertText
: The text of the alert
LinkText
: The text of the hyperlink
LinkCommand
: The name of the command the hyperlink should invoke
An Alerts list is exposed by the view model as an ObservableCollection
.
The ListBox
is implemented with a DataTemplate
that allows us to include a hyperlink:
<ListBox Grid.Row="1" Margin="10,0,10,10" ItemsSource="{Binding Alerts}">
<ListBox.Resources>
<vm:PaddedStringConverter x:Key="StringConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=AlertText,
Converter={StaticResource StringConverter}}">
<Hyperlink Command="{Binding Path=LinkCommand,
Converter={StaticResource CommandConverter}}">
<TextBlock Text="{Binding Path=LinkText}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The implementation is fairly straightforward. Note that the TextBlock
uses an IValueConverter
called StringConverter
. This converter simply adds a space after the alert text, in order to separate it from the hyperlink. Value converters used by the demo app are in the ViewModel/ValueConverters folder.
Walkthrough
The application is initialized in App.xaml.cs. An override to the OnStartup()
handler creates the view model and adds three alerts to it. The first two alerts invoke different ICommands
; the third alert does not invoke a command.
At that point, we instantiate an IValueConverter
called CommandConverter
:
Resources.Add("CommandConverter", new CommandConverter(viewModel));
MainWindow mainWindow = new MainWindow();
mainWindow.DataContext = viewModel;
mainWindow.Show();
We will use this converter to get our dynamic commands later in the application. We instantiate this converter in code, rather than markup, because it needs a reference to the application's view model. I will explain why later. Once the main window is shown, WPF takes over and displays the ListBox
. The XAML is self-explanatory.
Dynamic Commands
And that brings us to the core issue: How do we dynamically bind a hyperlink to its command? Normally, when we lay out a window, we know in advance which command a control will invoke. That’s what makes MVVM simple to implement—we simply bind the control’s Command
property to the view model property that exposes the command we want.
But in this case, we don't know at design-time which command a hyperlink will invoke. All we know is that the name of the appropriate ICommand
will be contained in the Alert.LinkCommand
property. So, we need some way to get from the name of the ICommand
to the actual ICommand
itself.
The CommandConverter
The demo implements an IValueConverter
, called CommandConverter
, to solve the problem. The CommandConverter
accepts a string
that represents the name of an ICommand
, and it returns the ICommand
with that name:
switch (commandName)
{
case "CompleteTransaction":
command = new CompleteTransaction(m_ViewModel);
break;
case "ReviewAccount":
command = new ReviewAccout(m_ViewModel);
break;
default:
throw new ArgumentException("Invalid ICommand name passed in.");
}
The heart of the CommandConverter
is a simple switch
statement that instantiates and returns the appropriate ICommand
object.
You might notice that the CommandConverter
holds a view model reference (m_ViewModel
) as a member variable, and that it injects this reference into the ICommands
. The ICommands
hold the reference as a member variable, but they don't do anything with it.
The view model reference is designed to facilitate communication between the ICommands
and the view model. The ICommands
don't call the view model in the demo app, but they do so frequently enough in my production apps that I routinely inject a view model reference into all new ICommands
.
The feature is included in the demo to show how the view model reference gets injected into the CommandConverter
, and from there how it is injected into the ICommand
objects. The need for the view model injection also explains why the CommandConverter
is instantiated in code, rather than simply being declared as a XAML resource in MainWindow.xaml.
The result is as follows:
<Hyperlink Command="{Binding Path=LinkCommand,
Converter={StaticResource CommandConverter}}">
WPF reads the name of the ICommand
from the LinkCommand
property and passes it to the CommandConverter
. The CommandConverter
creates the specified ICommand
object and passes it back to the hyperlink’s Command
property. When the hyperlink is clicked, the command is invoked.
Call for Comments
This solution works, but quite frankly, it feels like a bit of a hack. I am not entirely comfortable pushing IValueConverter
as far as this solution does. But is there a simpler, more elegant solution?
Here are some arguments I see in favor of the solution described in this article:
- The creation of
ICommands
is centralized in one place.
- The solution is very similar to the way WPF loads images.
Here are some arguments I see against it:
- The solution may use
IValueConverters
in a way not intended by WPF’s designers.
- You have to know to look in value converters for
ICommand
instantiation.
The last item concerns me the most. In conventional MVVM, I can find all of my commands in my command properties. This solution requires me to hunt around for them, and value converters are not the first place I would expect to find ICommand
instantiation.
So, is the solution in the article the best way to implement dynamic commands, or can it be done with a very clever line or two of XAML? I look forward to your comments!
History
- April 24, 2009: Initial posting