在 MFC 多文檔介面(MDI)應用程式開發中,開發者常遇到一個棘手問題:在 CMainFrame::PreCreateWindow 中修改 CREATESTRUCT 參數(如 cs.cx、cs.cy)來設定初始框架窗口大小,在單文檔介面(SDI)中有效,但在 MDI 架構下卻完全失效。這是因為 MDI 框架窗口的建立流程與 SDI 有本質不同,MDI 的主框架窗口大小受到客戶區(MDI Client Area)和子窗口管理機制的約束,PreCreateWindow 階段的尺寸參數往往會被後續的框架初始化邏輯覆蓋。
經實測驗證,最可靠的方法是在 CWinApp::InitInstance() 中,待主框架窗口建立完成後,直接呼叫 MoveWindow 或 SetWindowPos 來設定所需尺寸。此時框架窗口已完全初始化,不再受 MDI 客戶區布局邏輯干擾。
BOOL CMyApp::InitInstance()
{
// ... 標準初始化代碼 ...
// 建立主框架窗口
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// 關鍵:在框架窗口建立後設定尺寸
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
// 設定為 1024x768,位置置中
CRect rect(0, 0, 1024, 768);
pMainFrame->CenterWindow();
pMainFrame->MoveWindow(&rect);
return TRUE;
}
InitInstance 階段框架窗口的 HWND 已有效,MDI 客戶區也已建立完成。CenterWindow() 可確保窗口在螢幕中央顯示,提升使用者體驗。| 方法 | 適用場景 | 可靠度 | 備註 |
|---|---|---|---|
PreCreateWindow 修改 cs | SDI 應用 | 低 | MDI 無效 |
CMainFrame::OnCreate 中 MoveWindow | MDI/SDI | 中 | 偶發閃爍 |
InitInstance 中 MoveWindow | MDI/SDI | 高 | 推薦做法 |
| 註冊表/設定檔持久化 | 記住使用者上次尺寸 | 高 | 需配合 WINDOWPLACEMENT |
若需在應用程式關閉時保存窗口尺寸與位置,下次啟動自動恢復,可使用 WINDOWPLACEMENT 結構配合註冊表或 INI 檔案:
// 關閉時保存
void CMainFrame::OnDestroy()
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(&wp);
// 寫入註冊表
AfxGetApp()->WriteProfileBinary(_T("MainFrame"), _T("WindowPlacement"),
(BYTE*)&wp, sizeof(wp));
CMDIFrameWnd::OnDestroy();
}
// 啟動時恢復(在 InitInstance 中)
WINDOWPLACEMENT wp;
UINT nBytes;
if (AfxGetApp()->GetProfileBinary(_T("MainFrame"), _T("WindowPlacement"),
(BYTE**)&wp, &nBytes))
{
if (nBytes == sizeof(wp))
pMainFrame->SetWindowPlacement(&wp);
}
OnCreate 前調用窗口操作:此時 m_hWnd 可能尚未有效。GetSystemMetrics(SM_CXSCREEN) 等 API 計算相對尺寸。CenterWindow() 預設置中於主螢幕,若需支援多螢幕,需自行計算目標螢幕工作區矩形。總結:MFC MDI 架構下修改框架窗口大小,關鍵在於選擇正確的時機點。在 CWinApp::InitInstance() 中框架窗口完全建立後呼叫 MoveWindow 或 SetWindowPos,是最穩健、副作用最小的標準做法。