当前位置:C++技术网 > 资讯 > MFC中响应按键消息WM_KEYDOWN不响应的原因

MFC中响应按键消息WM_KEYDOWN不响应的原因

更新时间:2016-01-17 22:52:42浏览次数:1+次

基于MFC对话框的应用程序在响应按键消息和热键方面都力不从心,CDialog类的消息循环中去掉了TranslateAccelerator函数,因此不能响应热键;同时由于对话框上可能有很多控件,且默认情况下这些子窗口已经截获了焦点,因此键盘消息已经被控件捕获了;同时为了实现控件焦点切换和对话框默认行为,  VK_TAB、VK_LEFT、VK_RIGHT、VK_UP、VK_DOWN、 VK_RETURN、VK_ESCAPE 等键已经被截获处理,因此对话框没有控件时仍然不能完全响应按键消息。对话框不能收到WM_KEYDOW,关键是基类的CDialog::PreTranslateMessage(pMsg)做了手脚,所以你必须在它做手脚之前做自己想做的事。
在对话框程序中,我们经常是利用对话框上的子控件进行命令响应来处理一些事件。如果我们想要让对话框(子控件的父窗口)类来响应我们的按键消息,我们可以通过ClassWizard对WM_KEYDOWN消息进行响应,当程序运行后,我们按下键盘上的按键,但对话框不会有任何的反应。这是因为在对话框程序中,某些特定的消息,例如按键消息,它们被Windows内部的对话框过程处理了(即在基类中完成了处理,有兴趣的读者可以查看MFC的源代码),或者被发送给子控件进行处理,所以我们在对话框类中就捕获不到按键的消息了。
有次,我在一个对话框(有水平滑块与垂直滑块)上面的组合框控件(或者是下拉列表框控件)中浏览控件项,但是主对话框的滑块并没有移动。我想这也能够用上述理由解释。
既然我们知道了这个处理的过程,我们就可以找到底层处理按键消息的函数,然后在子类中重载它,就可以在对话框程序中处理按键消息了。
我查了资料,有人用ProcessMessageFilter来处理,PreTranslateMessage,ProcessMessageFilter都是虚函数,你重载就行。但是,我在VS10里没有找到ProcessMessageFilter虚函数,估计在VC6.0里面有,我想ProcessMessageFilter可能已经过时了,因此,不推荐大家使用这个函数,而是PreTranslateMessage函数更合适。
与按键相关的消息大概有4个:WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP,需要说明的包括以下几点:
    1、KEY与SYSKEY消息的区别在于:如果某个按键动作的同时,ALT键或F10键被按下,则发送SYSKEY消息,否则发送KEY消息;
    2、某个按键动作依次产生WM_KEYDOWN和WM_KEYUP消息;
    3、一个按键一直按着不放,会按一定间隔时间不断发送WM_KEYDOWN消息;
    4、单键动作最好响应WM_KEYUP,组合键动作响应WM_KEYDOWN或WM_SYSKEYDOWN;

    关于几个按键消息的具体解释,请参考MSDN。

BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)   
{
if(pMsg->message == WM_KEYUP) 
{
// 响应keyup消息
if(pMsg->wParam == VK_RETURN)
{
// 回车
}
}
return CDialog::PreTranslateMessage(pMsg); 
}

    那么怎样判断组合键呢?使用GetKeyState函数。示例代码如下:
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)   
{
if(pMsg->message == WM_KEYDOWN)
{
// 组合键响应keydown消息
if( pMsg->wParam == VK_SPACE && (GetKeyState(VK_SHIFT) & 0x8000))
{
// 空格 + Shift
}
}
else if(pMsg->message == WM_SYSKEYDOWN)
{
// Alt组合键响应syskeydown消息
if( pMsg->wParam == 'A' && (HIWORD(pMsg->lParam) & KF_ALTDOWN))
{
// A + Alt
}
}

return CDialog::PreTranslateMessage(pMsg); 
}

另外,还有GetAsyncKeyState和GetKeyboardState等类似函数,涉及到逻辑按键和物理按键值等问题,大家可以参考MSDN的说明。