首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 文檔/視圖架構深度解析與實戰應用

     編輯:關於VC++

前言

文檔/視圖架構是 MFC 的核心設計模式,實現了數據(文檔)與表現(視圖)的分離,是觀察者模式在框架層的經典應用。然而,AppWizard 生成的龐大代碼往往讓初學者望而生畏。本文旨在提供一條「輕量級」學習路徑,結合理論與實戰,幫助開發者快速掌握文檔/視圖架構的核心機制並靈活應用於實際專案。

一、核心類別與關係圖譜

使用 AppWizard 建立 MDI 專案(專案名為 Demo)預設會產生六個核心類別:

  • CDemoApp(CWinApp 派生):應用程式物件,管理文檔模板列表
  • CMainFrame(CMDIFrameWnd 派生):主框架視窗,掛載選單、工具列、狀態列
  • CChildFrame(CMDIChildWnd 派生):子框架視窗,容納視圖
  • CDemoDoc(CDocument 派生):文檔類別,負責資料儲存與業務邏輯
  • CDemoView(CView 派生):視圖類別,負責資料顯示與用戶互動
  • CAboutDlg:關於對話框

關鍵成員變量速查表

類別關鍵成員用途
CWinAppm_pDocManager指向 CDocManager,管理所有文檔模板
CDocManagerm_templateList (CPtrList)文檔模板鏈表
CDocTemplatem_pDocClass, m_pViewClass, m_pFrameClass, m_nIDResource文檔/視圖/框架的運行時類別指標與資源 ID
CDocumentm_viewList (CPtrList)關聯的所有視圖
CFrameWndm_pActiveView當前活動視圖
CViewm_pDocument所屬文檔

二、類別間互訪實用速查

實戰中最常用的「從 A 找 B」模式整理如下:

// 取得應用程式指標
AfxGetApp();  // 或 extern CMyApp theApp;

// 取得主框架
AfxGetMainWnd();  // 或 AfxGetApp()->m_pMainWnd;

// 文檔 → 視圖
POSITION pos = GetFirstViewPosition();
while (pos) { CView* pView = GetNextView(pos); }

// 視圖 → 文檔
CDocument* pDoc = GetDocument();

// 框架 → 活動文檔/視圖
CDocument* pDoc = GetActiveDocument();
CView*     pView = GetActiveView();

// MDI 子框架 → 主框架
CMDIChildWnd* pChild = MDIGetActive();  // 或 GetActiveFrame()

// 視圖 → 父框架
CFrameWnd* pFrame = GetParentFrame();

// 文檔 → 文檔模板
CDocTemplate* pTemplate = GetDocTemplate();

// 應用程式 → 所有文檔模板
POSITION pos = GetFirstDocTemplatePosition();
while (pos) { CDocTemplate* p = GetNextDocTemplate(pos); }

三、動態建立文檔/視圖/框架關聯

方式一:新增文檔模板(模仿 AppWizard)

  1. 用「加入 MFC 類別」分別建立新的 CDocument、CView、CMDIChildWnd 派生類。
  2. 在資源視圖新增選單資源(如 IDR_NEW_TYPE)。
  3. 在 InitInstance 中註冊:
CMultiDocTemplate* pTpl = new CMultiDocTemplate(
    IDR_NEW_TYPE,
    RUNTIME_CLASS(CNewDoc),
    RUNTIME_CLASS(CNewChildFrame),
    RUNTIME_CLASS(CNewView));
AddDocTemplate(pTpl);
m_pNewTemplate = pTpl;  // 保存指標以便後續使用

方式二:同一文檔多視圖(使用 CCreateContext)

CNewView* pView = new CNewView();
CDocument* pDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();

CCreateContext ctx;
ctx.m_pCurrentDoc = pDoc;
ctx.m_pNewViewClass = RUNTIME_CLASS(CNewView);

CRect rect(0,0,0,0);
UINT nID = AFX_IDW_PANE_FIRST + 1;
pView->Create(NULL, NULL, WS_CHILD, rect, m_pMainWnd, nID, &ctx);

方式三:無文檔的框架+視圖(輕量級彈出視窗)

CChildFrame* pFrame = new CChildFrame();
CCreateContext ctx;
ctx.m_pNewViewClass = RUNTIME_CLASS(CDemoView);

pFrame->LoadFrame(IDR_MAINFRAME, WS_CHILD | WS_OVERLAPPEDWINDOW, this, &ctx);
pFrame->ShowWindow(SW_SHOWMAXIMIZED);
pFrame->InitialUpdateFrame(NULL, TRUE);  // 觸發 OnInitialUpdate & ActivateFrame

// 若需關聯現有文檔
ctx.m_pCurrentDoc = pExistingDoc;

四、實戰案例:MIS 系統主界面框架

需求描述

  • 啟動顯示「開始」選單(登入/登出)
  • 登入後切換至「功能」選單(業務邏輯一/二)
  • 點選功能項彈出 MDI 子視窗,可最大化/最小化/關閉共存
  • 登出時自動關閉所有業務視窗

關鍵實作步驟

  1. 建立不支援 Doc/View 的 MDI 專案,調整選單資源:
    IDR_MAINFRAME(未登入)→ IDR_DEMOTYPE(已登入)
  2. CDemoApp 保存兩個選單控制碼,OnLogin/OnLogout 切換選單與視窗標題。
  3. 新增 CDemoFrame(CMDIChildWnd)、CFuncOneView(CView)、CFuncTwoView(CFormView)。
  4. CMainFrame 以指標成員 m_pFuncOneFrame/m_pFuncTwoFrame 追蹤子視窗,避免重複建立。
  5. 命令處理函數中使用 CCreateContext 動態建立框架+視圖:
void CMainFrame::OnFuncOne() {
    if (m_pFuncOneFrame) { m_pFuncOneFrame->MDIActivate(); return; }
    m_pFuncOneFrame = new CDemoFrame();
    CCreateContext ctx; ctx.m_pNewViewClass = RUNTIME_CLASS(CFuncOneView);
    m_pFuncOneFrame->LoadFrame(IDR_MAINFRAME, WS_MAXIMIZE|WS_OVERLAPPEDWINDOW, this, &ctx);
    m_pFuncOneFrame->SetWindowText(_T("業務邏輯一"));
    m_pFuncOneFrame->ShowWindow(SW_SHOWMAXIMIZED);
    m_pFuncOneFrame->InitialUpdateFrame(NULL, TRUE);
}

進階:為視圖加入文檔支援(業務邏輯與表現分離)

  1. 新增 CDemoDoc(CDocument),建構函數設為 public,提供 GetData()。
  2. CFuncOneView 覆寫 GetDocument() 回傳 CDemoDoc*,OnDraw 中呼叫 pDoc->GetData() 繪製。
  3. 建立子視窗時在 CCreateContext 指定 m_pCurrentDoc = new CDemoDoc()。

五、常見坑點與最佳實踐

  • 記得包含對應標頭檔,否則編譯會報「未定義識別項」。
  • 若專案原本已支援 Doc/View,直接在 InitInstance 追加模板即可,無需重寫。
  • 主框架背景繪製可參考「VC 主框架視窗繪製背景」技巧。
  • 業務邏輯各自獨立時,建議為每個功能建立專屬 CMDIChildWnd 派生類,避免狀態汙染。
  • 登出時務必發送 WM_CLOSE 關閉子視窗,防止資源洩漏。

總結

文檔/視圖架構雖複雜,但掌握「文檔模板三要素(Doc/View/Frame)」與「CCreateContext 動態綁定」兩大核心機制,即可靈活構建從傳統 MDI 到輕量級彈出視窗的各種界面形態。本文提供的 MIS 案例可直接作為專案骨架擴展,祝開發順利!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved