0

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.

6
  • 2
    That doesn't seem to make sense. An object that is assigned to a Button's Command property must implement System.Windows.Input.ICommand. Commented Feb 20, 2017 at 11:23
  • Maybe my custom ICommand interface should inherit from System.Windows.Input.ICommand ? Or there is a work around using Click attribute? Commented Feb 20, 2017 at 11:26
  • "How do I bind my NewCommand class if Command attribute expects class that implements System.Windows.Input.ICommand?" You can't. You must set the Command property of the Button to an object that implements the built-in ICommand interface. Commented Feb 20, 2017 at 11:29
  • You can of course attach a Click event handler that calls some custom command object, but for what reason? If your custom ICommand interface inherits System.Windows.Input.ICommand, your ICommand implementation would obviously also implement System.Windows.Input.ICommand. So why have a custom ICommand at all? Commented Feb 20, 2017 at 11:33
  • Ignoring the question whether it makes sense or not, you may create an attached property that uses your custom ICommand as type, and that attaches a ButtonBase.Click handler in its PropertyChangedCallback. Commented Feb 20, 2017 at 11:58

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.