|
在使用windows程序的时候,我们用快捷键来使操作更加方便。比如在一个MDI程序中,不管当前输入焦点(接收键盘消息)在哪里,在view中也好,在edit中也好,按下快捷键,对应的消息响应总是会被调用,给我们的感觉,好像accelerator keys和当前focus没有关系。
实际上,在msdn中这样解释: The TranslateAccelerator function processes accelerator keys for menu commands. The function translates a WM_KEYDOWN or WM_SYSKEYDOWN message to a WM_COMMAND or WM_SYSCOMMAND message
也就是说它是从WM_KEYDOWN开始的,接收按键消息的,必然是当前有输入focus的窗口才对,但是为什么在MDI中,我们添加的快捷键和focus看上去没有关系呢? 实际上,是MFC进行了操作,使得accelerator key与当前focus没有关系,只要你在MDI的main frame中导入快捷键即可。MFC是怎么实现的呢? MFC在pump message的时候,会调用PreTranslateMessage //in BOOL CWinThread::PumpMessage() in THRDCORE.CPP if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur); }
而PreTranslateMessage会调用CWnd::WalkPreTranslateTree(),一直上溯到main window,也就是mdi的main frame。 if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg)) return TRUE;
而CWnd::WalkPreTranslateTree会从当前得到消息的focus窗口,一直找parent,找到在某个parent中快捷键消息被响应为止。 BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg) { ASSERT(hWndStop == NULL || ::IsWindow(hWndStop)); ASSERT(pMsg != NULL); // walk from the target window up to the hWndStop window checking // if any window wants to translate this message for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) { CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); if (pWnd != NULL) { // target window is a C++ window if (pWnd->PreTranslateMessage(pMsg)) return TRUE; // trapped by target window (eg: accelerators) } // got to hWndStop window without interest if (hWnd == hWndStop) break; } return FALSE; // no special processing }
这样,在MDI中,把快捷键响应汇聚到一个地方,只需要在main frame中响应快捷键就可以了,不用在每个view啊,edit中都加入代码。 到这里,MFC实现了收集快捷键消息,在一个地方查询快捷键表,转换。 然后还有另外一个问题,汇集到一个地方转换快捷键了,怎么最后响应消息调用的还是对应的edit或者view的消息响应函数呢?MFC把快捷键都转换成WM_COMMAND后,都送给main frame窗口,在CFrameWnd::OnCmdMsg()又向下查询,送给真正准备响应消息的view(然后给doc)或者其他窗口,去看看CFrameWnd::OnCmdMsg()的代码吧,别的文章(1,2)也有讲的 :-)
|