Back to Devexpress

Delegate Commands

wpf-17353-mvvm-framework-commands-delegate-commands.md

latest10.9 KB
Original Source

Delegate Commands

  • Jan 08, 2024
  • 6 minutes to read

Delegate commands are an implementation of the System.Windows.Input.ICommand interface, so they can be used to create commands in a ViewModel. A delegate command calls methods (delegates) that you assigned to the command when the command’s Execute and CanExecute logic is invoked.

The following delegate commands are available:

  • DelegateCommand<T> - Specifies a command whose Execute and CanExecute delegates accept a single parameter of type T.
  • DelegateCommand - Specifies a command whose Execute and CanExecute delegates do not have any parameters.

Creating DelegateCommands

Both commands support two constructors: a constructor that accepts an Execute delegate, and a constructor that accepts Execute and CanExecute delegates.

csharp
public class DelegateCommandsViewModel : ViewModelBase {
    public DelegateCommand DelegateCommand1 { get; private set; }
    public DelegateCommand<string> DelegateCommand2 { get; private set; }

    public DelegateCommandsViewModel() {
        DelegateCommand1 = new DelegateCommand(
            () => MessageBoxService.Show("This is a DelegateCommand"));

        DelegateCommand2 = new DelegateCommand<string>(
            x => MessageBoxService.Show(x), 
            x => !string.IsNullOrEmpty(x));
    }
}
vb
Public Class DelegateCommandsViewModel
    Inherits ViewModelBase

    Public Property DelegateCommand1 As DelegateCommand
    Public Property DelegateCommand2 As DelegateCommand(Of String)

    Public Sub New()
        DelegateCommand1 = New DelegateCommand(Function() MessageBoxService.Show("This is a DelegateCommand"))
        DelegateCommand2 = New DelegateCommand(Of String)(Function(x) MessageBoxService.Show(x), Function(x) Not String.IsNullOrEmpty(x))
    End Sub
End Class

CommandParameter Conversion

A DelegateCommand<T> automatically converts the command argument to the parameterized type if possible. In the example below, the CommandParameter (the “Text” string) will be automatically converted to the DocumentType parametrized type.

xaml
<Button Command="{Binding ShowDocumentCommand}" CommandParameter="Text"/>
csharp
public enum DocumentType { Text, Data }

public class DelegateCommandsViewModel : ViewModelBase {
    public DelegateCommand<DocumentType> ShowDocumentCommand { get; private set; }

    public DelegateCommandsViewModel() {
        ShowDocumentCommand = new DelegateCommand<DocumentType>(ShowDocument);
    }
    void ShowDocument(DocumentType parameter) {
        if(parameter == DocumentType.Text) {
            //... 
        }
        if(parameter == DocumentType.Data) {
            //... 
        }
    }
}
vb
Public Enum DocumentType
    Text
    Data
End Enum

Public Class DelegateCommandsViewModel
    Inherits ViewModelBase

    Public Property ShowDocumentCommand As DelegateCommand(Of DocumentType)

    Public Sub New()
        ShowDocumentCommand = New DelegateCommand(Of DocumentType)(AddressOf ShowDocument)
    End Sub

    Private Sub ShowDocument(ByVal parameter As DocumentType)
        If parameter = DocumentType.Text Then
        End If

        If parameter = DocumentType.Data Then
        End If
    End Sub
End Class

Updating Commands

The DelegateCommand constructors have an optional useCommandManager parameter. This parameter specifies whether a DelegateCommand uses the CommandManager to raise the CanExecuteChanged event. The default setting for useCommandManager is true , which makes it unnecessary to manually implement the disable/enable logic for your commands. Once you set the CanExecute delegate for the command, the delegate is automatically triggered when a user interacts with the UI. When the CommandManager is used for this purpose, the CanExecute handler is called frequently to avoid performing time-consuming operations in the delegate.

If you pass false as the last constructor argument, the CommandManager is not used. In this case, call the RaiseCanExecuteChanged method to update your command.

csharp
public class DelegateCommandsViewModel : ViewModelBase {
    public DelegateCommand GoBackCommand{ get; private set; }
    public DelegateCommand GoForwardCommand { get; private set; }

    public DelegateCommandsViewModel() {
        GoBackCommand = new DelegateCommand(GoBack, CanGoBack, false);
        GoForwardCommand = new DelegateCommand(GoForward, CanGoForward, false);
    }

    void GoBack() {
        //... 
        UpdateCommandsState();
    }
    void GoForward() {
        //... 
        UpdateCommandsState();
    }
    bool CanGoBack() {
        //... 
    }
    bool CanGoForward() {
        //... 
    }
    void UpdateCommandsState() {
        GoBackCommand.RaiseCanExecuteChanged();
        GoForwardCommand.RaiseCanExecuteChanged();
    }
}
vb
Public Class DelegateCommandsViewModel
    Inherits ViewModelBase

    Public Property GoBackCommand As DelegateCommand
    Public Property GoForwardCommand As DelegateCommand

    Public Sub New()
        GoBackCommand = New DelegateCommand(AddressOf GoBack, AddressOf CanGoBack, False)
        GoForwardCommand = New DelegateCommand(AddressOf GoForward, AddressOf CanGoForward, False)
    End Sub

    Private Sub GoBack()
        UpdateCommandsState()
    End Sub

    Private Sub GoForward()
        UpdateCommandsState()
    End Sub

    Private Function CanGoBack() As Boolean
    End Function

    Private Function CanGoForward() As Boolean
    End Function

    Private Sub UpdateCommandsState()
        GoBackCommand.RaiseCanExecuteChanged()
        GoForwardCommand.RaiseCanExecuteChanged()
    End Sub
End Class

DelegateCommands in POCO

In a POCO View Model, DelegateCommands can be automatically generated based on public methods (with one parameter or none) of your View Model.

For instance, for the following Save and CanSave methods…

csharp
[POCOViewModel]
public class ViewModel {
    public void Save(string fileName) {
        //... 
    }
    public bool CanSave(string fileName) {
        return !string.IsNullOrEmpty(fileName);
    }
}
vb
<POCOViewModel>
Public Class ViewModel
    Public Sub Save(fileName As String)
        '...
    End Sub
    Public Function CanSave(fileName As String) As Boolean
        Return Not String.IsNullOrEmpty(fileName)
    End Function
End Class

…the following SaveCommand property is automatically generated.

csharp
DelegateCommand<string> saveCommand;
public DelegateCommand<string> SaveCommand {
    get {
        return saveCommand ?? 
            (saveCommand = new DelegateCommand<string>(Save, CanSave));
    }
}
vb
Dim _SaveCommand As DelegateCommand(Of String)
Public ReadOnly Property SaveCommand
    Get
        If (_SaveCommand Is Nothing) Then
            _SaveCommand = New DelegateCommand(Of String)(AddressOf Save, AddressOf CanSave)
        End If
        Return _SaveCommand
    End Get
End Property

To update an automatically generated command in a POCO View Model, use the RaiseCanExecuteChanged extension method available from the DevExpress.Mvvm.POCO.POCOViewModelExtensions class.

csharp
[POCOViewModel]
public class ViewModel {
    public void GoBack(){
        //...
    }
    public bool CanGoBack(){
        //...
    }
    public void UpdateSaveCommand(){
        this.RaiseCanExecuteChanged(c => c.GoBack());
    }
}
vb
<POCOViewModel>
Public Class ViewModel
    Public Sub GoBack()
        '...
    End Sub
    Public Function CanGoBack() As Boolean
        '...
    End Function
    Public Sub UpdateSaveCommand()
        Me.RaiseCanExecuteChanged(Sub(c) c.GoBack())
    End Sub
End Class

Delegate Commands in View Models Generated at Compile Time

To add a DelegateCommand in the View Models Generated at Compile Time, create a void method with the GenerateCommand attribute in your base View Model class.

csharp
[GenerateViewModel]
public partial class ViewModel {
    [GenerateCommand]
    void Login(string parameter) {
        //...
    }
    bool CanLogin(string parameter) {
        //...
    }
}
csharp
partial class ViewModel : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);

    DelegateCommand<string> loginCommand;
    public DelegateCommand<string> LoginCommand {
        get => loginCommand ??= new DelegateCommand<string>(Login, CanLogin, true);
    }
}

Commands created in compile time-generated View Models have the default true value in the UseCommandManager attribute property.

If you set the UseCommandManager attribute property to false , you should add the RaiseCanExecuteChanged method to the base View Model.

csharp
[GenerateViewModel]
public partial class ViewModel {
    [GenerateCommand(UseCommandManager = false)]
    void Login(string parameter) {
        //...
    }
    bool CanLogin(string parameter) {
        //...
    }

    public void UpdateLoginCommand() => loginCommand.RaiseCanExecuteChanged();
}

Use the Command Attribute to Create Commands

You can use the CommandAttribute to create commands in POCO View Models and ViewModelBase descendants.

csharp
public class ViewModel : ViewModelBase {
    [Command]
    public void Save() {
        //...
    }
    public bool CanSave() {
        //...
    }
}
vb
Public Class ViewModel
    Inherits ViewModelBase
    <Command>
    Public Sub Save()
        '...
    End Sub
    Public Function CanSave() As Boolean
        '...
    End Function
End Class

Refer to the following help topic for more information: Create Commands Without Command Properties.