BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Windows Presentation Foundation
.NET 4.0+

WPF Commanding - Creating Custom Commands

The one hundred and sixty-fifth part of the Windows Presentation Foundation Fundamentals tutorial continues the description of commanding. This article explains the process for creating custom commands and binding them to controls.

Passing Command Parameters

To allow greater flexibility, you can add a command parameter that will be passed to the Execute and CanExecute methods of your commands. The parameter can be of any data type, which is why the ICommand interface accepts them using objects. Depending upon the requirements, you can hard code parameter values into your XAML or set them through data bindings or code.

To demonstrate, we'll create a second command that will be called by the Add button. This command will read the first and last name properties from the data context, combine them into a single string and add the full name to the name list. To access all of these properties, we'll pass a reference to the data context to the Execute method's parameter.

To begin, we need to create the AddCommand class. Add a new class file by that name and implement the command, as follows:

public class AddCommand : ICommand
{
    public void Execute(object parameter)
    {
        var nameList = parameter as NameList;
        var newName = string.Format("{0} {1}", nameList.FirstName, nameList.LastName);
        nameList.Names.Add(newName);
        nameList.FirstName = nameList.LastName = "";
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;
}

As before, the command's CanExecute property returns true, so the command will always be available. The Execute method joins the two names using the Format method from the string class and adds the result to the Names collection. Finally, the two name properties are set to empty strings, which will clear the bound text boxes.

Add a new field for the command to the NameList class:

AddCommand _addNameCommand = new AddCommand();

Next, create a property to which controls can be bound:

public AddCommand AddNameCommand
{
    get { return _addNameCommand; }
}

We can now add the binding to the XAML for the button. As before, the binding needs to reference the property from the data context. In addition, we must set the command's parameter to the data context object. We can do this by setting the CommandParameter property, using data binding to return the entire data context.

Update the Add button's XAML to apply the bindings:

<Button Command="{Binding AddNameCommand}" CommandParameter="{Binding .}">Add</Button>

To test the new command, run the program. Enter a first name and a last name using the text boxes, then click the Add button. The text boxes should be cleared and the combined name added to the list. Try adding several names.

NB: This example shows the separation of user interface and logic that is made possible with commands. It would be easy to test the functionality of the command and the NameList class using a unit test framework, as the user interface need not be instantiated to execute the tests.

Controlling Command Availability

The add command works well in most scenarios but gives poor results if one or both of the text boxes is left blank. Ideally, it should not be possible to add a name unless both parts have been provided. We can control this with the CanExecute method. This method should return true if the command is available and false if it cannot be used. It receives the same parameter as Execute, so we can read the two name properties from the data context and respond accordingly.

Update the CanExecute method for AddCommand, as follows. This code checks that both names contain text. If they do, the method returns true to allow the command to be executed. If either is blank or whitespace, the method returns false, disabling the command and the controls to which it is bound.

public bool CanExecute(object parameter)
    var nameList = parameter as NameList;
    return nameList != null
        && !string.IsNullOrWhiteSpace(nameList.FirstName)
        && !string.IsNullOrWhiteSpace(nameList.LastName);
}

If you run the program now, you'll find that it does not work as you might expect. The Add button is disabled on loading and remains that way, no matter what you type into the name boxes. The problem is that WPF does not know that the command's availability has changed because the CanExecuteChanged event has not been raised.

For routed commands this problem does not occur because WPF is aware of the existence of the commands. When certain events happen, such as control properties changing or the keyboard focus moving, the RequerySuggested event of the CommandManager class is raised. The CanExecute method of commands is then checked and controls are enabled and disabled automatically.

You can take advantage of CommandManager.RequerySuggested by linking this event to your command's CanExecuteChanged event. Simply replace the existing event handler line with the following code:

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}

Run the program again and enter a first and last name. As soon as the two text boxes both contain non-whitespace characters, the Add button becomes available. If you clear either text box, the button is disabled automatically.

14 June 2015