Defining CommandBindings in the ViewModel

28 07 2008

These are some variations about the Command Bindings between the view and the view-model following the DataModel-View-ViewModel pattern series of Dan Crevier’s Blog.

Recently Josh Smith has added a code project article showing another approach to the same pattern.

What I don’t like of these two approaches is the way that they define the each command binding independly in the view: for each button (or other UIElement) they must define the command and the binding:


<Button Command="{Binding AddCommandModel.Command}"
  CommandParameter="{Binding Path=Text,ElementName=AddSymbol}"
  local:CreateCommandBinding.Command="{Binding AddCommandModel}">
...

Josh Smith version it’s different in that he makes the binding at the view level, but it also does it for each command:

<UserControl.CommandBindings>
    <jas:CommandSinkBinding Command="vm: PersonViewModel.DieCommand" />
    <jas:CommandSinkBinding Command="vm: PersonViewModel.SpeakCommand"/>
</UserControl.CommandBindings>

What I suggest is to that make the viewmodel register all his commands. This will simplify the view in some degree and will be more clear in general.

The ICommandContext interface

For that we need to define the following interface


public interface ICommandContext
{
    List<CommandModel> Commands { get; }
}

Now each viewmodel with commands has to implement this interface to define his commands. Here is an auxiliary base class for the viewmodels

public class ViewModelBase : INotifyPropertyChanged, ICommandContext
{
    List<CommandModel> _commandList;

    public List<CommandModel> Commands
    {
        get
        {
            if (_commandList == null)
            {
                _commandList = new List<CommandModel>();
                this.AddCommandModels(_commandList);
            }

            return _commandList;
        }
    } 

    public virtual void AddCommandModels(List<CommandModel> List)
    {
    }

    ... implementation if INotifyPropertyChanged

Suppose we have a class VMPersons wich is the viewmodel of a view showing a list of persons, and let’s suppose this has one command for adding new persons to the list and another command for deleting persons. This class will be something like that


public class VMPersons : ViewModelBase, ICommandContext
{
...

public override void AddCommandModels(List<CommandModel> list)
{
  list.Add(new NewCommand(this));
  list.Add(new DeleteCommand(this));
}

private class NewCommand : CommandModel
{
  ... implementation of the New command
}

private class DeleteCommand: CommandModel
{
  ... implementation of the DeleteCommand command
}

...
}

Now, instead of defining each binding for each command using attached properties, we define an unique “command context” property for all the view. If the DataContext is already set to de viewmodel, definind de command context is as simple as doing the following attribute

local:CreateCommandContext.Context="{Binding}"

And here is the implementation of the CreateCommandContext class (this is a variation of Dan Crevier implementation)

public static class CreateCommandContext
{
  public static readonly DependencyProperty ContextProperty
    = DependencyProperty.RegisterAttached("Context",
        typeof(ICommandContext), typeof(CreateCommandContext),
        new PropertyMetadata(new PropertyChangedCallback(OnCommandInvalidated)));

  public static ICommandContext GetContext(UIElement element)
  {
    return (ICommandContext)element.GetValue(ContextProperty);
  }

  public static void SetContext(UIElement element, ICommandContext commandContext)
  {
    element.SetValue(ContextProperty, commandContext);
  }

 /// <summary>
 /// Callback when the Command property is set or changed.
 /// </summary>
  private static void OnCommandInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
  {
    // Clear the exisiting bindings on the element we are attached to.
    UIElement element = (UIElement)dependencyObject;
    element.CommandBindings.Clear();

    // If we're given a command model, set up a binding
    ICommandContext commandContext = e.NewValue as ICommandContext;
    if (commandContext != null)
    {
      foreach (CommandModel commandModel in commandContext.Commands)
      {
        element.CommandBindings.Add(
          new CommandBinding(commandModel.Command, commandModel.OnExecute, commandModel.OnQueryEnabled));
      }
    }

    // Suggest to WPF to refresh commands
    CommandManager.InvalidateRequerySuggested();
   }

 }

Databinding to the Commands

One of the advantatges of this approach is that the commands are just another property of the viewmodel and they are ready for databinding.

For example, lets suppose that you are a designer who want to know exactly which commands does the viewmodel implements, the only thing you need is to put this code in the view


<ListBox ItemsSource="{Binding Commands}">

But you can go further and use the command list to directly show the commands in the view:


<ToolBar ItemsSource="{Binding Commands}" >
    <ToolBar.ItemTemplate>
        <DataTemplate>
            <Button Command="{Binding Command}" Content="{Binding Name}"/>
        </DataTemplate>
    </ToolBar.ItemTemplate>
</ToolBar>

This opens some new possibilities, for example:

  • the viewmodel could define a filtered version of the command list “local menu commands” for direct use in the view
  • define templates directly for each command types. For example an template for the close command. This may require some changes in the way the commands are defined.

Update: You can find the source code here.


Actions

Information

4 responses

5 08 2008
Alex Simkin

How do I bind control to commands? Using index in Commands collection?

11 08 2008
Martin

Hi Daniel. Thanks for the excellent article. I quite like it, because we actually share the same idea, but you move more forward than I do.

I was thinking about the implementation of the idea. Yet, since you have already done a very nice job, I would like to learn from you.

I was wondering if you could kindly post a concrete example, which can fully demonstrate the approach. I want to see how it really works out.

Keep it up, man! Thanks!

12 08 2008
danicalbet

Hi,
For Alex:

How do I bind control to commands? Using index in Commands collection?

Following Dan Crevier I use an object wrapper for each command. Each command “object” has associated a “normal/wpf” command. That is assigned in the object construction and is accessed in the command’s object “Command” property.
The key code here is the definition of the bindings from the commands properties:

foreach (CommandModel commandModel in commandContext.Commands)
{
element.CommandBindings.Add(
new CommandBinding(commandModel.Command,
commandModel.OnExecute, commandModel.OnQueryEnabled));
}

In the xaml code in the itemtemplate, when the datacontex is the command object, we use {Binding Command}, the wpf command of the object, instead of {Binding} which refer to the command “object”.

For that you can use the wpf/command directly to define the binding: for example if you have Delete command and in the construction you associate it to the ApplicationCommands.Delete you can bind the control to it in the usual way

<Button Command=”{Binding ApplicationCommands.Delete}”…

I hope this answer the question.

For Martin,
Thanks for the kind words! I certainly have an example. I’m on holidays now but I will upload it when I come back, about the 25 of August.

2 07 2009
MASH

Hi Daniel, this is article I’m looking for.

I’m interested in how your CommandModel class looks like, because
your commands use a different constructor compared to Dan Crevier’s CommandModel.

Thanks a lot!

Leave a comment