MFC窗口WM_COMMAND消息

通過分析MFC的源代碼,我們可以得到WM_COMMAND的消息響應順序如下:

多文檔框架中,有打開的文檔時:視圖 > 文檔 > 子框架窗口 > 應用程序 >主框架窗口

多文檔框架在沒有打開文檔時,應用程序和主框架窗口的順序相反:主框架窗口 > 應用程序

 

在單文檔框架應用程序中,因爲沒有子框架窗口,所以順序應該是:視圖 > 文檔 >主框架窗口> 應用程序。無論有沒有打開文檔,主框架窗口都比應用程序類更優先。

 

 

 

 

事實上在多文檔框架中,系統自動生成的代碼使用的是CDocTemplate的子類CMultiDocTemplate。在應用程序類(多文檔應用框架)的InitInstance()裏可以找到類似下面的代碼:

    // 註冊應用程序的文檔模板。文檔模板
    // 將用作文檔、框架窗口和視圖之間的連接
    CMultiDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(IDR_test3TYPE,
       RUNTIME_CLASS(Ctest3Doc),
       RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架
       RUNTIME_CLASS(Ctest3View));
    if (!pDocTemplate)
       return FALSE;
而CMultiDocTemplate類並沒有重寫OnCmdMsg函數,所以依然不影響WM_COMMAND消息的響應順序。
 
BOOL CDocTemplate::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
...{
    BOOL bReturn;
    CCmdTarget* pFactory = DYNAMIC_DOWNCAST(CCmdTarget, m_pAttachedFactory);
 
    if (nCode == CN_OLE_UNREGISTER && pFactory != NULL)
       bReturn = pFactory->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
    else
       bReturn = CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 
    return bReturn;
}

因爲應用程序中並沒有開發直接從CDocTemplate繼承的類,所以應用程序無法使用CDocTemplate的子類定義WM_COMMAND消息的響應函數。CDocTemplate類在OnCmdMsg函數實現裏調用的CCmdTarget::OnCmdMsg實現不了任何消息的傳遞。

 

視圖CView > 文檔CDocument > 子框架CFrame > 應用程序CWinApp >主框架窗口CMDIFrameWnd

 

再查看CDocument的OnCmdMsg的實現代碼:

文檔類CDocument首先查找自己的響應函數(調用CCmdTarget::OnCmdMsg),然後讓其成員m_pDocTemplate來處理。m_pDocTemplate是文檔模板類CDocTemplate的指針,MFC框架使用文檔模板來實現自動化的文檔管理。在應用程序開發過程中,沒有針對文檔模板類的編程。查看CDocTemplate類中OnCmdMsg的實現:

BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
...{
    if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
       return TRUE;
 
    // otherwise check template
    if (m_pDocTemplate != NULL &&
     m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
       return TRUE;
 
    return FALSE;
}

存在打開的視圖時,視圖CView > 子框架CFrame > 應用程序CWinApp >主框架窗口CMDIFrameWnd

沒有打開視圖時,主框架窗口CMDIFrameWnd > 應用程序CWinApp

 

再看視圖CView類對OnCmdMsg的處理:

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
    if (m_pDocument != NULL)
    ...{
       // special state for saving view before routing to document
       CPushRoutingView push(this);
       return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
    }
 
    return FALSE;
}

 

視圖類首先查找自己的消息響應表(調用CWnd::OnCmdMsg),然後讓視圖對應的CDocument類處理消息。所以可以得出如下順:

 

子框架窗口CMDIChildWnd > 主框架窗口CMDIFrameWnd

 

CMDIChildWnd窗口沒有重載OnCmdMsg函數,所以使用基本框架窗口CFrameWnd的OnCmdMsg函數:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
...{
    CPushRoutingFrame push(this);
 
    // 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;
}

基本框架窗口先讓當前活動視圖CView響應函數,然後再查找自己的消息響應表(調用CWnd::OnCmdMsg),最後讓應用程序處理該消息。所以得出順序:

MFC:

        Win32的封裝,窗口體系單元化,單元子父類繪製關係體系和消息響應機制,分別是互繪製機制和

消息傳遞機制

       

在MFC開發的程序中,菜單、工具條按鈕等都會產生WM_COMMAND消息。而在MFC的Document/View框架中,有很多類可以響應WM_COMMAND消息,分別是框架類:CFrameWnd、CMDIChildWndCMDIFrameWnd;應用程序類CWinApp;文檔類CDocument;以及視圖類CView。

當應用程序主菜單發送了一個WM_COMMAND消息時,WM_COMMAND消息將會按一定順序被交這些類的實例,並調用第一個發現的響應函數。以多文檔視圖框架應用程序爲例,我們可以分析MFC中這些類的源代碼,並一步一步找出WM_COMMAND消息的響應順序。

因爲框架窗口是菜單的父窗口,所以消息首先發到CMDIFrameWnd類的實例(也就是主框架窗口,通常爲CMainFrame)。CMDIFrameWnd類的OnCmdMsg函數如下:

BOOL CMDIFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
...{
    CMDIChildWnd* pActiveChild = MDIGetActive();
    // pump through active child FIRST
    if (pActiveChild != NULL)
    ...{
       CPushRoutingFrame push(this);
       if (pActiveChild->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
           return TRUE;
    }
 
    // then pump through normal frame
    return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

 

主框架窗口CMDIFrameWnd先查找當前活動的子框架窗口CMDIChildWnd,讓其優先訪問,然後調用基本框架窗口CFrameWnd的響應函數。所以響應優先級:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章