'WPF change bound command for keybindings based on property?

I have a xaml window that I want to bind the Escape key to different commands in my view model depending on the value of a boolean property.

IsSearching == true, bind to a CancelSearch command

IsSearching == false, bind to a Close command.

Is there a way to do this without a code behind?



Solution 1:[1]

<Window>
 <Window.InputBindings>
     <KeyBinding Command="{Binding SomeCommand}" Key="F5"/>
 </Window.InputBindings>
</Window>

This is a workaround

private ICommand someCommand;
public ICommand SomeCommand
{
    get
    {
        return someCommand 
            ?? (someCommand = new ActionCommand(() =>
            {

                if(IsSearching)
                   OnCancelExecute();
                else
                   OnCloseExecute();
            }));
    }
}

Solution 2:[2]

You can't do everything directly in XAML, and sometimes, some code-behind is needed.
Here's how I'd do it if I didn't want to spend hours searching for a pure XAML solution.

In your XAML, give a x:Name to your Keybinding (here, searchKey).
Then in code-behind, add something like this in your constructor:

public partial class YourView
{
    public YourView()
    {
        InitializeComponent();
        var vm = DataContext as YourViewModel;
        vm.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName != "IsSearching")
                    return;

                searchKey.Command = vm.IsSearching ? vm.SearchCommand : vm.CancelCommand:
            };
    }
}

Solution 3:[3]

Another way to hande this is to implement custom KeyBinding class and keep your ViewModel or code behind as clean as possible.

MultiKeyBinding.cs

using Prism.Commands;
using System.Windows;
using System.Windows.Input;

namespace ExampleDemoApp
{
    /// <summary>
    /// An extended <see cref="KeyBinding"/> that covers binding to two commands and switch between them (<see cref="UseSecondaryCommand"/>).
    /// </summary>
    public class MultiKeyBinding : KeyBinding
    {
        public MultiKeyBinding()
        {
            this.Command = new DelegateCommand<object?>(this.OnExecuteCommand);
        }

        private void OnExecuteCommand(object? parameter)
        {
            var command = this.UseSecondaryCommand ? this.SecondaryCommand : this.PrimaryCommand;
            var commandParameter = this.UseSecondaryCommand ? this.SecondaryCommandParameter : this.PrimaryCommandParameter;

            if (command.CanExecute(commandParameter))
                command.Execute(commandParameter);
        }

        public bool UseSecondaryCommand
        {
            get => (bool)GetValue(UseSecondaryCommandProperty);
            set => SetValue(UseSecondaryCommandProperty, value);
        }

        public static readonly DependencyProperty UseSecondaryCommandProperty = DependencyProperty.Register(
            nameof(UseSecondaryCommand), typeof(bool), typeof(MultiKeyBinding), new PropertyMetadata(defaultValue: false));

        public ICommand PrimaryCommand
        {
            get => (ICommand)GetValue(PrimaryCommandProperty);
            set => SetValue(PrimaryCommandProperty, value);
        }

        public static readonly DependencyProperty PrimaryCommandProperty = DependencyProperty.Register(
            nameof(PrimaryCommand), typeof(ICommand), typeof(MultiKeyBinding), new PropertyMetadata(null));

        public object PrimaryCommandParameter
        {
            get => (object)GetValue(PrimaryCommandParameterProperty);
            set => SetValue(PrimaryCommandParameterProperty, value);
        }

        public static readonly DependencyProperty PrimaryCommandParameterProperty = DependencyProperty.Register(
            nameof(PrimaryCommandParameter), typeof(object), typeof(MultiKeyBinding), new PropertyMetadata(null));

        public ICommand SecondaryCommand
        {
            get => (ICommand)GetValue(SecondaryCommandProperty);
            set => SetValue(SecondaryCommandProperty, value);
        }

        public static readonly DependencyProperty SecondaryCommandProperty = DependencyProperty.Register(
            nameof(SecondaryCommand), typeof(ICommand), typeof(MultiKeyBinding), new PropertyMetadata(null));

        public object SecondaryCommandParameter
        {
            get => (object)GetValue(SecondaryCommandParameterProperty);
            set => SetValue(SecondaryCommandParameterProperty, value);
        }

        public static readonly DependencyProperty SecondaryCommandParameterProperty = DependencyProperty.Register(
            nameof(SecondaryCommandParameter), typeof(object), typeof(MultiKeyBinding), new PropertyMetadata(null));
    }
}

Usage:

<UserControl.InputBindings>
    <local:MultiKeyBinding
        Key="Esc"
        PrimaryCommand="{Binding CloseCommand}"
        SecondaryCommand="{Binding CancelSearchCommand}"
        UseSecondaryCommand="{Binding IsSearching, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
        />
</UserControl.InputBindings>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 yurislav