Mike Taulty's Blog
Bits and Bytes from Microsoft UK
XAML: ViewModels, ICommands in Dictionaries

Blogs

Mike Taulty's Blog

Elsewhere

Archives

If I’m starting from scratch with writing some kind of view model classes then I often find myself implementing INotifyPropertyChanged in some class like;

  public abstract class ViewModelBase :
    INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value,
      [CallerMemberName] String propertyName = null)
    {
      if (object.Equals(storage, value)) return false;

      storage = value;
      this.OnPropertyChanged(propertyName);
      return true;
    }
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
      var eventHandler = this.PropertyChanged;
      if (eventHandler != null)
      {
        eventHandler(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }

but then I often want commands and I find defining commands to be tedious. Let’s say I’ve got some implementaton of ICommand like this;

class SimpleCommand : ICommand
    {
      public event EventHandler CanExecuteChanged;

      public SimpleCommand(Action action)
      {
        this._action = action;
      }
      public bool CanExecute(object parameter)
      {
        return (true);
      }
      public void Execute(object parameter)
      {
        if (this._action != null)
        {
          this._action();
        }
      }
      Action _action;
    }

then I often want a base class that represents a source of a number of commands – e.g. here where I’ve encapsulated the SimpleCommand inside of this CommandableViewModelBase class;

  public abstract class CommandableViewModelBase : ViewModelBase
  {
    class SimpleCommand : ICommand
    {
      public event EventHandler CanExecuteChanged;

      public SimpleCommand(Action action)
      {
        this._action = action;
      }
      public bool CanExecute(object parameter)
      {
        return (true);
      }
      public void Execute(object parameter)
      {
        if (this._action != null)
        {
          this._action();
        }
      }
      Action _action;
    }
    public CommandableViewModelBase()
    {
      this.commands = new Dictionary<string, ICommand>();
    }
    protected void AddCommand(string name, Action handler)
    {
      this.commands.Add(name, new SimpleCommand(handler));
    }
    public IReadOnlyDictionary<String, ICommand> Commands
    {
      get
      {
        return (this.commands);
      }
    }
    Dictionary<string, ICommand> commands;
  }

and then I can write a view model class derived from this;

  public class ViewModel : CommandableViewModelBase
  {
    public ViewModel()
    {
      base.AddCommand("invoke", OnInvoke);
    }
    void OnInvoke()
    {
    }
  }

and bind it up;

<Button Content="Hello"
            Command="{Binding Path=Commands[invoke]}" />

it’s just something that I find myself doing from time to time so thought I’d share. That kind of binding is a little “opaque” in terms of being able to build up those expressions graphically by pointing and clicking inside of Visual Studio or Blend so sometimes I’ll also add a specific property InvokeCommand which reaches into the dictionary in the parent class just to make it easier to use the tooling. It could be taken a lot further – e.g. those commands aren’t observable but that’d be easy enough to fix.


Posted Fri, Jun 6 2014 3:36 PM by mtaulty
Filed under: , , ,

Comments

Baski wrote re: XAML: ViewModels, ICommands in Dictionaries
on Fri, Jun 6 2014 3:43 PM

Mike , Perfect timing , I was thinking about the same issue today. As I was writing my RelayCommand , your tweet showed up.. :) thank you.

Bao wrote re: XAML: ViewModels, ICommands in Dictionaries
on Mon, Jul 21 2014 6:24 AM

There seems to be one draw back of this approach is the ability to have lazy command. I'd like to avoid initialize all commands to put into the dictionary when they are not yet used.

Add a Comment

(optional)  
(optional)
(required)  
Remember Me?