How to: Implement ICommandSource

This example shows how to create a command source by implementing ICommandSource. A command source is an object that knows how to invoke a command. The ICommandSource interface exposes three members:

  • Command: the command that will be invoked.
  • CommandParameter: a user-defined data type which is passed from the command source to the method that handles the command.
  • CommandTarget: the object that the command is being executed on.

In this example, a class is created that inherits from the Slider control and implements the ICommandSource interface.

Example

WPF provides a number of classes which implement ICommandSource, such as Button, MenuItem, and Hyperlink. A command source defines how it invokes a command. These classes invoke a command when they're clicked and they only become a command source when their Command property is set.

In this example, you'll invoke the command when the slider is moved, or more accurately, when the Value property is changed.

The following is the class definition:

public class CommandSlider : Slider, ICommandSource
{
    public CommandSlider() : base()
    {
    }
Public Class CommandSlider
    Inherits Slider
    Implements ICommandSource
    Public Sub New()
        MyBase.New()

    End Sub

The next step is to implement the ICommandSource members. In this example, the properties are implemented as DependencyProperty objects. This enables the properties to use data binding. For more information about the DependencyProperty class, see the Dependency Properties Overview. For more information about data binding, see the Data Binding Overview.

Only the Command property is shown here.

// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty CommandProperty =
    DependencyProperty.Register(
        "Command",
        typeof(ICommand),
        typeof(CommandSlider),
        new PropertyMetadata((ICommand)null,
        new PropertyChangedCallback(CommandChanged)));

public ICommand Command
{
    get
    {
        return (ICommand)GetValue(CommandProperty);
    }
    set
    {
        SetValue(CommandProperty, value);
    }
}
' Make Command a dependency property so it can use databinding.
Public Shared ReadOnly CommandProperty As DependencyProperty =
    DependencyProperty.Register("Command", GetType(ICommand),
        GetType(CommandSlider),
        New PropertyMetadata(CType(Nothing, ICommand),
            New PropertyChangedCallback(AddressOf CommandChanged)))

Public ReadOnly Property Command1() As ICommand Implements ICommandSource.Command
    Get
        Return CType(GetValue(CommandProperty), ICommand)
    End Get
End Property

Public Property Command() As ICommand
    Get
        Return CType(GetValue(CommandProperty), ICommand)
    End Get
    Set(ByVal value As ICommand)
        SetValue(CommandProperty, value)
    End Set
End Property

The following is the DependencyProperty change callback:

// Command dependency property change callback.
private static void CommandChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    CommandSlider cs = (CommandSlider)d;
    cs.HookUpCommand((ICommand)e.OldValue,(ICommand)e.NewValue);
}
' Command dependency property change callback.
Private Shared Sub CommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim cs As CommandSlider = CType(d, CommandSlider)
    cs.HookUpCommand(CType(e.OldValue, ICommand), CType(e.NewValue, ICommand))
End Sub

The next step is to add and remove the command which is associated with the command source. The Command property cannot simply be overwritten when a new command is added, because the event handlers associated with the previous command, if there was one, must be removed first.

// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
    // If oldCommand is not null, then we need to remove the handlers.
    if (oldCommand != null)
    {
        RemoveCommand(oldCommand, newCommand);
    }
    AddCommand(oldCommand, newCommand);
}

// Remove an old command from the Command Property.
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
    EventHandler handler = CanExecuteChanged;
    oldCommand.CanExecuteChanged -= handler;
}

// Add the command.
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
    EventHandler handler = new EventHandler(CanExecuteChanged);
    canExecuteChangedHandler = handler;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += canExecuteChangedHandler;
    }
}
' Add a new command to the Command Property.
Private Sub HookUpCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    ' If oldCommand is not null, then we need to remove the handlers.
    If oldCommand IsNot Nothing Then
        RemoveCommand(oldCommand, newCommand)
    End If
    AddCommand(oldCommand, newCommand)
End Sub

' Remove an old command from the Command Property.
Private Sub RemoveCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    Dim handler As EventHandler = AddressOf CanExecuteChanged
    RemoveHandler oldCommand.CanExecuteChanged, handler
End Sub

' Add the command.
Private Sub AddCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    Dim handler As New EventHandler(AddressOf CanExecuteChanged)
    canExecuteChangedHandler = handler
    If newCommand IsNot Nothing Then
        AddHandler newCommand.CanExecuteChanged, canExecuteChangedHandler
    End If
End Sub

The next step is to create logic for the CanExecuteChanged handler.

The CanExecuteChanged event notifies the command source that the ability of the command to execute on the current command target may have changed. When a command source receives this event, it typically calls the CanExecute method on the command. If the command cannot execute on the current command target, the command source will typically disable itself. If the command can execute on the current command target, the command source will typically enable itself.

private void CanExecuteChanged(object sender, EventArgs e)
{

    if (this.Command != null)
    {
        RoutedCommand command = this.Command as RoutedCommand;

        // If a RoutedCommand.
        if (command != null)
        {
            if (command.CanExecute(CommandParameter, CommandTarget))
            {
                this.IsEnabled = true;
            }
            else
            {
                this.IsEnabled = false;
            }
        }
        // If a not RoutedCommand.
        else
        {
            if (Command.CanExecute(CommandParameter))
            {
                this.IsEnabled = true;
            }
            else
            {
                this.IsEnabled = false;
            }
        }
    }
}
Private Sub CanExecuteChanged(ByVal sender As Object, ByVal e As EventArgs)

    If Me.Command IsNot Nothing Then
        Dim command As RoutedCommand = TryCast(Me.Command, RoutedCommand)

        ' If a RoutedCommand.
        If command IsNot Nothing Then
            If command.CanExecute(CommandParameter, CommandTarget) Then
                Me.IsEnabled = True
            Else
                Me.IsEnabled = False
            End If
            ' If a not RoutedCommand.
        Else
            If Me.Command.CanExecute(CommandParameter) Then
                Me.IsEnabled = True
            Else
                Me.IsEnabled = False
            End If
        End If
    End If
End Sub

The last step is the Execute method. If the command is a RoutedCommand, the RoutedCommand Execute method is called; otherwise, the ICommand Execute method is called.

// If Command is defined, moving the slider will invoke the command;
// Otherwise, the slider will behave normally.
protected override void OnValueChanged(double oldValue, double newValue)
{
    base.OnValueChanged(oldValue, newValue);

    if (this.Command != null)
    {
        RoutedCommand command = Command as RoutedCommand;

        if (command != null)
        {
            command.Execute(CommandParameter, CommandTarget);
        }
        else
        {
            ((ICommand)Command).Execute(CommandParameter);
        }
    }
}
' If Command is defined, moving the slider will invoke the command;
' Otherwise, the slider will behave normally.
Protected Overrides Sub OnValueChanged(ByVal oldValue As Double, ByVal newValue As Double)
    MyBase.OnValueChanged(oldValue, newValue)

    If Me.Command IsNot Nothing Then
        Dim command As RoutedCommand = TryCast(Me.Command, RoutedCommand)

        If command IsNot Nothing Then
            command.Execute(CommandParameter, CommandTarget)
        Else
            CType(Me.Command, ICommand).Execute(CommandParameter)
        End If
    End If
End Sub

See also