A newer version of this page is available. Switch to the current version.

Commands

  • 12 minutes to read

The common way to perform certain actions on the WinForms platform is to handle the specific event and execute required methods within the event handler. For instance, if you need to show a message box when a button is clicked, handle the ButtonClick event and call the MessageBox.Show method.

However, this pattern breaks the idea of separating Views and ViewModels in MVVM, since your View gets code behind that should be placed within a ViewModel or Presenter. The MVVM pattern suggests a different approach: your actions should be encapsulated into a specific object defined elsewhere. This object is bound to a specific UI element and clicking (checking, modifying, etc.) this UI element triggers the object, which performs the desired functionality. This leaves your View free from the business logic code. Such objects are called Commands.

Since the WinForms platform does not provide commands, using the DevExpress MVVM Framework, which features commands of multiple types and classes, is helpful when developing MVVM applications.

Delegate Commands

NOTE

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Commanding

Example: Delegate Commands

18.2 Demo Center: Launch the demo

Delegate commands are simple commands, designed for synchronously executed actions. In a simple scenario, the command is defined as a DelegateCommand object only with an Execute function.


DelegateCommand command = new DelegateCommand(() =>
{
    MessageBox.Show("Hello! I'm running!");
});

To bind a command, you can use the BindCommand method, available for most DevExpress UI controls (buttons, check boxes, bar items, etc.).


commandButton.BindCommand(command);

The DelegateCommand class contains other constructors. Using them, you can define a command that has:

  • a related CanExecute function that controls this command’s and the related UI element’s availability;

    
    //If this function returns false, the command is disabled
    Func<bool> canExecute = () => (2 + 2 == 4);
    DelegateCommand command = new DelegateCommand(() =>
    {
        MessageBox.Show("Hello! I'm running, because the `canExecute` condition is `True`. Try to change this condition!");
    }, canExecute);
    
  • a parameter, passed to the command and processed by it;

    
    DelegateCommand<object> command = new DelegateCommand<object>((v) =>
    {
        MessageBox.Show(string.Format("Hello! The parameter passed to command is {0}. Try to change this parameter!", v));
    });
    object parameter = 5;
    commandButton.BindCommand(command, () => parameter);
    
  • the parameter and the CanExecute condition.

    
    Func<int, bool> canExecute = (p) => (2 + 2 == p);
    // This command is created parameterized and with 'canExecute' parameter.
    DelegateCommand<int> command = new DelegateCommand<int>((v) =>
    {
        MessageBox.Show(string.Format(
        "Hello! The parameter passed to command is {0}." + Environment.NewLine +
        "And I'm running, because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
        "Try to change this parameter!", v));
    }, canExecute);
    int parameter = 4;
    commandButton.BindCommand(command, () => parameter);
    

If you define DelegateCommands within your ViewModel, they must be used as the return values of specific properties and constructed by specifying delegates to the Execute and CanExecute methods. The command is then exposed to the View using a read-only property that returns a reference to an ICommand (see the code below).


public class QuestionnaireViewModel
{
    public QuestionnaireViewModel()
    {
       this.SubmitCommand = new DelegateCommand<object>(
                                        this.OnSubmit, this.CanSubmit );
    }

    public ICommand SubmitCommand { get; private set; }

    private void OnSubmit(object arg)   {...}
    private bool CanSubmit(object arg)  { return true; }
}

POCO Commands

NOTE

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Commanding

Example: POCO Commands

18.2 Demo Center: Launch the demo

Since the DevExpress MVVM Framework supports POCO classes, your commands can also be defined as void methods with none or one parameter within such classes. The framework is smart enough to recognize such methods and treat them as commands.


public class ViewModelWithParametrizedCommand {
    public void DoSomething(object p) {
        MessageBox.Show(string.Format("Hello! The parameter passed to command is {0}. Try to change this parameter!", p));
    }
}

To bind these commands, use the MvvmContext component’s API.


mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
mvvmContext.BindCommand<ViewModelWithSimpleCommand>(commandButton, x => x.DoSomething());

Features like CanExecute functions and parameters, are also supported by POCO commands.


//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
int parameter = 4;
mvvmContext.BindCommand<ViewModelWithParametrizedConditionalCommand, int>(commandButton, (x, p) => x.DoSomething(p), x => parameter);

//ViewModelWithParametrizedConditionalCommand
public class ViewModelWithParametrizedConditionalCommand {
    // Parameterized POCO-command will be created from this method.
    public void DoSomething(int p) {
        MessageBox.Show(string.Format(
            "Hello! The parameter passed to command is {0}." + Environment.NewLine +
            "And I'm running, because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
            "Try to change this parameter!", p));
    }
    // Parameterized `CanExecute` method for the `DoSomething` command.
    public bool CanDoSomething(int p) {
        return (2 + 2) == p;
    }
}

The code below illustrates a common workflow when implementing POCO commands. The DoSomething command (1) is a POCO command that performs certain actions on collection entities (e.g., selected grid control rows). The currently selected entity is kept as the bindable SelectedEntity (2) property’s value. Whenever this property changes its value, its OnSelectedEntityChanged callback (3) is automatically triggered. This callback fires the standard DevExpress POCO RaiseCanExecuteChanged method (4) for the DoSomething command. When called, this method re-checks the CanDoSomething method’s return value (5) and updates the DoSomething command’s availability accordingly.


//2 - Bindable Property
public virtual MyEntity SelectedEntity{ get; set; }
//3 - Changed callback
protected void OnSelectedEntityChanged(){
    //4 - Checks the CanExecute return value
    this.RaiseCanExecuteChanged(x=>x.DoSomething());
}
//1 - POCO Command
public void DoSomething() {
    // command body
}
//5 - CanExecute function for the POCO Command
public bool CanDoSomething() {
    //CanExecute condition (Amount>500$)
    return (SelectedEntity != null) && (SelectedEntity.Amount > 500);
}

Asynchronous Commands

NOTE

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Commanding

Example: POCO Asynchronous Commands

18.2 Demo Center: Launch the demo

Asynchronous commands are designed to fulfill delayed or continuous tasks. If void methods are automatically transformed into simple commands, you need to utilize a method of the System.Threading.Tasks.Task type to create an asynchronous command.


public class ViewModelWithAsyncCommand {
    // Asynchronous POCO-command will be created from this method. 
    public Task DoSomethingAsynchronously() {
        return Task.Factory.StartNew(() =>
        {
            System.Threading.Thread.Sleep(1000); // do some work here 
        });
    }
}

The binding itself remains unchanged - you still use the BindCommand method.


mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommand);
mvvmContext.BindCommand<ViewModelWithAsyncCommand>(commandButton, x => x.DoSomethingAsynchronously());

Asynchronous commands usually take some time to be performed. Thus, in most scenarios, developers give their end-users the ability to cancel these commands (e.g., cancel downloading a file, unpacking an archive, etc.). To do so, make sure that the methods that perform asynchronous actions include the cancellation check.


public class ViewModelWithAsyncCommandAndCancellation {
    // An asynchronous POCO-command will be created from this method.
    public Task DoSomethingAsynchronously() {
        var dispatcher = this.GetService<IDispatcherService>();
        return Task.Factory.StartNew(() => {
            var asyncCommand = this.GetAsyncCommand(x => x.DoSomethingAsynchronously());
            for(int i = 0; i <= 100; i++) {
                if(asyncCommand.IsCancellationRequested) // cancellation check
                    break;
                System.Threading.Thread.Sleep(25); // do some work here
                UpdateProgressOnUIThread(dispatcher, i);
            }
            UpdateProgressOnUIThread(dispatcher, 0);
        });
    }
    // Property for progress
    public int Progress { get; private set; }
    void UpdateProgressOnUIThread(IDispatcherService dispatcher, int progress) {
        dispatcher.BeginInvoke(() => {
            Progress = progress;
            this.RaisePropertyChanged(x => x.Progress);
        });
    }
}

If you have such a command, you can use the MvvmContext component’s BindCancelCommand method to specify a UI element that will cancel the running asynchronous command.


mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
// Binding the async command
mvvmContext.BindCommand<ViewModelWithAsyncCommandAndCancellation>(commandButton, x => x.DoSomethingAsynchronously());
// Binding the cancellation for the previous command
mvvmContext.BindCancelCommand<ViewModelWithAsyncCommandAndCancellation>(cancelButton, x => x.DoSomethingAsynchronously());

As you can also see, CanExecute methods are not manually defined for the async command, nor its cancellation. The Framework does all the work automatically and locks the UI element that calls the async command till this command is finished (or canceled). The UI element that cancels the async command is also disabled automatically until the target command is started. So all you have to do is define your Task method and bind commands using the MvvmContext API - the DevExpress MVVM Framework will do the rest for you.

Legacy Commands

NOTE

MVVM Best Practices Demo The text below has a related example in the DevExpress ‘MVVM Best Practices’ demo.

Group: API Code Examples

Module: Commanding

Example: Legacy Commands

18.2 Demo Center: Launch the demo

Same as with data binding, you do not need to have all ViewModels built according to POCO concepts to be able to use the DevExpress MVVM Framework. Your commands can be defined within separate custom classes. For these commands, use the BindCommand method of the target UI element, same as you did with delegate commands.


//ViewModel
public class LegacyCommandWithParameter {
    public void Execute(object parameter) {
        MessageBox.Show(string.Format(
            "Hello! I'm a Legacy command and the parameter passed to me is {0}." + Environment.NewLine +
            "I'm running because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
            "Try to change this parameter!", parameter));
    }
    public bool CanExecute(object parameter) {
        return object.Equals(2 + 2, parameter);
    }
}

//View
// This is parameterized legacy-command with both the Execute (object) and the CanExecute (object) methods.
LegacyCommandWithParameter command = new LegacyCommandWithParameter();
int parameter = 4;
// UI binding for button with `queryParameter` function
commandButton.BindCommand(command, () => parameter);