ICommand Interface

Represents a command, which can be used to create a binding between an event of one or more sources and zero or more target objects. Can be used easily to bind events with targets with any technology.

Definition

Namespace: KGySoft.ComponentModel
Assembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 8.1.0
C#
public interface ICommand

Remarks

Unlike the System.Windows.Input.ICommand type, this ICommand represents a stateless command so the implementations are best to be accessed via static members. The command states (such as Enabled or any other status) belong to the created binding represented by the ICommandBinding interface and can be accessed by the ICommandBinding.State property, which returns an ICommandState instance.

To implement a command by using a delegate you can also choose one of the four pairs of predefined classes: SimpleCommand/SimpleCommandTParam, TargetedCommandTTarget/TargetedCommandTTarget, TParam, SourceAwareCommandTEventArgs/SourceAwareCommandTEventArgs, TParam and SourceAwareTargetedCommandTEventArgs, TTarget/SourceAwareTargetedCommandTEventArgs, TTarget, TParam depending whether the command is parameterized, targets specific objects and behaves differently based on the source's state or event arguments.

A binding can be created by the Commands.CreateBinding methods or by the CommandBindingsCollection class. When a binding or a collection of bindings are disposed all of the event subscriptions are released, which makes the cleanup really simple.

Example

  Tip

  • Try also online.
  • For a more detailed step-by-step guide see the Command Binding section at the Project Site.

The following examples demonstrate how to define different kind of commands:

C#
public static partial class MyCommands
{
    // A simple command with no target and ignored source: (assumes we have an ExitCode state)
    public static ICommand CloseApplicationCommand =>
        new SimpleCommand(state => Environment.Exit((int)state["ExitCode"])); // or: .Exit(state.AsDynamic.ExitCode)

    // A source aware command, which can access the source object and the triggering event data
    public static ICommand LogMouseCommand =>
        new SourceAwareCommand<MouseEventArgs>(source => Debug.WriteLine($"Mouse coordinates: {source.EventArgs.X}; {source.EventArgs.Y}"));

    // A targeted command (also demonstrates how to change the command state of another command):
    public static ICommand ToggleCommandEnabled =>
        new TargetedCommand<ICommandState>((state, targetState) => targetState.Enabled = !targetState.Enabled);

    // A source aware targeted command:
    public static ICommand ProcessKeysCommand => new SourceAwareTargetedCommand<KeyEventArgs, Control>(OnProcessKeysCommand);

    private static void OnProcessKeysCommand(ICommandSource<KeyEventArgs> source, Control target)
    {
        // do something with target by source.EventArgs
    }
}

And a binding for a command can be created in an application, with any kind of UI, which uses events, or even without any UI: only event sources are needed.

C#
public class MyView : SomeViewBaseWithEvents // base can be a Window in WPF or a Form in WindowsForms or simply any component with events.
{
    private ICommandBinding exitBinding;

    private CommandBindingsCollection commandBindings = new CommandBindingsCollection();

    public MyView()
    {
        // ...some initialization of our View...

        // Simplest case: using the CreateBinding extension on ICommand.
        // Below we assume we have a menu item with a Click event.
        // We set also the initial status. By adding the property state updater the
        // states will be applied on the source as properties.
        exitBinding = MyCommands.CloseApplicationCommand.CreateBinding(
            new Dictionary<string, object>
            {
                { "Text", "Exit Application" },
                { "ShortcutKeys", Keys.Alt | Keys.F4 },
                { "ExitCode", 0 },
            })
           .AddStateUpdater(PropertyCommandStateUpdater.Updater)
           .AddSource(menuItemExit, "Click");

        // If we add the created bindings to a CommandBindingsCollection, then all of them can be disposed at once by disposing the collection.
        commandBindings.Add(exitBinding);

        // We can create a binding by the Add methods of the collection, too:
        // As we added the property state updater to the exitBinding the menuItemExit.Enabled property will reflect the command state.
        var toggleEnabledBinding = commandBindings.Add(MyCommands.ToggleCommandEnabledCommand, buttonToggle, "Click", exitBinding.State);

        // The line above can be written by a more descriptive fluent syntax (and that's how multiple sources can be added):
        var toggleEnabledBinding = commandBindings.Add(MyCommands.ToggleCommandEnabledCommand)
            .AddSource(buttonToggle, nameof(Button.Click))
            .AddTarget(exitBinding.State);

        // If we set the state of a binding with a property updater it will be applied for all sources (only if a matching property exists):
        exitBinding.State["Text"] = "A new text for the exit command";

        // Or as dynamic:
        toggleEnabledBinding.State.AsDynamic.Text = "A new text for the exit command";
    }

    protected override Dispose(bool disposing)
    {
         // disposing a CommandBindingsCollection will release all of the internal event subscriptions at once
        if (disposing)
            commandBindings.Dispose();

        base.Dispose(disposing);
    }
}

Commands can also have parameter, which is evaluated whenever the command is triggered:

C#
// A parameterized and targeted command:
public static ICommand SetBackColorCommand =>
    new TargetedCommand<Control, Color>((target, value) => target.BackColor = value);

// [...]

// The parameter is evaluated only once whenever the command is triggered but SetBackColorCommand
// will be invoked three times (once for each target) with the same parameter value.
// It is recommended to specify the parameter callback before adding any sources to avoid the possible
// issues if there is any chance that the source can be triggered before completing the initialization.
commandBindings.Add(MyCommands.SetBackColorCommand)
    .WithParameter(() => GetSomeColor(myViewModel.Severity)) // specifying a callback to return a parameter value
    .AddSource(myViewModel, nameof(myViewModel.SeverityChanged)) // whatever source event
    .AddTarget(this)
    .AddTarget(panelInfo) // now multiple targets will be set by the same parameter
    .AddTarget(buttonDoSomething);

Methods

Execute Executes the command invoked by the specified source for the specified target.

Extension Methods

CreateBinding Creates a binding for a command without any sources and targets. At least one source must be added by the ICommandBinding.AddSource method to make the command invokable. Targets can be added by the ICommandBinding.AddTarget method.
(Defined by Command)
CreateBinding Creates a binding for a command using the specified source, eventName and targets.
(Defined by Command)
CreateBinding Creates a binding for a command using the specified source, eventName and targets as well as the optionally provided initial state of the binding.
(Defined by Command)

See Also