[MVVMbasics] New version 2.0 features #3: Intelligent Command Binding

[notice]All blog posts with a title that starts with [MVVMbasics] are related to the MVVMbasics framework. If you are only interested in my regular blog posts and not in MVVMbasics, ignore these. To list all articles covering MVVMbasics, go to http://mvvmbasics.mobilemotion.eu/documentation![/notice]

Intelligent Command Binding

Commands must be defined as public properties of the Viewmodel or Model they are exeuted in, and they must be instantiated which is usually done in the Viewmodel’s or Model’s constructor. This isn’t new, but what’s new is the way how they are instantiated, since there exist a few new features and shortcuts.

Command instantiation:

Let’s assume SearchCommand is a custom command defined in some Viewmodel. It is defined as a Viewmodel’s property using the BaseCommand helper class:

public BaseCommand SearchCommand { get; set; }

In the Viewmodel’s constructor, this command is instantiated and paired with either a private method or a delegate. Depending on whether the command expects to retrieve a CommandParameter from the View or not, one of the following two constructors of BaseCommand can be used:

private void Search()
{
    // ... business logic goes here
}

public MyViewmodel()
{
    SearchCommand = new BaseCommand(Search);
}
private void Search(object parameter)
{
    // ... do something with the parameter
}

public MyViewmodel()
{
    SearchCommand = new BaseCommand(Search);
}

(Note that we could have also used an inline lambda expression instead of the Search method.)

Still nothing new so far, but now it comes: As alternative to calling the BaseCommand‘s constructor in order to instantiate the command, we can call the CreateCommand method (which is new in version 2.0, and is defined within the BaseModel class, meaning it is available in all Models and Viewmodels inheriting from the framework’s generic BaseModel and BaseViewmodel classes):

SearchCommand = CreateCommand(Search);

This has no advantage when dealing with such a simple command, but it has as soon as a CanExecute condition shall be specified for the command, which will be shown in the next section. I just wanted to show that both ways are valid. (By the way: Internally, the CreateCommand method just calls the BaseCommand constructor and returns its result, nothing fancy here.)

Specifying a CanExecute condition:

This is the interesting part. As we know, each command can contain a condition that is evaluated at runtime to decide whether the command may be executed or not. If this condition resolves to false, the visual control the command is binding to is disabled. I generally refer to this condition as CanExecute condition.

In most cases, this condition depends on one or multiple of the Viewmodel’s bindable properties. For example, the CanExecute condition of our SearchCommand could look like:

IsUserLoggedIn && !String.IsNullOrEmpty(SearchText)

In this example, the command may only be invoked if a boolean property indicating whether the current user is logged in is true, and if a property of type string that represents the entered search criteria contains some text.

In previous versions of the MVVMbasics framework, it was necessary to specify for each property all the commands that depend on it (technically, this was done by listing all commands that depend on a particular property as part of the property’s Set method). As stated in the previous blog post, this is not necessary any more. We simply specify the CanExecute condition as additional parameter while instantiating the command, and the MVVMbasics framework takes care of the rest:

SearchCommand = CreateCommand(Search,
    () => IsUserLoggedIn && !String.IsNullOrEmpty(SearchText));

The MVVMbasics framework will analyze the expression that represents the CanExecute condition, store each bindable property it finds and force the condition to be re-evaluated each time one of these properties is changed. Analyzing the expression works fine in practice and should find all properties involved automatically, except for two special cases:

  • In case a method call is specified somewhere within the expression, the framework will only take into account this method’s parameters (if bindable properties are among them), but not analyze the body of this method! For example, if the condition contains an expression like

    () => String.IsNullOrEmpty(SearchText)
    

    the SearchText property will be registered as property the command depends on (and the command’s CanExecute condition will be re-evaluated when the SearchText property changes). This works, because the SearchText property is passed to the String.IsNullOrEmpty method as parameter.

    On the other hand, if the command is instantiated with a condition structured as follows:

    SearchCommand = CreateCommand(Search,
    	() => CheckIfEnabled());
    
    private bool CheckIfEnabled()
    {
    	return String.IsNullOrEmpty(SearchText);
    }
    

    then no properties will be recognized as dependent properties, because the SearchText property is only referenced within the CheckIfEnabled method’s body, which is not parsed and analyzed in detail. In this case, you need to manually specify the properties the command depends on. See the next section on how to do this!

  • In some cases, you want to omit parsing the CanExecute expression, e.g. for performance reason when using rather complex conditions. To do so, you can also manually specify all properties the command depends on – see the next section on how to do this. If properties are specified manually, the expression will never be automatically analyzed!

Manually specifying properties the command’s CanExecute condition depends on:

If the properties a command’s CanExecute condition depends on shall be specified manually (instead of letting the condition be analyzed automatically), they can be passed to the CreateCommand method as additional parameters. The method allows an arbitrary number of optional parameters that all represent these dependent properties, as shown in the following example:

SearchCommand = CreateCommand(Search,
    () => IsUserLoggedIn && !String.IsNullOrEmpty(SearchText),
	() => IsUserLoggedIn, () => SearchText);

In this case, the CanExecute condition is not parsed by the BaseCommand‘s initializer, and the properties IsUserLoggedIn and SearchText are registered as properties the command depends on (and the command’s CanExecute condition will be re-evaluated when one of those two properties changes).

It should be noted, however, that the two approaches of specifying dependent properties (automatical parsing of the CanExecute expression vs. manually specifying them) can not be combined. If at least one property is passed to the CreateCommand method as third parameter, the expression will not be analyzed.

Alternatives to the CreateCommand method:

For the sake of completeness, I’d like to add that it’s still possible to invoke the BaseCommand‘s constructor directly, instead of using the CreateCommand method:

SearchCommand = new BaseCommand(Search,
    () => IsUserLoggedIn && !String.IsNullOrEmpty(SearchText),
	this);
SearchCommand = new BaseCommand(Search,
    () => IsUserLoggedIn && !String.IsNullOrEmpty(SearchText),
	this,
	() => IsUserLoggedIn, () => SearchText);

These two examples show the only drawback of this approach: A reference to the command’s host (the Model or Viewmodel it resides in) must be passed to the constructor as third parameter. When using the CreateCommand method, this can be omitted.

Migrating to MVVMbasics 2.0 from an earlier version:

Compared to earlier versions of the framework, which expected the CanExecute condition to be passed as Func<bool> or Predicate<object> to the BaseCommand‘s constructor, the new 2.0 version of the BaseCommand‘s constructor expects a LINQ Expression (Expression<Func<bool>> or Expression<Func<object, bool>>). This is necessary to allow the BaseCommand class to analyze the given expression and detect dependent properties automatically.

When migrating existing projects to MVVMbasics 2.0, you can keep most of the existing command initializers, because they will be automatically passed as LINQ Expression. The only exception to this rule is a case where a separately defined method is passed as CanExecute condition, as in the following example:

SearchCommand = new BaseCommand(Search, IsEnabled);

private bool IsEnabled()
{
	// ...
}

These need to be rewritten to an Expression by switching to the following syntax:

SearchCommand = new BaseCommand(Search, () => IsEnabled());

private bool IsEnabled()
{
	// ...
}

A note about backwards compatibility:

The “old” versions of the BaseCommand constructor are still available, but are flagged as deprecated and will be removed from the framework in a future version. When creating a new project, never use deprecated functions but only the new ones. When migrating an existing project to MVVMbasics 2.0, you can still use the old constructor calls, but remember to plan an upgrade task to replace these with the new CreateCommand at a later point in time. It’s not that complex, and it guarantees that you’re project is future-proof. (By the way, Visual Studio will not let you forget about that since it shows a compiler warning for each deprecated function that is still in use…)

The next blog post will present the new, shorter way of passing parameters between Views, and two new view states that can be useful when handling navigation events – read all about it in New version 2.0 features part #4!