深入浅出MFC文档/视图架构之相互关系
2006-03-21 14:20 作者: 宋宝华 出处: 天极开发 责任编辑:方舟
2. 消息流动机制
在基于"文档/视图"架构的MFC程序中,用户消息(鼠标、键盘输入等)会先发往视图,如果视图未处理则会发往框架窗口。所以,一般来说,消息映射宜定义在视图中。另外,如果一个应用同时拥有多个视图而当前活动视图没有对消息进行处理则消息也会发往框架窗口。
下面我们来看实例,我们利用Visual C++向导创建一个单文档/视图架构的MFC程序,在其中增加一个菜单项为"自定义"(ID为IDM_SELF,如图6.4)。
图6.4 含"自定义"菜单的单文档/视图架构MFC程序
|
我们分别在视图类和框架窗口类中为"自定义"菜单添加消息映射,代码如下:
//视图中的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleView, CView) //{{AFX_MSG_MAP(CExampleView) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CExampleView::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在视图中处理"); }
//框架窗口中的消息映射和处理函数 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP()
void CMainFrame::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在框架窗口中处理"); } |
这时候,我们单击"自定义"菜单,弹出对话框显示"消息在视图中处理";如果我们删除框架窗口中的消息映射,再单击"自定义"菜单,弹出对话框也显示"消息在视图中处理";但是,若我们将视图中的消息映射删除了,就会显示"消息在框架窗口中处理"!这验证了我们关于消息处理顺序论述的正确性。
欲深入理解消息流动过程,还需认真分析CFrameWnd::OnCmdMsg、CView::OnCmdMsg函数的源代码:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // pump through current view FIRST CView* pView = GetActiveView(); if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE;
// then pump through frame if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE;
// last but not least, pump through app CWinApp* pApp = AfxGetApp(); if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE;
return FALSE; }
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // first pump through pane if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE;
// then pump through document BOOL bHandled = FALSE; if (m_pDocument != NULL) { // special state for saving view before routing to document _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); CView* pOldRoutingView = pThreadState->m_pRoutingView; pThreadState->m_pRoutingView = this; bHandled = m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); pThreadState->m_pRoutingView = pOldRoutingView; }
return bHandled; } |
分析上述源代码可知,WM_COMMAND消息的实际流动顺序比前文叙述的"先视图,后框架窗口"要复杂得多,文档和应用程序都参与了消息的处理过程。如果我们再为文档和应用添加消息映射和处理函数:
//文档的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleDoc, CDocument) //{{AFX_MSG_MAP(CExampleDoc) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP()
void CExampleDoc::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在文档中处理"); }
//应用的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleApp, CWinApp) //{{AFX_MSG_MAP(CExampleApp) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP()
void CExampleApp::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在应用中处理"); } |
屏蔽掉视图和框架窗口的消息映射,再单击"自定义"菜单,弹出对话框显示"消息在文档中处理";再屏蔽掉文档中的消息映射,弹出对话框显示"消息在应用中处理"!由此可见,完整的WM_COMMAND消息的处理顺序是"视图――文档――框架窗口――应用"!
实际上,关于MFC的消息流动是一个很复杂的议题,陷于篇幅的原因,我们不可能对其进行更详尽的介绍,读者可自行寻找相关资料。