65.9K
CodeProject 正在变化。 阅读更多。
Home

键盘(热键)自定义类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (9投票s)

2007 年 6 月 29 日

CPOL

4分钟阅读

viewsIcon

59169

downloadIcon

1458

提供可调节的键盘快捷键,用于您的菜单命令,可直接集成此类。

Screenshot - customize.png

引言

任何“专业”程序的必备功能是键盘自定义对话框,用户可以在其中为所有菜单命令分配自己选择的热键。这看起来是一项艰巨的任务,至少对我来说是这样,当我考虑将它添加到我的 xplorer² 时。我需要为所有命令准备好脚本结构吗?我必须为每个命令添加文本和描述吗?GUI 的翻译又如何呢?

在 MSDN、CodeProject 和 usenet 组中搜索现成的解决方案——即使是付费的——结果都是徒劳的。使用 Krugle(一个很好的资源)搜索 SourceForge 找到了几个有希望的代码片段,但没有完整的解决方案。

就像您一样,我最终从头开始编写了自己的键盘自定义类。好消息是,它可以轻松地将热键自定义添加到您的 WTL 应用程序中:只需添加一个头文件、一个对话框资源、几行框架创建代码,即可完成。所有命令字符串都从您现有的菜单结构中读取。

在可用性方面,该对话框类似于 VS6 的热键自定义对话框,尽管没有命令类别。您仍然可以为每个命令支持多个加速键。该类负责自己的注册表持久化,甚至允许重置,将所有热键恢复为出厂默认设置(在加速器资源中定义的)。最后,每次更改加速器表时,菜单项上出现的热键助记符都会自动更新。

背景

命令热键存储在加速器表中。大多数程序都有一个在设计时在资源编辑器中创建的固定加速器表,并在框架窗口通过 LoadAccelerators 创建时加载。在 WTL 中,句柄存储在 CFrameWindowImplBase 的成员变量 m_hAccel 中。框架在隐藏加速器的使用方面做得很好。消息循环在每个检索到的消息上调用 TranslateMessage,如果它碰巧对应于一个加速器键,那么就会分派等效的 WM_COMMAND,而不是键盘事件。

为了提供可调节的热键,应用程序必须包含:

  • 一种动态修改加速器表的方法。有一个名为 CreateAcceleratorTable 的 API,可以使用键/命令信息数组(ACCEL 结构)来实现。
  • 一种读取按键组合的方法,使用 CHotKeyCtrl 系统控件。
  • 通过 GetKeyNameText API 提供用户可读的热键名称。

使用这些组件,动态更改热键涉及以下步骤:

  1. 使用 CopyAcceleratorTable 获取存储在框架 m_hAccel 中的当前加速器表的副本。
  2. 使用提供的 CKeyAssignDlg 根据需要修改表。
  3. 销毁旧表并创建一个新表,将其设置为 m_hAccel 以立即生效。
  4. 更新菜单热键助记符(和工具栏工具提示),以反映当前键盘分配。

就是这样!自定义表以 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 热键控件并不完美。它会将包含 ENTERTABSPACEDELESCBACKSPACE 的所有按键组合传递给 DefWindowProc,这使得这些按键无法用作键盘快捷键。我曾尝试使用 WM_GETDLGCODE 来捕获所有按键,但效果不佳。至少,GetKeyNameText 返回的按键名称是为国际化准备的。

参考文献

历史

  • 2007 年 6 月:初次发布。
© . All rights reserved.