MFC 預設單文檔架構 (SDI) 僅支援單一視圖。若需在同一文檔下切換多種視圖 (如表單、列表、圖表),採用 **靜態建立** 方式:在 `CWinApp::InitInstance` 中一次性建立所有視圖物件,運行期僅切換顯示/隱藏,避免動態建立/銷毀的閃爍與狀態丟失。本文提供完整實作步驟與核心切換邏輯。
CView* m_pView[4];BOOL CMyApp::InitInstance() {
// ... 標準 MFC 初始化 (文檔範本、主框架建立) ...
// 主框架建立後,取得預設視圖 (索引 0)
CView* pActiveView = ((CFrameWnd*)m_pMainWnd)->GetActiveView();
m_pView[0] = pActiveView; // 預設視圖
// 建立其餘視圖 (隱藏狀態)
m_pView[1] = new CProductionFormView;
m_pView[2] = new CEventsListView;
m_pView[3] = new CRunningFormView;
// 共享文檔指標的建立內容
CCreateContext context;
context.m_pCurrentDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();
for (int i = 1; i < 4; ++i) {
m_pView[i]->Create(NULL, NULL,
AFX_WS_DEFAULT_VIEW & ~WS_VISIBLE, // 初始隱藏
CFrameWnd::rectDefault, m_pMainWnd,
AFX_IDW_PANE_FIRST + i, &context); // 唯一視窗 ID
}
// 手動觸發 OnInitialUpdate (因非框架自動建立)
for (int i = 0; i < 4; ++i)
m_pView[i]->OnInitialUpdate();
return TRUE;
}
void CMyApp::SwithToView(int nView) {
ASSERT(nView >= 0 && nView < 4);
CView* pOldView = ((CFrameWnd*)m_pMainWnd)->GetActiveView();
CView* pNewView = m_pView[nView];
if (pOldView == pNewView) return;
// 1. 交換視窗 ID (關鍵:讓 RecalcLayout 識別新的作用中視圖)
UINT nOldID = ::GetWindowLong(pOldView->m_hWnd, GWL_ID);
UINT nNewID = ::GetWindowLong(pNewView->m_hWnd, GWL_ID);
::SetWindowLong(pOldView->m_hWnd, GWL_ID, nNewID);
::SetWindowLong(pNewView->m_hWnd, GWL_ID, nOldID);
// 2. 顯示/隱藏
pOldView->ShowWindow(SW_HIDE);
pNewView->ShowWindow(SW_SHOW);
// 3. 通知框架視窗更新作用中視圖與佈局
((CFrameWnd*)m_pMainWnd)->SetActiveView(pNewView);
((CFrameWnd*)m_pMainWnd)->RecalcLayout();
pNewView->Invalidate(); // 強制重繪
}
// 在 CMyApp 或主框架中對應選單 ID
void CMainFrame::OnViewProduction() { ((CMyApp*)AfxGetApp())->SwithToView(1); }
void CMainFrame::OnViewEvents() { ((CMyApp*)AfxGetApp())->SwithToView(2); }
void CMainFrame::OnViewRunning() { ((CMyApp*)AfxGetApp())->SwithToView(3); }
void CMainFrame::OnViewDefault() { ((CMyApp*)AfxGetApp())->SwithToView(0); }
| 方案 | 優點 | 缺點 |
|---|---|---|
| 靜態建立 (本文) | 切換極快、視圖狀態保留 (捲軸位置、輸入焦點)、程式碼簡單 | 啟動較慢、佔用記憶體固定、視圖數固定 |
| 動態建立/銷毀 | 啟動快、記憶體按需、視圖類型可擴展 | 切換有延遲、狀態需手動序列化/還原、程式碼複雜 |
| CSplitterWnd 分割視窗 | 原生支援多視圖並列、拖曳分割 | 非「切換」模式、UI 風格不同 |