Wiring up Commands in MVVM apps is painful, because you’ll often have 4 discrete blocks of code relating to a single command:
- The Command property on the ViewModel
- A Func<bool> or Predicate<T> which is referenced by Command.CanExecute
- An Action or Action<T> which is referenced by Command.Execute
- Instantiation and Initialization code which instantiates the Command and links it to the CanExecute/Execute delegates, and sets up relations to other properties.
While the first 3 blocks can be located contiguously (and thus easier to find/maintain), the Instantiation & Initialization code is often all done in the constructor, or in a class-wide initialization method.
This may be a minor annoyance to most, but it really bugs me – because I like to have all code that’s related to each other located grouped together in the source code. I don’t like having to jump back and forth inside of a many-hundreds-of-lines file – it takes a lot more concentration and effort while developing.
Plus, a the instantiation code is very often simple repetitive plumbing code. Why not try to automate it?
For example, let’s say I have a ViewModel with 2 Commands, Foo and Bar:
// 1. Command Property
public ICommand FooCommand { get; private set; }
// 2. CanExecute
private bool myCanFoo;
public bool CanFoo
{
get { return myCanFoo; }
set
{
myCanFoo = value;
RaisePropertyChanged();
}
}
// 3. Execute
private void Foo ()
{ if (CanFoo) Output = "Foo!"; }
// 1. Command Property
public ICommand BarCommand { get; private set; }
// 2. CanExecute
public bool CanBar (String barParam)
{ return !String.IsNullOrEmpty (barParam); }
// 3. Execute
private void Bar ()
{ if (CanBar) Output = "Bar!"; }
// ... many lines later ...
public MyViewModel ()
{
// 4. Instantiation & Initialization for each individual ICommand
FooCommand = new DelegateCommand (Foo, () => CanFoo);
PropertyChanged +=
(sender, args) =>
if (args.PropertyName == "CanFoo")
((DelegateCommand) FooCommand).InvalidateCanExecuteChanged ();
BarCommand = new DelegateCommand<String> (Bar, CanBar);
}
It’s #4 that bugs me, having the Command instantiation & initialization code located far away from the actual Command logic.
I didn’t know what to do about it, until I read this article by XAML BLOG and became inspired to write a small library.
Introducing MvvmCommandWirer, available on github!
Here’s how Command wiring works with MvvmCommandWirer:
// 1. Command Property
[CommandProperty(commandType: typeof(DelegateCommand))]
public ICommand FooCommand { get; private set; }
// 4. Initialization
[CommandInitializationMethod]
private void InitializeFooCommand(DelegateCommand command)
{
PropertyChanged +=
(sender, args) =>
if (args.PropertyName == "CanFoo")
command.InvalidateCanExecuteChanged ();
}
private bool myCanFoo;
// 2. CanExecute
[CommandCanExecuteMethod]
public bool CanFoo
{
get { return myCanFoo; }
set
{
myCanFoo = value;
RaisePropertyChanged();
}
}
// 3. Execute
[CommandExecuteMethod]
private void Foo ()
{ if (CanFoo) Output = "Foo!"; }
// 1. Command Property
[CommandProperty(commandType: typeof(DelegateCommand<String>), parameterType: typeof(String))]
public ICommand BarCommand { get; private set; }
// 2. CanExecute
[CommandCanExecuteMethod]
public bool CanBar (String barParameter)
{ return !String.IsNullOrEmpty(barParameter); }
// 3. Execute
[CommandExecuteMethod]
private void Bar (String barParameter)
{ if (CanBar (barParameter)) Output = "Bar!"; }
// ... dozens of lines later ...
public MyViewModel ()
{
// 4. Instantiation of ALL ICommands using CommandWirer
CommandWirer.WireAll (this);
}
So now, all the code related to a specific Command can now be located in a single contigiuous block, instead of spread throughout the ViewModel file!
This is still very much a work-in-progress, I have done very little real-world testing with it. So I would greatly appreciate any feedback on this!
For working example code, please check out the github repository.
Pingback: MVVM pain points: Scattered Command code | PhilChuang.com