Search code examples
c#wpfxamldata-binding

Can window keybindings assign the press and release event individually?


In WPF (XAML/C#), I can bind a hotkey on the window to execute a command, like:

<Window.InputBindings>
    <KeyBinding Modifier="Control" Key="Space" Command="{Binding TogglePushToTalk}"/>
</Window.InputBindings>

This executes the ICommand called TogglePushToTalk in my ViewModel when Ctrl+Space is pressed then subsequently released.

Is there a way to bind commands or actions to the press and release events individually, in an MVVM-friendly way, without getting into code-behind? I.e., so that pressing Ctrl+Space will execute StartPushToTalk, and then releasing those keys will call StopPushToTalk.


Solution

  • Is there a way to bind commands or actions to the press and release events individually, in an MVVM-friendly way, without getting into code-behind?

    Not using pure XAML but you could implement an attached behaviour. Here is an example that should at least give you the idea:

    public static class KeyBehavior
    {
        public static readonly DependencyProperty KeyDownCommandProperty = DependencyProperty.RegisterAttached(
            "KeyDownCommand", typeof(ICommand), typeof(KeyBehavior), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSet)));
    
        public static readonly DependencyProperty KeyUpCommandProperty = DependencyProperty.RegisterAttached(
            "KeyUpCommand", typeof(ICommand), typeof(KeyBehavior));
    
        private static void OnSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Window w = (Window)d;
            w.KeyDown += OnKeyDown;
        }
    
        private static void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Space && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
            {
                Window w = (Window)sender;
                ICommand keyDownCommand = GetKeyDownCommand(w);
                if (keyDownCommand != null)
                {
                    w.KeyUp += W_KeyUp;
                    keyDownCommand.Execute(null);
                }
            }
        }
    
        private static void W_KeyUp(object sender, KeyEventArgs e)
        {
            Window w = (Window)sender;
            w.KeyUp -= W_KeyUp;
    
            ICommand keyUpCommand = GetKeyUpCommand(w);
            if (keyUpCommand != null)
                keyUpCommand.Execute(null);
        }
    
        public static void SetKeyDownCommand(Window element, ICommand value) => element.SetValue(KeyDownCommandProperty, value);
        public static ICommand GetKeyDownCommand(Window element) => (ICommand)element.GetValue(KeyDownCommandProperty);
    
        public static void SetKeyUpCommand(Window element, ICommand value) => element.SetValue(KeyUpCommandProperty, value);
        public static ICommand GetKeyUpCommand(Window element) => (ICommand)element.GetValue(KeyUpCommandProperty);
    }
    

    You may want to add dependency properties for the keys in order to be able to reuse the command for several different keys and key combinations.