Search code examples
c#wpfhotkeysregisterhotkeyglobal-hotkey

WPF NHotkey global hotkey only runs when window is focused


On my C# WPF project that runs in the background from the system tray I installed a package called NHotkey to try and turn my keyboard hotkey into a global hotkey that works even if the window isn't up: https://github.com/thomaslevesque/NHotkey

And I added it through XAML:

<Window.Resources>
    <RoutedUICommand x:Key="CtrLeft" Text="Left" />
</Window.Resources>
<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Windows+Alt+Left" Command="{StaticResource CtrLeft}" HotkeyManager.RegisterGlobalHotkey="True" />
</Window.InputBindings>
<Window.CommandBindings>
    <CommandBinding Command="{StaticResource CtrLeft}" Executed="Test" />
</Window.CommandBindings>

Unfortunately it still only executes the "Test" function if the window is open and focused.

Using the NHotkey readme I also tried implementing the hotkey programmatically and ended up with the same result. NHotkey is a very popular package and there don't appear to be any related open issues with it so I suspect I'm doing something wrong.

I've also tried other global hotkey packages which either result in the same thing, or in the application crashing and giving an error about the hotkey already being registered in Event Viewer. In those instances I've tried obscure key combinations that wouldn't ever be in use by anything and still get an error in Event Viewer about it already being registered. An example of one of the packages I tried with this outcome is HotkeyUtility: https://github.com/giosali/HotkeyUtility

These results are on a fresh test virtual machine with no third party software. Tried on both Windows 10 and 11.

A global hotkey would be very useful for my application and any help getting there would be very much appreciated. Any ideas? Thanks.


Solution

  • After messing around with it for a bit I discovered doing this programmatically works you just have to be sure to register the hotkey with HotkeyManager.Current.AddOrReplace(...); inside Window_SourceInitialized. I originally had it in the main function.