[TOC]
纳闷啊,这基础的东西没找到写的比较清楚了,大家藏私?把自己用到的整理了一下。
MFC捕捉键盘组合键事件
PreTranslateMessage 捕获消息
在MFC中捕捉按键状态,我是通过实现PreTranslateMessage来实现的。如下通过重写这个方法:
头文件
class CXxxDialog : public CDialogEx {
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
};
源文件
BOOL CXxxDialog::PreTranslateMessage(MSG* pMsg) {
/* 在这里搞事情 */
return FALSE;
}
参数MSG
主要关心的就是这个参数 MSG中的message和wParam参数。
typedef struct tagMSG { // msg
HWND hwnd; /* 接收消息的窗口 */
UINT message; /* 指示按键类型,比如是WM_KEYDOWN(按键按下),WM_SYSKEYDOWN(系统按键按下),WM_KEYUP(按键松开)等等,详细看参考。 *
WPARAM wParam; /* 就是按下的按键值,比如我们按下A,那它的值就是'A',我们按下9,它的按键值就是'9',按下F5就是VK_F5 详细看参考。 */
LPARAM lParam; /* 根据message的不同指示包含不同的内容,这个详细看某种类型,比如WM_KEYDOWN的详细介绍,里面就有说每个位是什么用。*/
DWORD time; /* 指定消息已传递的时间 */
POINT pt; /* 当前鼠标的坐标 */
} MSG;
返回值
如果返回TRUE则表示该消息已被处理了,不会再被传播下去。
如果返回FALSE则该消息会被继续传播下去(传递给TranslateMessage和DispatchMessage)。
单键触发事件demo
BOOL CXxxDialog::PreTranslateMessage(MSG* pMsg) {
if (WM_KEYDOWN == pMsg->message) { /* 键盘按下事件 */
if ('F' == pMsg->wParam) {
MessageBox("press F");
return TRUE; /* 如上所说,如果认为该按键事件已处理了,就返回true,不需要后面再传播了。 */
}
if ('U' == pMsg->wParam) {
MessageBox("press U");
return FALSE; /* 如上所说,如果认为该按键事件没处理了,就返回false,事件继续传播 */
}
if ('C' == pMsg->wParam) {
MessageBox("press C");
return TRUE;
}
if ('K' == pMsg->wParam) {
MessageBox("press K");
return TRUE;
}
if (VK_F5 == pMsg->wParam) {
MessageBox("press F5");
return TRUE;
}
if (VK_RETURN == pMsg->wParam) {
MessageBox("press enter");
return TRUE;
}
}
#if 0
/* 当关注多个事件的时候用switch比较好 */
switch (pMsg->message) {
case WM_KEYDOWN: {
break;
}
case WM_SYSKEYDOWN: {
break;
}
default: {
}
}
#endif
return CDialogEx::PreTranslateMessage(pMsg);
}
GetKeyState() 获取CTRL/ALT/SHIFT按键的状态
前面是单键的,不过我们玩快捷键一般是组合键,如ctrl + ?之类的。要玩组合键,那就要知道CTRL/ALT/SHIFT这几个按键的状态,通过GetKeyState可以获取到这几个键的状态。
SHORT GetKeyState(
int nVirtKey
);
参数 nVirtKey
根据文档此处支持下面几类按键的状态获取,包括该类按键整体(如VK_CONTROL)状态,左按键状态(如VK_LCONTROL),右按键状态(如VK_RCONTROL)的单独获取。
VK_CONTROL
VK_RCONTROL
VK_LCONTROL
VK_RMENU
VK_LMENU
VK_MENU /* 这个是ALT键 */
VK_LSHIFT
VK_RSHIFT
VK_SHIFT
在Windows CE 1.0 到 2.01,,GetKeyState仅用于检测capital(大小写键)是否被激活,从WindowsCE 2.10之后,VK_NUMLOCK(数字锁键)也可以被检测了。
返回值
返回值是一个SHORT值:
- 最高位为1时说明该键被按下,为0时则没有按下
- 最低位为1说明该键被激活(老外叫toggle,我就叫激活了),以caps键为例(就是切换大小写的键),被激活时为大写(键盘大写灯亮了),没有激活时为小写
如下:判断当前是大写还是小写
if (0X01 & GetKeyState(VK_CAPITAL)) { /* 激活状态,当前为大写 */
MessageBox("it's upper-case now");
} else { /* 激活状态,当前为小写 */
MessageBox("it's lower-case now");
}
在按下caps键的时候判断是切换为大写或者小写
if (0X8000 & GetKeyState(VK_CAPITAL)) { /* 按下caps键 */
if (0X01 & GetKeyState(VK_CAPITAL)) { /* 切换为大写 */
MessageBox("switch to upper-case");
} else { /* 切换为小写 */
MessageBox("switch to lower-case");
}
}
同样的对于VK_CONTROL(万万没想到ctrl/alt还有激活状态,因为平常没用到吧~)以及VK_SHIFT也是同样操作的。
组合键demo1 - 愉快组合键
现在我们就可以愉快的玩组合键了
BOOL CXxxDialog::PreTranslateMessage(MSG* pMsg) {
if (WM_KEYDOWN == pMsg->message) { /* 键盘按下事件 */
if ('F' == pMsg->wParam && (0X8000 & GetKeyState(VK_CAPITAL))) {
MessageBox("press CTRL + F"); /* 捕获CTRL + F事件*/
return TRUE;
}
if ('U' == pMsg->wParam && (0X8000 & GetKeyState(VK_CAPITAL)) {
MessageBox("press CTRL + U"); /* 捕获CTRL + U事件*/
return FALSE;
}
}
return FALSE;
}
组合键demo2 - 怎么 CTRL + ALT + ? 也触发了 CTRL + ?事件?
按照组合键demo1的方式,有个问题,下面几种按键方式都能触发弹框显示press CTRL + F
,这好吗?这不好。(如果你只关注CTRL+?的键盘事件而不关心CTRL+ALT+?的键盘事件,其实也无伤大雅,不过软件看起来还是专业点的好 ) (~。~)
CTRL+F
CTRL + SHIFT + F
CTRL + ALT + F
CTRL+ SHIFT + ALT + F
头文件
class CXxxDialog : public CDialogEx {
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
bool isAltDown(MSG* pMsg);
bool isCtrlDown();
bool isShiftDown();
bool isAltDown();
/* 有了上面3个基础的方法,就可以根据需要进行扩展,如下面这个我只要ctrl + ?的按键 */
bool isCtrlWith(MSG* pMsg, const char * cKey);
};
源文件
bool CXxxDialog::isAltDown(MSG* pMsg) { return (pMsg->lParam & 0x20000000); }
bool CXxxDialog::isCtrlDown() { return (GetKeyState(VK_CONTROL) & 0X8000); }
bool CXxxDialog::isShiftDown() { return (GetKeyState(VK_SHIFT) & 0X8000); }
bool CXxxDialog::isAltDown() { return (GetKeyState(VK_MENU) & 0X8000); }
bool isCtrlWith(MSG* pMsg, const char * cKey) {
return (cKey == pMsg->wParam && isCtrlDown() && !isAltDown() && !isShiftDown());
}
BOOL CXxxDialog::PreTranslateMessage(MSG* pMsg) {
if (pMsg->message == WM_KEYDOWN) {
if (isCtrlWith(pMsg, 'F')) {
MessageBox("press ctrl + F"); /* 好了现在就只有CTRL + F能够触发这里了 */
return TRUE;
}
if (isCtrlWith(pMsg, 'U')) {
MessageBox("press ctrl + U"); /* 好了现在就只有CTRL + U能够触发这里了 */
return TRUE;
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
关于bool isAltDown(MSG* pMsg)方法
在上面这个例子中可以看到有两个判断是否按下ALT键的方法,这个是通过pMsg->lParam & 0x20000000
获取的,另一个是使用GetKeyState获取的。
GetKeyState()获取的就不赘述了,网络上我看到有人是通过这种方法获取到Alt的状态,MSDN文档中说了这个位(从右往左数第29位)是 context code,这个位为1就是ALT按下,为0则反之。
The context code indicates whether the ALT key was down when the keystroke message was generated. The code is 1 if the ALT key was down and 0 if it was up.
/* lParam 32位
* 2 0 0 0 0 0 0 0
* 0010 0000 0000 0000 0000 0000 0000 0000
*/
这就是为什么通过这个,能够取到alt的状态。
bool CXxxDialog::isAltDown(MSG* pMsg) { return (pMsg->lParam & 0x20000000); }
组合键demo3 - 怎么捕捉不了 ALT+ ? 事件?
当尝试捕捉 ALT + ? 的快捷键的时候,发现在WM_KEYDOWN中抓不到了,这个时候就需要了解一下WM_KEYDOWN和WM_SYSKEYDOWN的区别。
在windows中ALT被认为是系统菜单键(我猜想这就是为啥它的宏定义是VK_MENU而不是VK_ALT,我刚开始还以为VK_MENU是那个和右键一样功能的菜单键),如我们切换程序使用 ALT + TAB。 如果想要使用 ALT + ? 的快捷键的时候,要捕捉message为WM_SYSKEYDOWN的事件。
如下:(头文件就不贴了,加个isAltWith()方法)
bool CXxxDialog::isAltWith(MSG* pMsg, const char * cKey) {
return (cKey == pMsg->wParam && !isCtrlDown() && isAltDown() && !isShiftDown());
}
BOOL CXxxDialog::PreTranslateMessage(MSG* pMsg) {
switch (pMsg->message) {}
caes WM_KEYDOWN: {
if (isCtrlWith(pMsg, 'F')) {
MessageBox("press ctrl + F");
return TRUE;
}
if (isCtrlWith(pMsg, 'U')) {
MessageBox("press ctrl + U");
return TRUE;
}
break;
}
case WM_SYSKEYDOWN: { /* 捕捉ATL + 事件*/
if (isAltWith(pMsg, 'C')) {
AlertInfo("system alt + C");
return TRUE;
}
if (isAltWith(pMsg, 'K')) {
AlertInfo("system alt + K");
return TRUE;
}
break;
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}