eXPerience
eXPerience

Reputation: 466

Implementation of Command pattern in WPF

Let's say I'm not allowed to use default wpf classes RoutedCommand, CommandBinding and ICommand interface. Instead I must provide my own implementation. So I specify simple ICommand interface:

public interface ICommand
{
    void Execute();
}

Make a class for specific command:

public class NewCommand : ICommand
{
    private readonly IReceiver _receiver;
    // receiver is any class that provides implementation of a New() method

    public NewCommand(IReceiver receiver)
    {
        _receiver = receiver;
    }
    public void Execute()
    {
        _receiver.New();
    }
}

There must be an Invoker as well:

public class Invoker
{
    private ICommand _command;
    public void SetCommand(ICommand c)
    {
        _command = c;
    }
    public void Run()
    {
        _command.Execute();
    }
}

How do I wire this all to XAML considering that with default implementation my code looks like this:

public class DesignerCanvas : Canvas
{
    private readonly IDesignerCommandsReceiver _receiver;

    public DesignerCanvas()
    {
        _receiver = new DesignerCommandsReceiver(this);
        CommandBindings.Add(new CommandBinding(ApplicationCommands.New, New_Executed));
    }

    private void New_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        _receiver.New();
    }
}

And the button that invokes New command is binded like:

<Button Margin="3" Width="55" Style="{StaticResource ToolBarButtonBaseStyle}"
                    Command="{x:Static ApplicationCommands.New}"
                    CommandTarget="{Binding ElementName=MyDesigner}">
                <Button.Content>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="4*"/>
                            <RowDefinition Height="1*"/>
                        </Grid.RowDefinitions>
                        <Image Source="Images/GenericDocument.png" Width="45"/>
                        <TextBlock Grid.Row="1" Text="New" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
                    </Grid>
                </Button.Content>
            </Button>

How do I bind my NewCommand class if Command attribute expects class that implements System.Windows.Input.ICommand? Where do I create instance of Invoker, set NewCommand and Run it? Generally speaking, I'm a little confused about how to replace default implementation of the pattern with my own. Any advices are welcome.

Upvotes: 0

Views: 982

Answers (1)

Clemens
Clemens

Reputation: 128136

Although I doubt that it really makes sense to have a custom command interface and implementation, you could probably create a custom attached Command property, which attaches a Click handler in its PropertyChangedCallback. The Click handler would then execute the custom command.

The following code sample declares a custom ICustomCommand interface, and declares an attached Command property in a static CustomCommandEx class.

public interface ICustomCommand
{
    void Execute();
}

public static class CustomCommandEx
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICustomCommand),
            typeof(CustomCommandEx),
            new PropertyMetadata(CommandPropertyChanged));

    public static ICustomCommand GetCommand(DependencyObject obj)
    {
        return (ICustomCommand)obj.GetValue(CommandProperty);
    }

    public static void SetCommand(DependencyObject obj, ICustomCommand value)
    {
        obj.SetValue(CommandProperty, value);
    }

    private static void CommandPropertyChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs eventArgs)
    {
        var button = obj as ButtonBase;
        var command = eventArgs.NewValue as ICustomCommand;

        if (button != null)
        {
            button.Click += (s, e) => command.Execute();
        }
    }
}

You would assign the attached property like this:

<Button local:CustomCommandEx.Command="{Binding SomeCommand}"/>

where SomeCommand is a property in your view model that implements ICustomCommand.

Upvotes: 1

Related Questions