[WPF] Declare global hotkeys in XAML with NHotkey

Very poorPoorAverageGoodExcellent (1 votes) 
Loading...Loading...

A common requirement for desktop applications is to handle system-wide hotkeys, in order to intercept keyboard shortcuts even when they don’t have focus. Unfortunately, there is no built-in feature in the .NET framework to do it.

Of course, this is not a new issue, and there are quite a few open-source libraries that address it (e.g. VirtualInput). Most of them rely on a global system hook, which allow them to intercept all keystrokes, even the ones you’re not interested in. I used some of those libraries before, but I’m not really happy with them:

  • they’re often tied to a specific UI framework (usually Windows Forms), which makes them a bit awkward to use in another UI framework (like WPF)
  • I don’t really like the approach of intercepting all keystrokes. It usually means that you end up with a big method with lots of if/else if to decide what to do based on which keys were pressed.

A better option, in my opinion, is to listen only to the keys you’re interested in, and declare what to do for each of those. The approach used in WPF for key bindings is quite elegant:

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
    <KeyBinding Gesture="Ctrl+Alt+Subtract" Command="{Binding DecrementCommand}" />
</Window.InputBindings>

But of course, key bindings are not global, they require that your app has focus… What if we could change that?

NHotkey is a very simple hotkey library that enables global key bindings. All you have to do is set an attached property to true on your key bindings:

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

And that’s it; the commands defined in the key bindings will now be invoked even if your app doesn’t have focus!

You can also use NHotkey from code:

HotkeyManager.Current.AddOrReplace("Increment", Key.Add, ModifierKeys.Control | ModifierKeys.Alt, OnIncrement);
HotkeyManager.Current.AddOrReplace("Decrement", Key.Subtract, ModifierKeys.Control | ModifierKeys.Alt, OnDecrement);

The library takes advantage of the RegisterHotkey function. Because it also supports Windows Forms, it is split into 3 parts, so that you don’t need to reference the WinForms assembly from a WPF app or vice versa:

  • The core library, which handles the hotkey registration itself, independently of any specific UI framework. This library is not directly usable, but is used by the other two.
  • The WinForms-specific API, which uses the Keys enumeration from System.Windows.Forms
  • The WPF-specific API, which uses the Key and ModifierKeys enumerations from System.Windows.Input, and supports global key bindings in XAML.

If you install the library from Nuget, add either the NHotkey.Wpf or the NHotkey.WindowsForms package; the core package will be added automatically.

3 Comments

  1. HK1 says:

    This is exactly what I need right now but am getting error:

    System.Windows.Markup.XamlParseException was unhandled
    HResult=-2146233087
    Message=’Set property ‘NHotkey.Wpf.HotkeyManager.RegisterGlobalHotkey’ threw an exception.’ Line number ’19′ and line position ’61′.

    HResult=-2147023487
    Message=Hot key is already registered. (Exception from HRESULT: 0×80070581)
    Source=NHotkey
    ErrorCode=-2147023487

  2. HK1 says:

    The problem I posted about above is actually two problems.

    1) There is no Unregister method exposed which could be called when the application closes.

    2) The current design doesn’t return true or false on the Register function. Some hot keys might already be in use by other, unrelated applications and we need a way to know if registration fails.

  3. Thomas Levesque says:

    Hi HK1,

    There is a Remove method (declared in HotkeyManagerBase) that you can use to unregister a hotkey you have registered. Just pass the name you used for the registration.

    The current design does tell you if the registration fails, by throwing an exception; IMO this is better than returning true of false, because a boolean doesn’t tell you *why* the registration failed.

Leave a comment

css.php