Export (0) Print
Expand All

Commands

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 propagate events 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 plus or minus icons on the positions view invoke DelegateCommand. 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). 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 for the Buy/Sell Submit All button in the buy/sell view. When you click the Submit All button, all the child Submit commands in the Buy/Sell screens are executed.

When you call the Execute or the CanExecute method on the CompositeCommand, 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;

    ICommand[] commandList;
    lock (this.registeredCommands)
    {
        commandList = this.registeredCommands.ToArray();
    }
    foreach (ICommand command in commandList)
    {
        if (this.ShouldExecute(command))
        {
            if (!command.CanExecute(parameter))
            {
                return false;
            }

            hasEnabledCommandsThatShouldBeExecuted = true;
        }
    }

    return hasEnabledCommandsThatShouldBeExecuted;
}

public virtual void Execute(object parameter)
{
    Queue<ICommand> commands;
    lock (this.registeredCommands)
    {
        commands = new 
        Queue<ICommand>(this.registeredCommands.Where(this.ShouldExecute).ToList());
    }

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

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

Ff647893.bf8cb738-e22e-4eec-994d-1d73d4a8c71a(en-us,PandP.10).png

Figure 1

Connection between CompositeCommand and DelegateCommand

Activity Monitoring Behavior

In some cases, you may want the composite command to execute only active commands. To support this, 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 false, the command will not be executed nor accounted for when calculating the CompositeCommand's CanExecute return value. 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 Commands in Composite Commands

Individual commands are either registered or unregistered in composite commands 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.

commandProxy.SubmitAllOrdersCommand.RegisterCommand(orderCompositePresentationModel.SubmitCommand);
commandProxy.CancelAllOrdersCommand.RegisterCommand(orderCompositePresentationModel.CancelCommand);

Using Commands in Silverlight

Unlike WPF, the current version of Silverlight does not offer native support for commands. It contains the ICommand interface for future command implementations.

The Composite Application Library contains a command implementation for Silverlight that closely matches WPF's implementation and allows you to share presentation models with commands between WPF and Silverlight applications.

Ff647893.note(en-us,PandP.10).gifNote:
By default, commanding support in the Composite Application Library for Silverlight is limited to controls that inherit from ButtonBase. However, the commanding infrastructure allows you to build commands for other controls. For more information, see the section "Extending Command Support" later in this topic.

Binding Commands to a View

The following example from the Commanding QuickStart shows you how to bind a command that is defined on a ViewModel to a button's click event.

<Button Content="Submit All" commands:Click.Command="{Binding Path=SubmitAllOrdersCommand}"

Providing a Command Parameter Value

Like in WPF, you can also provide a command parameter value.

Commands:Click.CommandParameter="{Binding Path=TickerSymbol}"

Binding Commands Without Using the DataContext

Silverlight supports data binding only against the DataContext or against static resources; Silverlight does not support data binding against other elements in the visual tree. This causes issues if you want to data bind a control that is within an ItemsControl. Figure 2 illustrates this.

Ff647893.224ebec6-9e1b-4b12-9e31-c6634e192aff(en-us,PandP.10).png

Figure 2

Binding commands without using the DataContext

The DataContext of the items in the list box is not the same as the views DataContext. Each item's DataContext refers to an item in the collection that is bound to the list box's ItemsSource property.

A solution is to bind the command property to a static resource and set the value of the static resource to the command you want to bind. This is illustrated in the following XAML from the Stock Trader RI.

<!—Specifying the observablecommand in the view's resources-->
<UserControl.Resources>
    <Infrastructure:ObservableCommand x:Key="BuyCommand" />
</UserControl.Resources>

<!—Binding the Button Click to the command. This control can sit inside a datagrid or a list box. -->
<Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource BuyCommand}}" Commands:Click.CommandParameter="{Binding Path=TickerSymbol}" />

Then in the code-behind of the view, you must specify that the value of the resource actually points to the command on the presentation model. The following is an example of this from the Stock Trader RI, where the BuyCommand property on the presentation model is put in the resources.

((ObservableCommand)this.Resources["BuyCommand"]).Value = value != null ? value.BuyCommand : null;

Extending Command Support

The command support in Silverlight is built using an attached behavior pattern. This pattern connects events raised by controls to code on a Presenter or PresentationModel. In WPF and Silverlight, an attached behavior is comprised of two parts: an attached property and a behavior object. The attached property establishes a relationship between the target control and the behavior object. The behavior object monitors the target control and takes action based on events or state change in the control. For example, the Composite Application Library provides an attached behavior that executes commands from the Click event of ButtonBase. Figure 3 shows the relationship between ButtonBase, ButtonBaseClickCommandBehavior, and an ICommand.

Ff647893.8d9dbc99-d59f-42e9-b496-420358d2a961(en-us,PandP.10).png

Figure 3

Forwarding a ButtonClick event to an ICommand

Frequently, applications need to invoke commands from controls or events other than the Click event from ButtonBase. In these cases, you need to define your own attached property and behavior. The Composite Application Library provides a CommandBehaviorBase<T> to make it easier to create behaviors that interact with ICommands. This class invokes the command and monitors changes in the command's CanExecuteChanged event, and can be used to extend command support in both Silverlight and WPF.

To create your custom behavior, create a class that inherits from CommandBehaviorBase<T> and targets the control you want to monitor. In the constructor of your class, you will need to subscribe to the events you want to monitor from the control. The following code example shows ButtonBaseClickCommandBehavior.

public class ButtonBaseClickCommandBehavior : CommandBehaviorBase<ButtonBase>
{
    public ButtonBaseClickCommandBehavior(ButtonBase clickableObject) : base(clickableObject)
    {
        clickableObject.Click += OnClick;
    }
      
    private void OnClick(object sender, System.Windows.RoutedEventArgs e)
    {
        ExecuteCommand();
    }
}

Attaching the behavior to a control involves defining an attached property. To connect to commands, you need two attached properties defined. One public attached property to reference the ICommand to execute and one private attached property to keep track of the behavior associated with the control, which will not be accessible through XAML. For example, the following code example shows the attached properties for Click.

private static readonly DependencyProperty ClickCommandBehaviorProperty = 
DependencyProperty.RegisterAttached(
            "ClickCommandBehavior",
            typeof(ButtonBaseClickCommandBehavior),
            typeof(Click),
            null);

public static readonly DependencyProperty CommandProperty = 
DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICommand),
            typeof(Click),
            new PropertyMetadata(OnSetCommandCallback));

Setting up the behavior and connecting the command to the behavior is handled during the OnSetCommandCallback callback method, as shown in the following code.

private static void OnSetCommandCallback(DependencyObject dependencyObject,  
DependencyPropertyChangedEventArgs e)
{
     ButtonBase buttonBase = dependencyObject as ButtonBase;
     if (buttonBase != null)
     {
        ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase);
        behavior.Command = e.NewValue as ICommand;
     }
}

private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
    ButtonBase buttonBase = dependencyObject as ButtonBase;
    if (buttonBase != null)
    {
        ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase);
        behavior.CommandParameter = e.NewValue;
    }
}

private static ButtonBaseClickCommandBehavior GetOrCreateBehavior(ButtonBase buttonBase)
{
    ButtonBaseClickCommandBehavior behavior = 
        buttonBase.GetValue(ClickCommandBehaviorProperty) as 
             ButtonBaseClickCommandBehavior;
    if (behavior == null)
    {
        behavior = new ButtonBaseClickCommandBehavior(buttonBase);
        buttonBase.SetValue(ClickCommandBehaviorProperty, behavior);
    }

    return behavior;
}

For more information about attached properties, see Attached Properties Overview on MSDN.

Frequently Asked Questions About Commands

Why are WPF routed commands not used?

WPF routed commands have a number of limitations. They are coupled to elements in the logical tree because they use routed events to deliver the command messages. This means you cannot directly connect 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 CompositeCommands. 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. To work around for this, consider creating a derived CompositeCommand that allows commands to be ordered.

More Information

For more information about commands in the Composite Application Library, see the following topics:

For more information about other Composite Application Guidance technical concepts, see the following topics:





Home page on MSDN | Community site

Show:
© 2014 Microsoft