深入淺出MFC文檔/視圖架構(gòu)之相互關(guān)系
2006-03-21 14:20 作者: 宋寶華 出處: 天極開發(fā) 責(zé)任編輯:方舟
2. 消息流動(dòng)機(jī)制
在基于"文檔/視圖"架構(gòu)的MFC程序中,用戶消息(鼠標(biāo)、鍵盤輸入等)會(huì)先發(fā)往視圖,如果視圖未處理則會(huì)發(fā)往框架窗口。所以,一般來說,消息映射宜定義在視圖中。另外,如果一個(gè)應(yīng)用同時(shí)擁有多個(gè)視圖而當(dāng)前活動(dòng)視圖沒有對(duì)消息進(jìn)行處理則消息也會(huì)發(fā)往框架窗口。
下面我們來看實(shí)例,我們利用Visual C++向?qū)?chuàng)建一個(gè)單文檔/視圖架構(gòu)的MFC程序,在其中增加一個(gè)菜單項(xiàng)為"自定義"(ID為IDM_SELF,如圖6.4)。
 圖6.4 含"自定義"菜單的單文檔/視圖架構(gòu)MFC程序
|
我們分別在視圖類和框架窗口類中為"自定義"菜單添加消息映射,代碼如下:
//視圖中的消息映射和處理函數(shù) 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("消息在視圖中處理"); }
//框架窗口中的消息映射和處理函數(shù) 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("消息在框架窗口中處理"); } |
這時(shí)候,我們單擊"自定義"菜單,彈出對(duì)話框顯示"消息在視圖中處理";如果我們刪除框架窗口中的消息映射,再單擊"自定義"菜單,彈出對(duì)話框也顯示"消息在視圖中處理";但是,若我們將視圖中的消息映射刪除了,就會(huì)顯示"消息在框架窗口中處理"!這驗(yàn)證了我們關(guān)于消息處理順序論述的正確性。
欲深入理解消息流動(dòng)過程,還需認(rèn)真分析CFrameWnd::OnCmdMsg、CView::OnCmdMsg函數(shù)的源代碼:
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消息的實(shí)際流動(dòng)順序比前文敘述的"先視圖,后框架窗口"要復(fù)雜得多,文檔和應(yīng)用程序都參與了消息的處理過程。如果我們?cè)贋槲臋n和應(yīng)用添加消息映射和處理函數(shù):
//文檔的消息映射和處理函數(shù) 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("消息在文檔中處理"); }
//應(yīng)用的消息映射和處理函數(shù) 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("消息在應(yīng)用中處理"); } |
屏蔽掉視圖和框架窗口的消息映射,再單擊"自定義"菜單,彈出對(duì)話框顯示"消息在文檔中處理";再屏蔽掉文檔中的消息映射,彈出對(duì)話框顯示"消息在應(yīng)用中處理"!由此可見,完整的WM_COMMAND消息的處理順序是"視圖――文檔――框架窗口――應(yīng)用"!
實(shí)際上,關(guān)于MFC的消息流動(dòng)是一個(gè)很復(fù)雜的議題,陷于篇幅的原因,我們不可能對(duì)其進(jìn)行更詳盡的介紹,讀者可自行尋找相關(guān)資料。