MFC 文檔/視圖架構中,物件之間透過指針網絡關聯。熟練掌握「從任何一點導航到任意目標」是 MFC 開發效率的分水嶺。本文系統整理 15 類核心指針獲取模式,涵蓋單文檔/多文檔 (SDI/MDI)、分割視圖、運行時類型識別 (RTTI)、文檔模板枚舉,附帶正反例代碼與通用導航口訣。
| 函數 | 返回類型 | 用途 |
|---|---|---|
AfxGetApp() | `CWinApp*` | 應用程式物件單例 |
AfxGetMainWnd() | `CWnd*` | 主框架視窗 (`CFrameWnd*`/`CMDIFrameWnd*`) |
AfxGetInstanceHandle() | `HINSTANCE` | 模組實例句柄 (資源載入用) |
// 1. App → MainFrame
CMainFrame* pMain = (CMainFrame*)AfxGetApp()->m_pMainWnd; // 推薦
// 或
CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); // 等價
// 2. MainFrame → Menu
CMenu* pMenu = pMain->GetMenu(); // 或 AfxGetMainWnd()->GetMenu()
// 3. MainFrame → ToolBar / StatusBar (成員變量法,需預先在 MainFrame 宣告)
CToolBar* pTB = &pMain->m_wndToolBar;
CStatusBar* pSB = &pMain->m_wndStatusBar;
// 4. 通用子視窗查找法 (無需成員變量)
CToolBar* pTB = (CToolBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
CStatusBar* pSB = (CStatusBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd();
CMyDoc* pDoc = (CMyDoc*)pMain->GetActiveDocument(); // 唯一文檔
CMyView* pView = (CMyView*)pMain->GetActiveView(); // 唯一視圖
CMDIFrameWnd* pMain = (CMDIFrameWnd*)AfxGetMainWnd();
CMDIChildWnd* pChild = (CMDIChildWnd*)pMain->MDIGetActive(); // 或 GetActiveFrame()
CMyDoc* pDoc = (CMyDoc*)pChild->GetActiveDocument();
CMyView* pView = (CMyView*)pChild->GetActiveView();
// View → Doc (視圖類成員函數)
CMyDoc* pDoc = GetDocument(); // 自動生成,含 ASSERT 檢查
// Doc → View (枚舉所有視圖)
POSITION pos = GetFirstViewPosition();
while (pos) {
CView* pView = GetNextView(pos);
if (pView->IsKindOf(RUNTIME_CLASS(CMyView))) {
// 找到目標視圖
break;
}
}
// 通用輔助函數 (加入 Doc 類)
CView* CMyDoc::GetView(CRuntimeClass* pClass) {
POSITION pos = GetFirstViewPosition();
CView* pView = NULL;
while (pos) {
pView = GetNextView(pos);
if (pView->IsKindOf(pClass)) break;
}
return (pView && pView->IsKindOf(pClass)) ? pView : NULL;
}
// 使用
CMyView* pView = (CMyView*)GetView(RUNTIME_CLASS(CMyView));
// 在 CMyViewA 獲取 CMyViewB
CMyDoc* pDoc = GetDocument();
CMyViewB* pViewB = (CMyViewB*)pDoc->GetView(RUNTIME_CLASS(CMyViewB));
// MainFrame 成員
CSplitterWnd m_wndSplitter;
// OnCreateClient 創建
m_wndSplitter.CreateStatic(this, 2, 1); // 2行1列
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CView1), CSize(100,100), pContext);
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CView2), CSize(100,100), pContext);
// 獲取面板指針
CView1* pView1 = (CView1*)m_wndSplitter.GetPane(0, 0);
CView2* pView2 = (CView2*)m_wndSplitter.GetPane(1, 0);
// 1. 對話框/視圖內控件 (DDX 綁定首選,次選 GetDlgItem)
CButton* pBtn = (CButton*)GetDlgItem(IDC_MYBUTTON); // 需包含 afxwin.h
// 2. ClassWizard 綁定成員變量 (最推薦)
// 在 ClassWizard → Member Variables → Add Variable → Control
// App 遍歷所有文檔模板
POSITION tplPos = AfxGetApp()->GetFirstDocTemplatePosition();
while (tplPos) {
CDocTemplate* pTpl = AfxGetApp()->GetNextDocTemplate(tplPos);
// 判斷模板類型
CString str; pTpl->GetDocString(str, CDocTemplate::docName);
// 模板遍歷其文檔
POSITION docPos = pTpl->GetFirstDocPosition();
while (docPos) {
CDocument* pDoc = pTpl->GetNextDoc(docPos);
// 判斷文檔
if (pDoc->GetTitle() == "Target") { /* ... */ }
}
}
// Doc → 所屬模板
CDocTemplate* pTpl = pDoc->GetDocTemplate();
CWnd* pChild = pParent->ChildWindowFromPoint(pt); // 單一參數
// 或帶旗標
CWnd* pChild = pParent->ChildWindowFromPoint(pt, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
// 旗標含義
CWP_ALL // 不忽略任何
CWP_SKIPINVISIBLE // 忽略不可見
CWP_SKIPDISABLED // 忽略禁用
CWP_SKIPTRANSPARENT // 忽略透明 (WS_EX_TRANSPARENT)
// ⚠️ 返回偽指針,不可長期保存,僅即時使用
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| `CView* p = GetActiveView();` | 在 Doc/非 Frame 類中調用 | 僅 `CFrameWnd` 系有此函數,Doc 用 `GetFirstViewPosition()` |
| `CMainFrame* p = (CMainFrame*)GetParent();` | 視圖父視窗不一定是 MainFrame (MDI 為 ChildFrame) | 用 `GetParentFrame()` 獲取直接框架父視窗 |
| 保存 `ChildWindowFromPoint` 返回指針 | 偽指針,臨時對象 | 即時使用,不保存 |
| 直接 `static_cast | 無 RTTI 檢查,類型不匹配導致崩潰 | 用 `DYNAMIC_DOWNCAST(CMyView, GetActiveView())` 或 `IsKindOf` |
將這張導航圖貼在工位旁,下次需要「從這裡拿到那個指針」時,按圖索驥,秒級定位,拒絕盲目試錯。