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

MFC 文檔/視圖/框架交互深度解析:消息路由、視圖更新機制、初始化清理、文檔模板與修改標記

     編輯:關於C語言

MFC 文檔/視圖架構核心在於三者協作:文檔管理數據、視圖處理交互、框架提供窗口容器。本文深度解析消息路由規律、多視圖同步更新機制、視圖初始化/文檔清理生命週期、文檔模板作用及修改標記自動提示保存。

一、 消息路由規律:視圖優先,框架兜底

  • 用戶輸入消息 (菜單、工具欄、鍵盤、鼠標) 首先發送給當前活動視圖
  • 視圖未處理 (無消息映射) 時,沿命令鏈路由到框架窗口
  • 框架未處理則路由到應用程序對象
  • 多視圖時,僅活動視圖接收命令消息,非活動視圖不接收

菜單狀態自動管理

  • 菜單項無 ON_COMMAND 映射時自動禁用 (灰顯)
  • 多視圖下,菜單啟用/禁用狀態由活動視圖的 ON_UPDATE_COMMAND_UI 決定
  • 需全局生效的命令 (如「關於」) 應在 CWinApp 或 CFrameWnd 中映射

框架處理命令優勢

// 在 CMainFrame 中映射全局命令
ON_COMMAND(ID_FILE_SAVE_ALL, OnFileSaveAll)

void CMainFrame::OnFileSaveAll() {
    // 遍歷所有文檔保存
    POSITION pos = m_pDocTemplate->GetFirstDocPosition();
    while (pos) {
        CDocument* pDoc = m_pDocTemplate->GetNextDoc(pos);
        pDoc->OnSaveDocument(pDoc->GetPathName());
    }
}

二、 多視圖同步更新機制:文檔為中心

單文檔多視圖 (如同一文檔同時在表格視圖和圖表視圖顯示) 時,一視圖修改數據需通知其他視圖刷新。

核心雙函數

函數調用方參數作用
CDocument::UpdateAllViews修改數據的視圖/文檔pSender (發送者視圖, 可排除自身)
lHint (長整型提示)
pHint (對象指針提示)
通知所有關聯視圖調用 OnUpdate
CView::OnUpdate框架自動調用同上視圖響應更新,重繪/重新查詢數據

調用流程

// 視圖中修改數據後
void CMyView::OnDataChanged() {
    CMyDoc* pDoc = GetDocument();
    pDoc->ModifyData(...); // 修改文檔數據
    pDoc->SetModifiedFlag(TRUE); // 標記已修改
    
    // 通知其他視圖更新 (排除自己)
    pDoc->UpdateAllViews(this, 0, NULL); // this = pSender
}

// 其他視圖響應
void CAnotherView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) {
    if (pSender == this) return; // 可選:排除發送者
    
    // 根據 Hint 精準更新
    if (lHint == HINT_DATA_CHANGED) {
        Invalidate(); // 簡單重繪
    } else if (pHint) {
        // 針對性更新特定數據
        CDataHint* pData = (CDataHint*)pHint;
        UpdateSpecificItem(pData->index);
    }
}

Hint 參數進階用法

  • lHint:枚舉值,如 HINT_ADD_ITEM, HINT_DELETE_ITEM, HINT_REFRESH_ALL
  • pHint:指向自定義結構,可攜帶索引、舊值、新值、矩形區域等豐富信息
  • 傳遞 this 指針:pDoc->UpdateAllViews(this, HINT_SELECTION_CHANGED, (CObject*)this),接收端可識別發送者

三、 視圖初始化與文檔清理生命週期

1. 視圖初始化:OnInitialUpdate

void CMyView::OnInitialUpdate() {
    CView::OnInitialUpdate(); // 必須調用基類!
    
    // 1. 獲取文檔指針
    CMyDoc* pDoc = GetDocument();
    
    // 2. 根據文檔數據初始化視圖狀態
    m_listCtrl.SetItemCount(pDoc->GetItemCount());
    // ... 設置滾動範圍、選中項、視圖模式等
    
    // 基類 OnInitialUpdate 內部會調用 OnUpdate(NULL, 0, NULL)
    // 確保初始狀態同步
}

關鍵點:OnInitialUpdate 在視圖創建、文檔打開/新建時調用一次。區別於構造函數:此時文檔已關聯,可安全調用 GetDocument()。

2. 文檔清理:DeleteContents

void CMyDoc::DeleteContents() {
    // 1. 釋放文檔持有的所有動態資源
    m_dataArray.RemoveAll(); // 清空數組
    while (m_pHead) {        // 清空鏈表
        Node* p = m_pHead;
        m_pHead = p->next;
        delete p;
    }
    
    // 2. 重置狀態變量
    m_bModified = FALSE;
    m_strPathName.Empty();
    
    // 3. 調用基類 (通常為空)
    CDocument::DeleteContents();
}

調用時機:文檔關閉前、新建文檔前 (舊文檔清理)、應用退出時。單文檔應用中文檔對象不銷毀,僅反複調用 DeleteContents/OnNewDocument。

3. 單文檔 vs 多文檔對象生命週期

特性SDI (單文檔)MDI (多文檔)
文檔對象應用生命週期內唯一實例,反覆復用每個子窗口獨立文檔實例,創建/銷毀
視圖對象唯一實例,反覆復用每個子窗口獨立視圖實例
框架窗口主框架唯一主框架 + 多個 MDI 子框架
關鍵函數OnNewDocument, DeleteContents構造/析構, OnNewDocument

四、 文檔模板:架構粘合劑

CDocTemplate 記錄三者對應關係:

  • CSingleDocTemplate:SDI,一個文檔類 + 一個視圖類 + 一個框架類
  • CMultiDocTemplate:MDI,一個文檔類 + 多個視圖類 + MDI 子框架類

核心職責

  1. 創建文檔、視圖、框架實例並關聯
  2. 管理打開的文檔列表
  3. 根據文件擴展名/過濾器匹配正確模板打開文件
  4. 提供 CreateNewDocument, OpenDocumentFile, CreateNewFrame 等工廠方法

文件類型註冊 (資源字符串)

// 字符串資源 IDR_MAINFRAME
"項目名\n\n項目\n項目文件 (*.myext)\n.myext\n項目.Document\n項目 Document"
/* 解析:
行1: 應用名
行2: (空)
行3: 文件描述
行4: 擴展名
行5: 註冊表文檔類名
行6: 文檔類型顯示名 */

五、 修改標記自動提示保存

// 視圖修改數據時
GetDocument()->SetModifiedFlag(TRUE);

// 框架關閉文檔時自動檢查
// CDocument::SaveModified() 內部邏輯:
if (IsModified()) {
    int response = AfxMessageBox(IDS_ASK_TO_SAVE, MB_YESNOCANCEL);
    if (response == IDYES) return OnSaveDocument(GetPathName());
    if (response == IDCANCEL) return FALSE; // 取消關閉
    return TRUE; // 不保存關閉
}
return TRUE; // 未修改直接關閉

六、 獲取核心對象指針速查表

需求代碼上下文
應用對象AfxGetApp()全局
主框架窗口AfxGetApp()->m_pMainWnd全局
活動文檔 (框架中)GetActiveDocument()CFrameWnd
活動視圖 (框架中)GetActiveView()CFrameWnd
對應文檔 (視圖中)GetDocument()CView
遍歷文檔視圖GetFirstViewPosition()/GetNextView()CDocument
文檔模板GetDocTemplate()CDocument
活動 MDI 子窗口MDIGetActive()CMDIFrameWnd

本文整理自 MFC 經典技術文檔,適用於 VC6~VS2022 所有版本。

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