許多管理資訊系統 (MIS) 的主視窗都具備相似特徵:透過框架選單開啟不同類型的檢視視窗,這些視窗擁有 MDI 子視窗的最小化/最大化/還原能力,但點選選單項目只能產生單一實例 (類似 SDI 行為)。這種「單一文件多視圖」的混合架構兼具條理與彈性。本文記錄使用 VC 6.0 實作此架構的完整過程,涉及 MFC 文件/視圖架構的核心機制。
新建 MFC AppWizard (exe) 專案,命名 mdisdi,選擇 MDI 架構,其餘採預設值。
在 CApp 衍生類別 (CMdisdiApp) 的 public 區段新增兩個文件模板指標:
public:
CMultiDocTemplate* pDoctemp1;
CMultiDocTemplate* pDoctemp2;
將原本的區域變數 pDocTemplate 改為使用成員指標 pDoctemp1:
pDoctemp1 = new CMultiDocTemplate(
IDR_MDISDITYPE,
RUNTIME_CLASS(CMdisdiDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CMdisdiView));
AddDocTemplate(pDoctemp1);
CDoc2 (基類 CDocument)CChildFrame2 (基類 CMDIChildWnd)CView2 (基類 CFormView),在對話方塊屬性中指定 Document 為 CDoc2同樣使用成員指標 pDoctemp2,並修正 Frame 類別為 CChildFrame2:
pDoctemp2 = new CMultiDocTemplate(
IDR_VIEW2_TMPL,
RUNTIME_CLASS(CDoc2),
RUNTIME_CLASS(CChildFrame2),
RUNTIME_CLASS(CView2));
AddDocTemplate(pDoctemp2);
別忘在 mdisdi.cpp 頭部加入 #include "Doc2.h"。
IDR_MAINFRAME 選單,新增「功能」主選單,包含「視窗1」與「視窗2」兩個項目。IDR_MAINFRAME 產生 IDR_MAINFRAME1、IDR_MAINFRAME2,分別重命名為 IDR_MDISDITYPE、IDR_VIEW2_TMPL,分別對應兩組文件模板。在 MainFrm.h 加入標頭檔與文件指標:
#include "mdisdiDoc.h"
#include "Doc2.h"
public:
CMdisdiDoc * pDoc1;
CDoc2 * pDoc2;
在 MainFrm.cpp 實作選單命令處理函式,核心邏輯:若文件不存在則建立新文件;若已存在則激活現有視圖框架:
void CMainFrame::OnMenuitem32771() // 視窗1
{
if(pDoc1 == NULL)
{
CMdisdiApp * pApp = (CMdisdiApp *)AfxGetApp();
pDoc1 = (CMdisdiDoc *)pApp->pDoctemp1->OpenDocumentFile(NULL);
}
else
{
POSITION pos = pDoc1->GetFirstViewPosition();
CView * pView = pDoc1->GetNextView(pos);
pView->GetParentFrame()->ActivateFrame();
}
}
void CMainFrame::OnMenuitem32772() // 視窗2
{
if(pDoc2 == NULL)
{
CMdisdiApp * pApp = (CMdisdiApp *)AfxGetApp();
pDoc2 = (CDoc2 *)pApp->pDoctemp2->OpenDocumentFile(NULL);
}
else
{
POSITION pos = pDoc2->GetFirstViewPosition();
CView * pView = pDoc2->GetNextView(pos);
pView->GetParentFrame()->ActivateFrame();
}
}
CMainFrame 建構式將 pDoc1、pDoc2 初始化為 NULL。CChildFrame 解構式中取得主視窗指標,將 pDoc1 歸零。CChildFrame2 解構式同理將 pDoc2 歸零。// ChildFrm.cpp
#include "MainFrm.h"
CChildFrame::~CChildFrame()
{
CMainFrame * pMain = (CMainFrame *)AfxGetMainWnd();
pMain->pDoc1 = NULL;
}
// ChildFrame2.cpp
#include "MainFrm.h"
CChildFrame2::~CChildFrame2()
{
CMainFrame * pMain = (CMainFrame *)AfxGetMainWnd();
pMain->pDoc2 = NULL;
}
在 InitInstance() 註解掉命令列處理碼,避免啟動時彈出「新增檔案」對話方塊:
// CCommandLineInfo cmdInfo;
// ParseCommandLine(cmdInfo);
// if (!ProcessShellCommand(cmdInfo))
// return FALSE;
此設計讓單一 MDI 框架下管理多組獨立的 Document/View 組合,每組行為如 SDI (單一實例) 卻保有 MDI 子視窗的視覺操作體驗。透過主框架持有文件指標並攔截子視窗銷毀事件,精確控制實例生命週期,是 MFC 文件/視圖架構進階應用的經典範例。