在对话框程序中有时需要响应键盘和鼠标事件,MFC的对话框继承于CWnd窗口类,总结了如下几种可能的处理方式:
1,最直观的想法是重写类中的虚拟响应函数,这些响应函数有:
键盘相关:ON_WM_CHAR、ON_WM_KWEYDOWN、ON_WM_KEYUP等,对应的消息处理函数为:OnChar、OnKeyDown、OnKeyUp等
鼠标相关:ON_WM_MOUSEHWHEEL()、ON_WM_MOUSEMOVE()、ON_WM_KEYDOWN()、ON_WM_LBUTTONDOWN,对应的消息处理函数为:OnMouseHWheel、OnMouseMove等
1).cpp文件中的代码为:
BEGIN_MESSAGE_MAP(CtestDlg, CDialogEx)
ON_WM_LBUTTONDOWN()//mouse left key down
ON_WM_RBUTTONDOWN()//right key down
ON_WM_MOUSEHWHEEL()
ON_WM_MOUSEMOVE()//mouse move
ON_WM_KEYDOWN()//keyboard key down
END_MESSAGE_MAP()
void CtestDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
TRACE("OnLButtonDown\r\n");
//SetFocus();
CDialogEx::OnLButtonDown(nFlags, point);
}
2).h文件中的修改:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
在这种情况下,OnLButtonDown可以响应,但只响应对话框的空白区域,控件区域无法响应;OnKeyDown并不能响应,消息被拦截,于是切换到另一种更直接的方法,怎么处理这种拦截将在下面的方法中叙述。
2,对PreTranslateMessage函数的重载
PreTranslateMessage是消息送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这个函数,通过重载这个函数,我们可以改变MFC的消息控制流程,只有穿过消息队列的消息才受该函数的影响。处理按键的该函数重写为:
1).cpp中的代码
BOOL CtestDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
//判断是否是按键消息
if( pMsg->message == WM_KEYDOWN )
{
//判断具体键
switch( pMsg->wParam )
{
case VK_LEFT://按下左键
TRACE("left\r\n");
break;
case VK_RIGHT://按下右键
TRACE("right\r\n");
break;
case VK_UP://按下上键
TRACE("up\r\n");
//return TRUE;
break;
case VK_DOWN://按下下键
TRACE("down\r\n");
//return TRUE;
break;
default:
//return TRUE;
break;
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
2).h文件
afx_msg BOOL PreTranslateMessage(MSG* pMsg);
需要响应的操作都可以在该函数中操作。
现在回过头,第一种方法中按键不会响应,是因为按键消息被拦截了,此时只需要在PreTranslateMessage中添加消息转发函数即可:
SendMessage(pMsg->message,pMsg->wParam,pMsg->lParam);
但是此时发现界面刷新不顺畅,似乎还是直接在PreTranslateMessage函数中直接处理函数较好,可以直接在该函数中消息分流后直接调用响应函数。
此处介绍下MFC跟该函数相关的消息处理流程:
MFC 中PreTranslateMessage是GetMessage(...)函数的下一级操作,即GetMessage(...)从消息队列中获取消息后,交由PreTranslateMessage()处理,若其返回FALSE则再交给TranslateMessage和 DispatchMessage处理(进入WindowProc);
如果用SendMessage, 则消息直接交到WindowProc处理,所以GetMessage不会取得SendMessage的消息,当然PreTranslateMessage也就不会被调用。 [Page]
如果用PostMessage,则消息进入消息队列,由GetMessage取得,PreTranslateMessage就有机会进行处理。
3,使用低级钩子函数
以鼠标响应为例,鼠标使用WH_MOUSE_LL,对应的函数为LowLevelMouseProc:
//
// 全局变量和全局函数定义
//
HHOOK hhookMs = NULL;
LRESULT CALLBACK LowLevelMouseProc (INT nCode, WPARAM wParam, LPARAM lParam);
BOOL UninstallKbHook();
BOOL InstallKbHook();
//
// 安装鼠标Hook
//
void CTestMFCDlg::OnButton1()
{
InstallKbHook();
}
//
// 卸掉键盘Hook
//
void CTestMFCDlg::OnButton2()
{
UninstallKbHook();
}
LRESULT CALLBACK LowLevelMouseProc (INT nCode, WPARAM wParam, LPARAM lParam)
{
MSLLHOOKSTRUCT *pkbhs = (MSLLHOOKSTRUCT *)lParam;
char strMsg[100] = {0};
switch (nCode)
{
case HC_ACTION:
{
//鼠标移动
if (wParam == WM_MOUSEMOVE)
{
sprintf(strMsg, "WM_MOUSEMOVE: x= %d, y= %d\n", pkbhs->pt.x, pkbhs->pt.y);
OutputDebugString(strMsg);
}
//鼠标左击
if(wParam == WM_LBUTTONDOWN)
{
sprintf(strMsg, "WM_LBUTTONDOWN: x= %d, y= %d\n", pkbhs->pt.x, pkbhs->pt.y);
OutputDebugString(strMsg);
}
// //滚轮事件
// if (wParam == WM_MOUSEWHEEL)
// {
// sprintf(strMsg, "WM_MOUSEWHEEL: %d\n", HIWORD(pkbhs->mouseData));
// OutputDebugString(strMsg);
// }
}
default:
break;
}
return CallNextHookEx (NULL, nCode, wParam, lParam);
}
BOOL InstallKbHook( )
{
if (hhookMs )
UninstallKbHook();
hhookMs = SetWindowsHookEx(WH_MOUSE_LL,
(HOOKPROC)LowLevelMouseProc, AfxGetApp()->m_hInstance, NULL);
return(hhookMs != NULL);
}
BOOL UninstallKbHook()
{
BOOL fOk = FALSE;
if (hhookMs ) {
fOk = UnhookWindowsHookEx(hhookMs );
hhookMs = NULL;
}
return(fOk);
}
鼠标低级钩子是一个全局的,只要安装钩子成功,在整个系统中都是有效的。该方法参考了http://blog.csdn.net/dijkstar/article/details/9007167