Keybinding a RelayCommand

19,528

Solution 1

The Command property of the KeyBinding class doesn't support data binding. This issue is going to be solved in .NET 4.0 and you should be able to see it in the coming .NET 4.0 Beta 2 version.

Solution 2

I don't think you can do this from XAML, for exactly the reasons you describe.

I ended up doing it in the code-behind. Although it's code, it's only a single line of code, and still rather declarative, so I can live with it. However, I'd really hope that this is solved in the next version of WPF.

Here's a sample line of code from one of my projects:

this.InputBindings.Add(new KeyBinding(
    ((MedicContext)this.DataContext).SynchronizeCommand,
    new KeyGesture(Key.F9)));

SynchronizeCommand in this case is an instance of RelayCommand, and (obviously) F9 triggers it.

Solution 3

You could subclass KeyBinding, add a CommandBinding dependency property that sets the Command property, and then add it to XAML like any other input binding.

public class RelayKeyBinding : KeyBinding
{
    public static readonly DependencyProperty CommandBindingProperty =
        DependencyProperty.Register("CommandBinding", typeof(ICommand), 
        typeof(RelayKeyBinding),
        new FrameworkPropertyMetadata(OnCommandBindingChanged));
    public ICommand CommandBinding
    {
        get { return (ICommand)GetValue(CommandBindingProperty); }
        set { SetValue(CommandBindingProperty, value); }
    }

    private static void OnCommandBindingChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var keyBinding = (RelayKeyBinding)d;
        keyBinding.Command = (ICommand)e.NewValue;
    }
}

XAML:

<Window.InputBindings>
    <RelayKeyBinding 
        Key="PageUp" 
        CommandBinding="{Binding SelectPreviousLayerCommand}" />
</Window.InputBindings>

Solution 4

You can use the CommandReference class.

Very elegant code and works like a charm.

Take a look at: How do I associate a keypress with a DelegateCommand in Composite WPF?

It works the same with RelayCommands, but it won't update your CanExecutes since the CommandReference doesn't use call CommandManager.RequerySuggested All you need to do to achieve automatic CanExecute reevaluation is do the following change inside the CommandReference class

public event EventHandler CanExecuteChanged
{
     add { CommandManager.RequerySuggested += value; }
     remove { CommandManager.RequerySuggested -= value; }
}
Share:
19,528
Joe White
Author by

Joe White

Updated on June 06, 2022

Comments

  • Joe White
    Joe White about 2 years

    I'm using the RelayCommand in my app. It's great for putting the code in the viewmodel, but how do I bind keystrokes to my command?

    RoutedUICommand has its InputGestures property, which makes the command automatically be invoked when I press the keystroke. (As an added bonus, it even makes the keystroke display in the MenuItem.) Unfortunately, there's no reusable interface for RoutedUICommand's extra properties, so I can't make a RelayUICommand that gets the same magic.

    I've already tried using InputBindings:

    <Window.InputBindings>
        <KeyBinding Key="PageUp" Command="{Binding SelectPreviousLayerCommand}"/>
    </Window.InputBindings>
    

    But that gets me a runtime exception, because KeyBinding.Command isn't a dependency property. (Actually, what it complains about is that KeyBinding isn't even a DependencyObject.) And since my RelayCommand is a property on my ViewModel (as opposed to the static field that RoutedUICommand is designed for), databinding is the only way I know of to reference it from XAML.

    How have you guys solved this? What's the best way to bind a keystroke to a RelayCommand?