In GUI applications built using the MVVM pattern, it is common for the view model class to define commands that the user can perform in the associated XAML view. These commands are implemented as nested classes of the view model classes, and require a lot of boilerplate code.
The CommandAttribute aspect allows you to implement commands in your view model classes without adding nested classes, which greatly reduces boilerplate code. The aspect also integrates with the NotifyPropertyChangedAttribute aspect, which makes it much easier to implement the CanExecute logic.
Creating a simple command
Adding a command named LoadContactsList to your view model class
Implement the command logic in the
ExecuteLoadContactsListmethod in your view model class. This method is the equivalent of the Execute(object) method.Optionally, the
Executemethod can have one parameter of any type, which becomes the command parameter.Add a
LoadContactsListCommandproperty to your view model class and mark it with the CommandAttribute attribute. The property must be of type ICommand and have both a getter and a setter.
Example
The following code snippet, from a view model class annotated with NotifyPropertyChangedAttribute, defines a command named LoadContactsList.
[Command]
public ICommand LoadContactsListCommand { get; private set; }
public void ExecuteLoadContactsList()
{
this.AllContacts = new ObservableCollection<Contact>
{
new Contact { Id = 1, FullName = "Contact1", Email = "Email1" },
new Contact { Id = 2, FullName = "Contact2", Email = "Email2" },
new Contact { Id = 3, FullName = "Contact3", Email = "Email3" }
};
}
After adding a new command to your view model class, you can bind it to a UI control in your XAML code.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Contacts" Height="300" Width="400">
<StackPanel>
<Button x:Name="LoadButton" Content="Load contacts" Command="{Binding LoadContactsListCommand}" />
<Button x:Name="CreateButton" Content="Create contact" Command="{Binding CreateContactCommand}" />
<Button x:Name="DeleteButton" Content="Delete contact" Command="{Binding DeleteContactCommand}"
CommandParameter="{Binding SelectedValue, ElementName=ContactsDataGrid}" />
<DataGrid x:Name="ContactsDataGrid" ItemsSource="{Binding AllContacts}" SelectedValuePath="Id"
AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="Id" />
<DataGridTextColumn Binding="{Binding FullName}" Header="Name" />
<DataGridTextColumn Binding="{Binding Email}" Header="Email" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
The sample program binds this view to the view model and executes the commands. It prints the following output:
The Load button is bound to the command: True
Can create before loading: False
CanExecuteChanged raised for CreateContact.
Loaded 3 contacts.
Can create after loading: True
Created contact 4.
Can delete contact 2: True
Can delete contact 2 after deleting it: False
Can update contact 1: True
Contact 1: Joe Johnson
Determining whether your command can be executed
The UI controls in the GUI application can be enabled or disabled based on what actions are available to the user in the current state of the application. This feature is usually implemented by the CanExecute(object) method.
The CommandAttribute aspect provides a clean way to determine the availability of the actions. For each command in your view model class, you can implement a CanExecute property or method that validates whether this command can be currently executed.
Adding a CanExecute property
To allow the view to determine the availability of the CreateContact command, add a public bool property named CanExecuteCreateContact to the same class.
[Command]
public ICommand CreateContactCommand { get; private set; }
public bool CanExecuteCreateContact
{
get
{
// Contacts can be created only after the list is loaded.
return this.AllContacts != null;
}
}
public void ExecuteCreateContact()
{
this.AllContacts!.Add( new Contact { Id = this.AllContacts.Count + 1, FullName = "New contact" } );
}
The CanExecute properties associated with the commands support the NotifyPropertyChangedAttribute aspect. Therefore, if you add the NotifyPropertyChangedAttribute aspect to the view model class, the UI will be notified of changes in the CanExecute properties.
Adding a CanExecute method
A limitation of CanExecute properties is that they cannot depend on the input argument of the command. For example, with the DeleteContact command, you may want to validate whether the current user has the permission to delete the currently selected contact. If your availability logic depends on the input argument, you must use a CanExecute method instead of a property.
To allow the UI to determine the availability of the DeleteContact command, add a CanExecuteDeleteContact method to the view model class. The method must have one parameter and return a bool value. The parameter type must be assignable from the type of the command's input argument.
[Command]
public ICommand DeleteContactCommand { get; private set; }
public bool CanExecuteDeleteContact( int contactId )
{
return this.AllContacts != null && this.AllContacts.Any( c => c.Id == contactId );
}
public void ExecuteDeleteContact( int contactId )
{
this.AllContacts!.Remove( this.AllContacts.Single( c => c.Id == contactId ) );
}
Overriding naming conventions
The CommandAttribute aspect follows a predefined naming convention when looking for methods and properties associated with the command in your view model class. You can override the naming convention and choose your own member names by setting properties on the CommandAttribute. The following table shows the default naming convention and the properties used to override member names.
Command pattern's naming conventions
| Member kind | Default name | Example | Override property |
|---|---|---|---|
| Command property | CommandName -or- CommandName Command | UpdateContact -or- UpdateContactCommand |
N/A |
| Execute method | Execute CommandName -or- CommandName | ExecuteUpdateContact -or- UpdateContact |
ExecuteMethod |
| CanExecute method | CanExecute CommandName -or- Can CommandName | CanExecuteUpdateContact -or- CanUpdateContact |
CanExecuteMethod |
| CanExecute property | CanExecute CommandName -or- Can CommandName | CanExecuteUpdateContact -or- CanUpdateContact |
CanExecuteProperty |
The following example shows a command that does not follow the naming conventions.
[Command( ExecuteMethod = nameof(UpdateContact), CanExecuteMethod = nameof(CanUpdate) )]
public ICommand UpdateContactCommand { get; private set; }
public bool CanUpdate( Contact newData )
{
return this.AllContacts != null && this.AllContacts.Any( c => c.Id == newData.Id );
}
public void UpdateContact( Contact newData )
{
var contact = this.AllContacts!.Single( c => c.Id == newData.Id );
contact.FullName = newData.FullName;
contact.Email = newData.Email;
}
See Also
Reference
Other Resources