键盘(热键)自定义类
提供可调节的键盘快捷键,用于您的菜单命令,可直接集成此类。
引言
任何“专业”程序的必备功能是键盘自定义对话框,用户可以在其中为所有菜单命令分配自己选择的热键。这看起来是一项艰巨的任务,至少对我来说是这样,当我考虑将它添加到我的 xplorer² 时。我需要为所有命令准备好脚本结构吗?我必须为每个命令添加文本和描述吗?GUI 的翻译又如何呢?
在 MSDN、CodeProject 和 usenet 组中搜索现成的解决方案——即使是付费的——结果都是徒劳的。使用 Krugle(一个很好的资源)搜索 SourceForge 找到了几个有希望的代码片段,但没有完整的解决方案。
就像您一样,我最终从头开始编写了自己的键盘自定义类。好消息是,它可以轻松地将热键自定义添加到您的 WTL 应用程序中:只需添加一个头文件、一个对话框资源、几行框架创建代码,即可完成。所有命令字符串都从您现有的菜单结构中读取。
在可用性方面,该对话框类似于 VS6 的热键自定义对话框,尽管没有命令类别。您仍然可以为每个命令支持多个加速键。该类负责自己的注册表持久化,甚至允许重置,将所有热键恢复为出厂默认设置(在加速器资源中定义的)。最后,每次更改加速器表时,菜单项上出现的热键助记符都会自动更新。
背景
命令热键存储在加速器表中。大多数程序都有一个在设计时在资源编辑器中创建的固定加速器表,并在框架窗口通过 LoadAccelerators
创建时加载。在 WTL 中,句柄存储在 CFrameWindowImplBase
的成员变量 m_hAccel
中。框架在隐藏加速器的使用方面做得很好。消息循环在每个检索到的消息上调用 TranslateMessage
,如果它碰巧对应于一个加速器键,那么就会分派等效的 WM_COMMAND
,而不是键盘事件。
为了提供可调节的热键,应用程序必须包含:
- 一种动态修改加速器表的方法。有一个名为
CreateAcceleratorTable
的 API,可以使用键/命令信息数组(ACCEL
结构)来实现。 - 一种读取按键组合的方法,使用
CHotKeyCtrl
系统控件。 - 通过
GetKeyNameText
API 提供用户可读的热键名称。
使用这些组件,动态更改热键涉及以下步骤:
- 使用
CopyAcceleratorTable
获取存储在框架m_hAccel
中的当前加速器表的副本。 - 使用提供的
CKeyAssignDlg
根据需要修改表。 - 销毁旧表并创建一个新表,将其设置为
m_hAccel
以立即生效。 - 更新菜单热键助记符(和工具栏工具提示),以反映当前键盘分配。
就是这样!自定义表以 REG_BINARY
格式存储在注册表项中。有关更多详细信息,请参阅源代码,其中演示了一个具有完整热键自定义支持的最小 WTL SDI 应用程序。
使用代码
示例代码使用 Visual Studio 6 构建,使用 WTL v7.1(是的,我有点落后)。我想,使用最新的 VS/WTL 套件构建它应该不难,甚至端口到 MFC 也不难,如果您足够勤奋的话。
要为您的应用程序添加键盘自定义功能,您只需要主头文件 keyAssignDlg.h、对话框 IDD_ACCELEDIT_DLG
以及资源中的一些错误消息字符串。要将其集成到您的项目中,您需要为主框架窗口添加一些成员变量和消息处理程序。
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>, public CMessageFilter, public CIdleHandler { public: //... void CustomAccelerators(); // call after creation to setup accelerators BEGIN_MSG_MAP(CMainFrame) //... COMMAND_ID_HANDLER(ID_CUSTOMIZE_KEYBOARD, OnCustomizeKeys) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) //... END_MSG_MAP() LRESULT OnCustomizeKeys(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); protected: CAccelCombo* m_paCustomKeys; // user modified accelerator table (if any) HACCEL m_hAccelDefault; // original table from resources };
在 WTL 的 Run
处理程序成功创建主框架后,调用 CustomAccelerators
从注册表中加载自定义按键并将它们存储在帮助类 CAccelCombo
中。我们将默认按键备份在资源中的 m_hAccelDefault
中,以防用户从自定义对话框中重置它们。最后,在销毁时,我们将自定义加速器表保存回注册表。有关所有详细信息,请参阅源代码。
关注点
这个对话框类真正巧妙之处在于它填充类别和命令的方式。它重用了主菜单资源中的结构。它会自动导入资源中的所有信息,您无需单独输入命令名称和描述。这并非什么高深技术,但非常方便。
每个顶级菜单都成为一个类别,其所有命令(和子命令)都被扁平化为列表。命令描述取自您在遍历菜单系统时通常出现在状态栏上的文本。这确保了令人愉悦且直接的用户体验。
最后,请注意,使用的 Windows 热键控件并不完美。它会将包含 ENTER、TAB、SPACE、DEL、ESC 或 BACKSPACE 的所有按键组合传递给 DefWindowProc
,这使得这些按键无法用作键盘快捷键。我曾尝试使用 WM_GETDLGCODE
来捕获所有按键,但效果不佳。至少,GetKeyNameText
返回的按键名称是为国际化准备的。
参考文献
历史
- 2007 年 6 月:初次发布。