Keyboard shortcuts in WPF

WpfKeyboard Shortcuts

Wpf Problem Overview


I know about using _ instead of &, but I'm looking at all the Ctrl + type shortcuts.

Ctrl+Z for undo, Ctrl+S for save, etc.

Is there a 'standard' way for implementing these in WPF applications? Or is it a case of roll your own and wire them up to whatever command/control?

Wpf Solutions


Solution 1 - Wpf

One way is to add your shortcut keys to the commands themselves them as InputGestures. Commands are implemented as RoutedCommands.

This enables the shortcut keys to work even if they're not hooked up to any controls. And since menu items understand keyboard gestures, they'll automatically display your shortcut key in the menu items text, if you hook that command up to your menu item.


Steps
  1. Create static attribute to hold a command (preferably as a property in a static class you create for commands - but for a simple example, just using a static attribute in window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
    
  2. Add the shortcut key(s) that should invoke method:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
    
  3. Create a command binding that points to your method to call on execute. Put these in the command bindings for the UI element under which it should work for (e.g., the window) and the method:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" 
                         Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) 
     { ... }
    

Solution 2 - Wpf

I found this to be exactly what I was looking for related to key binding in WPF:

<Window.InputBindings>
        <KeyBinding Modifiers="Control"
                    Key="N"
                    Command="{Binding CreateCustomerCommand}" />
</Window.InputBindings>

See blog post MVVM CommandReference and KeyBinding

Solution 3 - Wpf

Try this.

First create a RoutedCommand object:

RoutedCommand newCmd = new RoutedCommand();
newCmd.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(newCmd, btnNew_Click));

Solution 4 - Wpf

It depends on where you want to use those.

TextBoxBase-derived controls already implement those shortcuts. If you want to use custom keyboard shortcuts you should take a look on Commands and Input gestures. Here is a small tutorial from Switch on the Code: WPF Tutorial - Command Bindings and Custom Commands

Solution 5 - Wpf

Documenting this answer for others, as there is a much simpler way to do this that is rarely referenced, and doesn't require touching the XAML at all.

To link a keyboard shortcut, in the Window constructor simply add a new KeyBinding to the InputBindings collection. As the command, pass in your arbitrary command class that implements ICommand. For the execute method, simply implement whatever logic you need. In my example below, my WindowCommand class takes a delegate that it will execute whenever invoked. When I construct the new WindowCommand to pass in with my binding, I simply indicate in my initializer, the method that I want the WindowCommand to execute.

You can use this pattern to come up with your own quick keyboard shortcuts.

public YourWindow() //inside any WPF Window constructor
{
   ...
   //add this one statement to bind a new keyboard command shortcut
   InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
      new WindowCommand(this)
      {
         ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
      }, new KeyGesture(Key.P, ModifierKeys.Control)));
   ...
}

Create a simple WindowCommand class which takes an execution delegate to fire off any method set on it.

public class WindowCommand : ICommand
{
    private MainWindow _window;

    //Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
    public Action ExecuteDelegate { get; set; }

    //You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
    public WindowCommand(MainWindow window)
    {
        _window = window;
    }

    //always called before executing the command, mine just always returns true
    public bool CanExecute(object parameter)
    {
        return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
    }

    public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface

    //the important method that executes the actual command logic
    public void Execute(object parameter)
    {
        if (ExecuteDelegate != null)
        {
            ExecuteDelegate();
        }
        else
        {
            throw new InvalidOperationException();
        }
    }
}

Solution 6 - Wpf

I had a similar problem and found @aliwa's answer to be the most helpful and most elegant solution; however, I needed a specific key combination, Ctrl + 1. Unfortunately I got the following error:

> '1' cannot be used as a value for 'Key'. Numbers are not valid enumeration values.

With a bit of further search, I modified @aliwa's answer to the following:

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+1" Command="{Binding MyCommand}"/>
</Window.InputBindings>

I found this to work great for pretty well any combination I needed.

Solution 7 - Wpf

VB.NET:
Public Shared SaveCommand_AltS As New RoutedCommand
Inside the loaded event:
SaveCommand_AltS.InputGestures.Add(New KeyGesture(Key.S, ModifierKeys.Control))

Me.CommandBindings.Add(New CommandBinding(SaveCommand_AltS, AddressOf Me.save))

No XAML is needed.

Solution 8 - Wpf

Although the top answers are correct, I personally like to work with attached properties to enable the solution to be applied to any UIElement, especially when the Window is not aware of the element that should be focused. In my experience I often see a composition of several view models and user controls, where the window is often nothing more that the root container.

Snippet

public sealed class AttachedProperties
{
    // Define the key gesture type converter
    [System.ComponentModel.TypeConverter(typeof(System.Windows.Input.KeyGestureConverter))]
    public static KeyGesture GetFocusShortcut(DependencyObject dependencyObject)
    {
        return (KeyGesture)dependencyObject?.GetValue(FocusShortcutProperty);
    }

    public static void SetFocusShortcut(DependencyObject dependencyObject, KeyGesture value)
    {
        dependencyObject?.SetValue(FocusShortcutProperty, value);
    }

    /// <summary>
    /// Enables window-wide focus shortcut for an <see cref="UIElement"/>.
    /// </summary>
    // Using a DependencyProperty as the backing store for FocusShortcut.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FocusShortcutProperty =
        DependencyProperty.RegisterAttached("FocusShortcut", typeof(KeyGesture), typeof(AttachedProperties), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnFocusShortcutChanged)));

    private static void OnFocusShortcutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is UIElement element) || e.NewValue == e.OldValue)
            return;

        var window = FindParentWindow(d);
        if (window == null)
            return;

        var gesture = GetFocusShortcut(d);
        if (gesture == null)
        {
            // Remove previous added input binding.
            for (int i = 0; i < window.InputBindings.Count; i++)
            {
                if (window.InputBindings[i].Gesture == e.OldValue && window.InputBindings[i].Command is FocusElementCommand)
                    window.InputBindings.RemoveAt(i--);
            }
        }
        else
        {
            // Add new input binding with the dedicated FocusElementCommand.
            // see: https://gist.github.com/shuebner20/349d044ed5236a7f2568cb17f3ed713d
            var command = new FocusElementCommand(element);
            window.InputBindings.Add(new InputBinding(command, gesture));
        }
    }
}

With this attached property you can define a focus shortcut for any UIElement. It will automatically register the input binding at the window containing the element.

Usage (XAML)

<TextBox x:Name="SearchTextBox"
         Text={Binding Path=SearchText}
         local:AttachedProperties.FocusShortcutKey="Ctrl+Q"/>

Source code

The full sample including the FocusElementCommand implementation is available as gist: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Disclaimer: You may use this code everywhere and free of charge. Please keep in mind, that this is a sample that is not suitable for heavy usage. For example, there is no garbage collection of removed elements because the Command will hold a strong reference to the element.

Solution 9 - Wpf

Special case: your shortcut doesn't trigger if the focus is on an element that "isn't native". In my case for example, a focus on a WpfCurrencyTextbox won't trigger shortcuts defined in your XAML (defined like in oliwa's answer).

I fixed this issue by making my shortcut global with the NHotkey package.

In short, for XAML, all you need to do is to replace

<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />

by

<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
            HotkeyManager.RegisterGlobalHotkey="True" />

Answer has also been posted to: https://stackoverflow.com/questions/48935/how-can-i-register-a-global-hot-key-to-say-ctrlshiftletter-using-wpf-and-ne/63843177#63843177

Solution 10 - Wpf

I tried all kinds of approaches using XAML and nothing worked. I finally found a solution based on the answer proved by Shahid Neermunda

First, the menu bar:

<Menu x:Name="MainMenuBar" Grid.Row="0" HorizontalContentAlignment="Left">
    <MenuItem Header="_File" HorizontalContentAlignment="Left">
        <MenuItem x:Name="NewProjectMenuItem"
                    Header="New Project"
                    InputGestureText="Ctrl+N"
                    Click="NewProject_Click"/>
        <MenuItem x:Name="OpenProjectMenuItem"
                    Header="Open Project"
                    InputGestureText="Ctrl+O"
                    Click="OpenProject_Click"/>
        <MenuItem x:Name="CloseProjectMenuItem"
                    Header="Close Project"
                    Click="CloseProject_Click"/>
        <Separator/>
        <MenuItem x:Name="SaveProjectMenuItem"
                    Header="Save Project"
                    InputGestureText="Ctrl+S"
                    Click="SaveProject_Click"/>
        <MenuItem x:Name="SaveProjectAsMenuItem"
                    Header="Save Project As ..."
                    InputGestureText="Shift+Ctrl+S"
                    Click="SaveProjectAs_Click"/>
        <Separator/>
        <MenuItem x:Name="ExitMenuItem"
                    Header="Exit"
                    InputGestureText="Alt+F4"
                    Click="Exit_Click"/>
    </MenuItem>
</Menu>

Nothing fancy. Each menu item has an 'InputGestureText' property (except for the close)

I then modified the click event methods that were auto-generated by the Click="[tab]" command. I'm only showing two here--one where there's a shortcut key defined and another where it isn't (Close):

private void OpenProject_Executed(object sender, ExecutedRoutedEventArgs e) => OpenProject_Click(sender, e);
private void OpenProject_Click(object sender, RoutedEventArgs e)
{
    OpenProject();
}

private void CloseProject_Click(object sender, RoutedEventArgs e)
{
    CloseProject();
}

The XXX_Executed(...) method is called by the shortcut binding (which I'll get to next) and the XXX_Click method is called by the Click command.

I did the same for the New Project, Open Project, Save Project As, and Exit auto-generated XXX_Click methods.

I then created a new file with the bindings (I separated it out to make it easier to find when the time comes to add additional bindings):

partial class MainWindow
{
    private void BindShortcuts()
    {
        BindShortcut(Key.N, ModifierKeys.Control, NewProject_Executed);
        BindShortcut(Key.O, ModifierKeys.Control, OpenProject_Executed);
        BindShortcut(Key.S, ModifierKeys.Control, SaveProject_Executed);
        BindShortcut(Key.S, ModifierKeys.Control | ModifierKeys.Shift, SaveProjectAs_Executed);
        BindShortcut(Key.F4, ModifierKeys.Alt, Exit_Executed);
    }

    private void BindShortcut(Key key, ModifierKeys modifiers, ExecutedRoutedEventHandler executed)
    {
        RoutedCommand cmd = new();
        _ = cmd.InputGestures.Add(new KeyGesture(key, modifiers));
        _ = CommandBindings.Add(new CommandBinding(cmd, executed));
    }
}

This way, when I add new menu items with more shortcuts attached, I only need to add the appropriate tag, define an XXX_Executed method to call into the auto-generated XXX_Click method, and update the BindShortcuts() function.

Finally, I added the following to my constructor for the MainWindow class:

public MainWindow()
{
    InitializeComponent();
    BindShortcuts();
}

Works like a charm.

Solution 11 - Wpf

How to associate the command with a MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBenjolView Question on Stackoverflow
Solution 1 - WpfAbby FichtnerView Answer on Stackoverflow
Solution 2 - WpfoliwaView Answer on Stackoverflow
Solution 3 - WpfShahid NeermundaView Answer on Stackoverflow
Solution 4 - WpfAnvakaView Answer on Stackoverflow
Solution 5 - WpfAyo IView Answer on Stackoverflow
Solution 6 - WpfNikView Answer on Stackoverflow
Solution 7 - WpfplaasmeisieView Answer on Stackoverflow
Solution 8 - WpfSebastian HübnerView Answer on Stackoverflow
Solution 9 - WpfAnthony NguyenView Answer on Stackoverflow
Solution 10 - Wpfuser1325543View Answer on Stackoverflow
Solution 11 - WpfJiří SkálaView Answer on Stackoverflow