[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’sCanExecute
condition will be re-evaluated when theSearchText
property changes). This works, because theSearchText
property is passed to theString.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 theCheckIfEnabled
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!