Поделиться через


Commands

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Commands are a way to handle user interface (UI) actions. They are a loosely coupled way to bind the UI to the logic that performs the action.

When building composite applications, presentation design patterns such as Model-View-Presenter (MVP) and Model-View-Controller (MVC) are often used to separate the UI logic from the UI layout and presentation. When implementing these patterns with Windows Presentation Foundation (WPF), the presenter or controller handles commands but lives outside the logical tree. WPF-routed commands deliver command messages through UI elements in the tree, but the elements outside the tree will not receive these messages because they only bubble up or down from the focused element or an explicitly stated target element. Additionally, the WPF-routed commands require a command handler in the code behind.

The Composite Application Library introduces two new commands that can be routed outside the boundaries of the logical tree and that do not require handling logic in the code behind. The commands are custom implementations of the ICommand interface defined by WPF, and they implement their own routing mechanism to get the command messages delivered to objects outside of the logical tree. The commands in the Composite Application Library include DelegateCommand and CompositeCommand.

DelegateCommand<T>

The DelegateCommand allows delegating the commanding logic instead of requiring a handler in the code behind. It uses a delegate as the method of invoking a target handling method.

In the Stock Trader Reference Implementation (Stock Trader RI), the Buy and Sell shortcut menu items that appear when the user right-clicks a specific symbol on the positions screen is a delegate command. These commands are handled by the OrdersController. DelegateCommands can also be handled by a either a ViewModel or a presenter, depending on the scenario.

The DelegateCommand uses its delegate to invoke a CanExecute method or Execute method on the target object when the command is invoked. Because the class is generic, it enforces compile-time checking on the command parameters, which traditional WPF commands do not. Additionally, because it accepts delegates, it removes the need for creating a new command type for every instance where you need commanding.

DelegateCommand accepts two constructor parameters, executeMethod* *and canExecuteMethod. Because the parameter types are generic delegates, the handlers can be easily hooked into the underlying controllers or presenters. The following code shows how the DelegateCommand holds onto the provided delegates that it uses to invoke the target methods.

public class DelegateCommand<T> : ICommand
{
    public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
    {
        …
        this.executeMethod = executeMethod;
        this.canExecuteMethod = canExecuteMethod;
        …
    }
…
}

IActiveAware Interface

In some instances, you may want a delegate command to execute only if it is in a view that is currently active (selected). For example, the Submit command (which the Submit button bind to) in each Buy/Sell order is active only when that order is selected.

To support this behavior, the DelegateCommand implements IActiveAware. This interface has an IsActive property, which can be set whenever the command becomes active. Whenever the property changes, the DelegateCommand raises the IsActiveChanged event.

The following code shows the implementation of the IActiveAware interface on the DelegateCommand.

public event EventHandler IsActiveChanged;

public bool IsActive
{
    get { return _isActive; }
    set
    {
        if (_isActive != value)
        {
            _isActive = value;
            OnIsActiveChanged();
        }
    }
}

protected virtual void OnIsActiveChanged()
{
    EventHandler isActiveChangedHandler = IsActiveChanged;
    if (isActiveChangedHandler != null) isActiveChangedHandler(this, EventArgs.Empty);
}

CompositeCommand

The CompositeCommand is a command that has multiple child commands. The CompositeCommand is used in the Stock Trader RI in two cases: first, for the Buy/Sell Submit All button on the main toolbar and second, for the Submit and Cancel buttons on that toolbar. When you click the Submit All button, all the child Submit commands in the Buy/Sell screens are executed. When you click the Submit or Cancel button, only the active Buy/Sell order's command is executed.

When you call the Execute or the CanExecute method on the composite command, it calls the respective method on the child commands. The following code shows how the Execute and CanExecute methods are implemented in the CompositeCommand class. The ShouldExecute method is described in the next section.

public virtual bool CanExecute(object parameter)
{
    bool hasEnabledCommandsThatShouldBeExecuted = false;

    foreach (ICommand command in registeredCommands)
    {
        if (ShouldExecute(command))
        {
            if (!command.CanExecute(parameter))
            {
                return false;
            }

            hasEnabledCommandsThatShouldBeExecuted = true;
        }
    }
    return hasEnabledCommandsThatShouldBeExecuted;
}

public virtual void Execute(object parameter)
{
    Queue<ICommand> commands = new Queue<ICommand>(registeredCommands);

    while (commands.Count > 0)
    {
        ICommand command = commands.Dequeue();
        if (ShouldExecute(command))
            command.Execute(parameter);
    }
}

Figure 1 illustrates a diagram of the connection between DelegateCommands and CompositeCommands.

Ff921126.3693ef42-984e-49b7-86f9-109ffe0500cf(en-us,PandP.10).png

Figure 1
Connection between CompositeCommand and DelegateCommand

In some instances, you may want a composite command to execute only if it is in a view that is currently active (selected). To support this behavior, the DelegateCommand implements IActiveAware. This interface has an IsActive property, which can be set whenever the command becomes active. Whenever the property changes, the DelegateCommand throws the IsActiveChanged event.

In the following code, you can see the implementation of the IActiveAware interface on the DelegateCommand.

Activity Monitoring Behavior

In some cases, you may want the composite command to only execute the active command. For example, in the Stock Trader RI, the Submit composite command, which is invoked by the Submit button on the toolbar, submits only the current order. That is, it executes only the current order's Submit command.

Figure 2 illustrates the **Submit **button on the main toolbar in the Stock Trader RI.

Ff921126.e275345a-1769-4a3d-b9b4-a84ed41fb6d7(en-us,PandP.10).png

Figure 2
Stock Trader RI main toolbar

To support the submit functionality, the CompositeCommand has an activity monitoring behavior. This behavior is enabled in the CompositeCommand's constructor by setting the monitorCommandActivity to true. When this behavior is enabled, the CompositeCommand performs an additional check on commands that implement IActiveAware before it executes them. If that command's IsActive property is set to true and the command's CanExecute method returns true, it executes. The ShouldExecute method handles this logic, as shown here.

protected virtual bool ShouldExecute(ICommand command)
{
    var activeAwareCommand = command as IActiveAware;

    if (monitorCommandActivity && activeAwareCommand != null)
    {
        return (activeAwareCommand.IsActive);
    }

    return true;
}

Registering and Unregistering Composite Commands

Composite commands are either registered or unregistered through the **RegisterCommand **and UnregisterCommand. In the Stock Trader RI, the OrdersController is responsible for registering and unregistering the child order commands. The following code shows where the submit and cancel commands are registered in the StartOrder method.

virtual protected void StartOrder(…)
{
…
     commandProxy.SubmitOrderCommand.RegisterCommand (orderCompositePresentationModel.SubmitCommand);
     commandProxy.CancelOrderCommand.RegisterCommand
(orderCompositePresentationModel.CancelCommand);
…
}

Frequently Asked Questions About Commands

Why are WPF commands not used?

WPF commands have a number of limitations. They are coupled to elements in the logical tree because they use routed events under the covers to deliver the command messages. This means you cannot directly hook up a separate class, such as a presentation model, presenter, or controller, to be the direct command handler. The view would have to be the routed command handler, and it would have to forward the call to the presenter or controller through a method call or event. Additionally, the command handler that the routed event is delivered to is determined by the current focus in the UI. This works fine if the command handler is at the window level, because the window is always in the focus tree of the currently focused element, so it gets called for command messages. However, it does not work for child views who have their own command handlers unless they have the focus at the time. Finally, only one command handler is ever consulted with routed commands. After one command handler is invoked (for CanExecute or Execute), no other handlers are consulted, even if they are in the focus tree of the focused element. For scenarios where multiple views (or their presenters) need to handle the command, there is no decoupled way to address it with routed commands.

Can DelegateCommands be replaced with Routed Commands?

No, because both are meant for two different purposes. Routed commands, such as Cut, Copy, and Paste, are meant for controls with command binding that live within the logical tree and that will have the focus for the intent of the command. They can also be used for general purposes if it is acceptable to put centralized command handling at the root window or page element and have it as part of the view. However, that approach does not scale for composite applications, so the DelegateCommand approach allows you to have the flexibility of multiple command handlers that live outside the logical tree.

Can the order of execution of commands be set up inside the Composite commands?

Currently, you cannot specify the order that commands are executed within Composite commands. Moreover, this is not required from a high level, because command handlers should be decoupled from one another and not rely on a specific invocation order. The workaround for this is the judicious usage of DelegateCommands and the implementation logic. You just need to know how the composite command works. Because the composite command orders the command list internally, execution mode will be First In, First Out (FIFO).

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.