Introduction
I was working on an app that needed
hotkey
support and found out technically how to do it, but did not see any very clean solutions, so I wrote my own. This code provides a dead-simple way to attach/detach a snippet of code to a
hotkey
in a WPF app.
Background
Hotkey
s are a relic from early versions of Windows, so we need to use interop functionality to get to it. All of this is abstracted in the
HotKeyHelper
class, however the main trick is to get the relic window handle (
hwnd
) of the main window of your WPF application. The
hwnd
is not available at construction time, so we need to hook an event that occurs at a point where the handle is known.
Using the Code
The
hotkey
code is implemented to be "fire and forget", so you can add the key without having to explicitly remove it, but that is available if needed. As I mentioned in the background, it is necessary to create the
HotKeyHelper
at a time when the window has a valid
hwnd
we can access.
OnSourceInitialized
is a good place to do this:
HotKeyHelper _hotKeys;
int
_throwConfettiKeyId;
protected
override
void
OnSourceInitialized(EventArgs e)
base
.OnSourceInitialized(e);
_hotKeys =
new
HotKeyHelper(
this
);
_throwConfettiKeyId = _hotKeys.ListenForHotKey(
Key.C,
HotKeyModifiers.Alt | HotKeyModifiers.Control,
() => {
this
.ThrowConfetti(); }
void
DoSomeStuffLater()
_hotKeys.StopListeningForHotKey(_throwConfettiKeyId);
Here is the actual code for the helper class:
using
System;
using
System.Collections.Generic;
using
System.Runtime.InteropServices;
using
System.Windows;
using
System.Windows.Forms;
using
System.Windows.Input;
using
System.Windows.Interop;
namespace
HotKeyTools
[Flags]
public
enum
HotKeyModifiers
None =
0
,
Alt =
1
,
Control =
2
,
Shift =
4
,
WindowsKey =
8
,
public
interface
IHotKeyTool : IDisposable
int
ListenForHotKey(System.Windows.Input.Key key, HotKeyModifiers modifiers, Action keyAction);
void
StopListeningForHotKey(
int
id);
public
class
HotKeyHelper : IHotKeyTool
[DllImport(
"
user32"
, SetLastError =
true
)]
[return: MarshalAs(UnmanagedType.Bool)]
protected
static
extern
bool
RegisterHotKey(
IntPtr
hwnd,
int
id,
uint
fsModifiers,
uint
vk);
[DllImport(
"
user32"
, SetLastError =
true
)]
protected
static
extern
int
UnregisterHotKey(
IntPtr
hwnd,
int
id);
protected
const
int
WM_HOTKEY =
0x312
;
int
_idSeed;
private
IntPtr
_windowHandle;
Dictionary<int, Action> _hotKeyActions =
new
Dictionary<int, Action>();
public
HotKeyHelper(Window handlerWindow)
_idSeed = (
int
)((DateTime.Now.Ticks %
0x60000000
) +
0x10000000
);
_windowHandle =
new
WindowInteropHelper(handlerWindow).Handle;
if
(_windowHandle ==
null
)
throw
new
ApplicationException(
"
Cannot find window handle.
Try calling this on or after OnSourceInitialized()"
);
var
source = HwndSource.FromHwnd(_windowHandle);
source.AddHook(HwndHook);
private
IntPtr
HwndHook(
IntPtr
hwnd,
int
msg,
IntPtr
wParam,
IntPtr
lParam,
ref
bool
handled)
if
(msg == WM_HOTKEY)
var
hotkeyId = wParam.ToInt32();
if
(_hotKeyActions.ContainsKey(hotkeyId))
_hotKeyActions[hotkeyId]();
handled =
true
;
return
IntPtr
.Zero;
public
int
ListenForHotKey
(System.Windows.Input.Key key, HotKeyModifiers modifiers, Action doThis)
var
formsKey = (Keys)KeyInterop.VirtualKeyFromKey(key);
var
hotkeyId = _idSeed++;
_hotKeyActions[hotkeyId] = doThis;
RegisterHotKey(_windowHandle, hotkeyId, (
uint
)modifiers, (
uint
)formsKey);
return
hotkeyId;
public
void
StopListeningForHotKey(
int
hotkeyId)
UnregisterHotKey(_windowHandle, hotkeyId);
public
void
Dispose()
foreach
(
var
hotkeyId
in
_hotKeyActions.Keys)
StopListeningForHotKey(hotkeyId);
History
13
th
November, 2018 - Initial version
nice Idea but there are already key bindings in WPF which results in less code. Or is there any other benefit in your solution?
Take a look here for using key bindings in wpf:
c# - Create Key binding in WPF
[
^
]
So you're not relying on those win32 calls.
Sign in
·
View Thread
Some keys are not working from VS2017 like F12, if change functionality to F5 work OK, I think is not the single key!
magsoft
Sign in
·
View Thread
I think that in some cases different apps will try to register the same hotkey. Not much we can do about this other than try to find unique hot key combinations.
Sign in
·
View Thread