为WPF添加快捷键的简单代码
为WPF添加快捷键的简单代码
引言
我在开发一个需要快捷键
支持的应用程序时,发现技术上可以实现,但没有看到非常简洁的解决方案,所以我自己编写了一个。这段代码提供了一种非常简单的方法,可以将一段代码片段附加/分离到WPF应用程序中的快捷键
上。
背景
快捷键
是早期Windows版本的遗留物,因此我们需要使用互操作功能来访问它。所有这些都抽象在HotKeyHelper
类中,但是关键在于获取WPF应用程序主窗口的遗留窗口句柄(hwnd
)。hwnd
在构造时不可用,因此我们需要挂钩一个在已知句柄时发生的事件。
Using the Code
快捷键
代码被实现为“发射后即忘”,因此您可以添加快捷键而无需显式删除它,但如果需要也可以删除。正如我在背景中提到的,有必要在窗口具有有效的hwnd
我们可以访问时创建HotKeyHelper
。OnSourceInitialized
是一个不错的选择。
HotKeyHelper _hotKeys;
int _throwConfettiKeyId;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_hotKeys = new HotKeyHelper(this);
// Assign Ctrl-Alt-C to our ThrowConfetti() method.
_throwConfettiKeyId = _hotKeys.ListenForHotKey(
Key.C,
HotKeyModifiers.Alt | HotKeyModifiers.Control,
() => { this.ThrowConfetti(); } // put any code you want here
);
}
// Key removal is handled implicitly, but you can explicitly remove
// a key like this
void DoSomeStuffLater()
{
_hotKeys.StopListeningForHotKey(_throwConfettiKeyId);
}
这是辅助类的实际代码
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
{
/// <summary>
/// Simpler way to expose key modifiers
/// </summary>
[Flags]
public enum HotKeyModifiers
{
None = 0,
Alt = 1, // MOD_ALT
Control = 2, // MOD_CONTROL
Shift = 4, // MOD_SHIFT
WindowsKey = 8, // MOD_WIN
}
/// <summary>
/// A helpful interface for abstracting this
/// </summary>
public interface IHotKeyTool : IDisposable
{
int ListenForHotKey(System.Windows.Input.Key key, HotKeyModifiers modifiers, Action keyAction);
void StopListeningForHotKey(int id);
}
// --------------------------------------------------------------------------
/// <summary>
/// A nice generic class to register multiple hotkeys for your app
/// </summary>
// --------------------------------------------------------------------------
public class HotKeyHelper : IHotKeyTool
{
// Required interop declarations for working with hotkeys
[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;
/// <summary>
/// The unique ID to receive hotkey messages
/// </summary>
int _idSeed;
/// <summary>
/// Handle to the window listening to hotkeys
/// </summary>
private IntPtr _windowHandle;
/// <summary>
/// Remember what to do with the hot keys
/// </summary>
Dictionary<int, Action> _hotKeyActions = new Dictionary<int, Action>();
// --------------------------------------------------------------------------
/// <summary>
/// ctor
/// </summary>
// --------------------------------------------------------------------------
public HotKeyHelper(Window handlerWindow)
{
// Create a unique Id seed
_idSeed = (int)((DateTime.Now.Ticks % 0x60000000) + 0x10000000);
// Set up the hook to listen for hot keys
_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);
}
// --------------------------------------------------------------------------
/// <summary>
/// Listen generally for hotkeys and route to the assigned action
/// </summary>
// --------------------------------------------------------------------------
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;
}
// --------------------------------------------------------------------------
/// <summary>
/// Assign a key to a specific action. Returns an id to allow you to stop
/// listening to this key.
/// </summary>
// --------------------------------------------------------------------------
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;
}
// --------------------------------------------------------------------------
/// <summary>
/// Stop listening for hotkeys.
/// hotkeyId The id returned from ListenForHotKey
/// </summary>
// --------------------------------------------------------------------------
public void StopListeningForHotKey(int hotkeyId)
{
UnregisterHotKey(_windowHandle, hotkeyId);
}
// --------------------------------------------------------------------------
/// <summary>
/// Dispose - automatically clean up the hotkey assignments
/// </summary>
// --------------------------------------------------------------------------
public void Dispose()
{
foreach(var hotkeyId in _hotKeyActions.Keys)
{
StopListeningForHotKey(hotkeyId);
}
}
}
}
历史
- 2018年11月13日 - 初始版本