对话框不响应按键消息
今天给测试程序加了热键和按键响应函数,然而未能按照预期响应。一瞬间有点懵!
查了一下资料发现焦点始终没有到窗口上而是在窗口的按钮上,于是问题就在这儿了,消息被按钮或者父窗口处理了。以下是查到的部分资料,文尾有对window消息深入介绍的链接:
基于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的说明。
目前window过程中对消息的分类以及消息队列进行处理的函数包括:
以下是消息的传递路径
GetMessage与PeekMessage
GetMessage 有消息且该消息不为WM_QUIT,返回TRUE。
有消息且该消息为WM_QUIT,返回FALSE。
没有消息时,挂起该UI线程,控制权交还给系统。
PeekMessage 有消息返回TRUE,如果没有消息返回FALSE;不会阻塞。
是否从消息队列中删除此消息(PM_REMOVE),由函数参数来指定。
关于消息的深入了解可查阅以下链接:
https://blog.csdn.net/shenya1314/article/details/54924685