《Windows API每日一练》9.3.1 快捷键

为什么使用快捷键

你可能会问:为什么我应该使用快捷键?为什么我不能捕捉WM_KEYDOWN或 WM_CHAR消息并自己复制菜单功能?它有什么优点?对一个单窗口程序,你当然可以捕捉键盘消息。但是使用快捷键的一个简单优点是,你不需要重写菜单和快捷键处理逻辑。

对有多个窗口和窗口过程的应用程序,快捷键变得十分重要。正如我们所见,对拥有当前输入焦点的窗口,Windows会将键盘消息发送给它的窗口过程。然而,对快捷键而言,Windows会将WM_COMMAND消息发送给一个句柄在Windows函数 TranslateAccelerator中指定的窗口程序。通常,这会是你的主窗口,亦即拥有菜单的那个窗口,这意味着响应快捷键的逻辑不需要被复制到每一个窗口过程。

如果你使用非模态对话框(在第十章讨论)或在主窗口的客户区内使用子窗口,这个优 点就变得极其重要。如果给多个窗口定义了同一个快捷键,只需在其中一个窗口包含这个逻辑即可。子窗口不会接收到来自快捷键的WM_COMMAND消息。

指定加速键的一些规则

理论上,你可以使用几乎任何虚拟键或字符键与Shift键、Ctrl键或Alt键的组合来定义快捷键。然而,你应该试图和其他应用程序保持一致,以避免干扰Windows对键盘的使用。你应该避免使用Tab、回车键、Esc和空格键作为快捷键,因为它们通常保留给系统功能。

快捷键最通常的用途是用于程序中Edit菜单的菜单项。对这些菜单项推荐使用的快捷键在Windows 3.0和Windows 3.1之间有变化,因此同时支持新旧两种快捷键很常见,如下表所示:

功能

旧快捷键

新快捷键

Undo(撤销)

Alt+Backspace

Ctrl+Z

Cut(剪切)

Shilft+Del

Ctrl+X

Copy(复制)

Ctrl+Ins

Ctrl+C

Paste(粘贴)

Shilft+Ins

Ctrl+V

Delete或Clear(删除)

Del

Del

另一个很常见的快捷键是F1功能键,它用来激活帮助。应该避免使用F4、F5和F6 键,因为它们经常被多文档界面(Multiple Document Interface, MDI)程序用作特殊功能,我们会在第十八章中讨论MDI。

加速键表

你可以在VS中定义快捷键表。为了方便在你的程序中加载快捷键表,可以使它与应用程序同名(这也适用于菜单和图标)。

每个快捷键有一个ID和一个击键组合,这些你可以在Accelerator Properties对话框中 定义。如果你已经定义了菜单,菜单ID会存在于组合框中,你不需要重新输入它们。

快捷键可以是虚拟键代码或ASCII字符与Shift、Ctrl或Alt键的组合。你可以在字母前加A来指定ASCII字符与Ctrl键的组合。你也可以从组合框中选择虚拟键代码。

在为菜单项定义快捷键时,应当在菜单文本中包含相应的键组合。Tab字符(\t)能将文本和快捷键分开,这样快捷键会排列在第二列。为了在菜单中标识快捷键,可以使用文字Ctrl、Shift或Alt,后面跟着一个加号和键名(例如Shift+F6或Ctrl+F6)。

加载快捷键表

在应用程序中,应使用LoadAccelerators函数来把快捷键表加载到内存并获得它的句柄。LoadAccelerators 语句和 Loadlcon、LoadCursor 及 LoadMenu 语句很相似。

首先用类型HANDLE定义一个快捷键表的句柄:

HANDLE hAccel;

然后加载快捷键表:

hAccel = LoadAccelerators (hInstance, TEXT ("MyAccelerators")) ;

和图标、鼠标指针及菜单一样,你也可以用数字来命名加速键表,然后在LoadAccelerators 中通过MAKEINTRESOURCE宏来使用该数字,或者将数字用引号括起来并加上前缀#字符。

翻译按键

我们现在要改动三行代码,这三行代码是本书中迄今为止对所有Windows程序都通用 的。这三行代码就是标准消息循环:

while (GetMessage (&msg, NULL, 0, 0))

{

       TranslateMessage (&msg) ;

       DispatchMessage (&msg) ;

}

下面显示了我们如何改变它来使用快捷键:

while (GetMessage (&msg, NULL, 0, 0))

{

       if (!TranslateAccelerator (hwnd, hAccel, &msg))

       {

              TranslateMessage (&msg) ;

              DispatchMessage (&msg) ;

       }

}

Translate Accelerator函数确定保存在msg消息结构中的消息是否是键盘消息。如果是,该函数在快捷键表中寻找句柄为hAccel的匹配值。如果找到匹配值,它会调用句柄为hwnd 的窗口过程。如果快捷键ID对应系统菜单的一个菜单项,则相应消息为 WM_SYSCOMMAND;否则,消息是 WM_COMMAND。

当TranslateAccelerator返回时,如果消息被翻译过(并且已被发送给窗口过程),则返回值为非零值,否则返回值为零。如果TranslateAccelerator返回非零值,你就不应该再调用 TranslateMessage 和 DispatchMessage,而是应该返回 GetMessage 循环。

TranslateAccelerator中的hwnd参数看起来不太合适,因为消息循环中的三个函数都不需要它。并且,消息结构自身(结构变量msg)有一个名字为hwnd的成员,它也是一个窗口句柄。

这个函数之所以有些不同的原因是:msg结构的字段是由GetMessage调用来填充的。 当GetMessage的第二个参数是NULL时,它检索属于该应用程序的所有窗口的消息。当 GetMessage返回时,msg结构的hwnd成员是将会得到该消息的窗口句柄。然而,当 TranslateAccelerator 将键盘消息翻译成 WM_COMMAND 或 WM_SYSCOMMAND 消息时, 它将msg.hwnd窗口句柄替换成该函数第一个参数所指定的窗口句柄。于是Windows会将所有快捷键消息发送给同一个窗口,即使程序中另外一个窗口当前拥有输入焦点。当模态对话框或消息框拥有输入焦点时,TranslateAccelerator不翻译键盘消息,因为这些窗口的消息不通过程序的消息循环。

某些情况下,当应用程序中另一个窗口(比如非模态对话框)拥有输入焦点时,你并不想翻译键盘消息。如何处理这种情况将在第十一章介绍。

接收加速键消息

当一个快捷键对应系统菜单的一个菜单项时,TranslateAccelerator会向窗口过程发送一条WM_SYSCOMMAND消息;否则,TranslateAccelerator向窗口过程发送一条 WM_COMMAND消息。下表列出了对于快捷键、菜单命令和子窗口控件你会接收到的WM_COMMAND消息的类型:

快捷键

菜单

控件

LOWORD(wParam)

快捷键ID

菜单ID

控件ID

HIWORD(wParam)

1

0

通知码

lParam

0

0

子窗口句柄

如果快捷键对应某个菜单项,那么窗口过程还会接收到WM_INITMENU,M_INITMENUPOPUP和WM_MENUSELECT消息,就像菜单项被选择了一样。在处理 WM_INITMENUPOPUP时,程序通常可以启用或禁用弹出菜单的菜单项。使用快捷键时,你仍然拥有这种机制。当一个快捷键对应一个禁用或变灰的菜单项时, TranslateAccelerator 不会向窗口过程发送 WM_COMMAND 或 WM_SYSCOMMAND 消息。

如果当前窗口被最小化,对于映射到启用的系统菜单项的快捷键, TranslateAccelerator 将向窗口过程发送 WM_S YSCOMMAND 消息而不是 WM_COMMAND 消息。对于没有映射到任何菜单项的快捷键,TranslateAccelerator也会向窗口过程发送 WM_COMMAND 消息。

9.3.2 第62练:带有菜单和快捷键的简单记事本程序

/*------------------------------------------------------------------------

 062 WIN32 API 每日一练

     第62个例子POPPAD2.C:带有菜单和加速键的简单记事本程序

     TranslateAccelerator函数

     LoadAccelerators函数

     自定义函数AskConfirmation函数

     WM_INITMENUPOPUP消息

     EnableMenuItem函数

     IsClipboardFormatAvailable 函数

     WM_QUERYENDSESSION消息

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <windows.h>

#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值