在 MFC 單文檔架構中,使用 `CSplitterWnd` 實現複雜的多視圖分割(如三列、中間兩行)時,常遇到「中間列寬度異常窄」的佈局失效問題。本文解析靜態分割器建立流程,揭示 **`SetColumnInfo`/`SetRowInfo` 才是控制視圖尺寸的關鍵**,而非建構時傳入的 `CSize`。
開發者在 `CMainFrame::OnCreateClient` 中建立三層巢狀分割器:
代碼中透過 `CreateView` 時傳入 `CSize(rect.Width()/3, ...)` 期望控制寬高,**但運行時中間列極窄**,高度倒是正確。
查閱 MFC 源碼可知,`CSplitterWnd::CreateView` 內部調用 `CreateView` 時,**若分割器已有有效的列/行資訊(透過 `SetColumnInfo`/`SetRowInfo` 設定),則 `sizeInit` 參數會被忽略**,改用已儲存的列寬/行高。靜態分割器建立後,預設列寬/行高可能為 0 或最小值,導致視圖壓縮。
在所有 `CreateStatic` / `CreateView` 完成後,呼叫 `SetColumnInfo` / `SetRowInfo` 明確指定各欄列的**最小寬度/理想寬度**。
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) {
// ... 前序 CreateStatic/CreateView 代碼保持不變 ...
// 關鍵修正:建立完成後,統一設定列寬與行高
// 主分割器:第 0 列 (左) 寬 200,第 1 列 (右) 剩餘空間
m_wndSplitter.SetColumnInfo(0, 200, 50); // cxIdeal=200, cxMin=50
m_wndSplitter.SetColumnInfo(1, 400, 100); // 右側容器初始寬 400
// 二級分割器 (在主分割器第 1 列內):單行,兩列平均分配
m_wndSplitter2.SetColumnInfo(0, 200, 50);
m_wndSplitter2.SetColumnInfo(1, 200, 50);
// 三級分割器 (在二級分割器第 0 列內):兩行,高度比 1:9
// 注意:SetRowInfo 參數為 cyIdeal, cyMin
int totalHeight = rect.Height(); // 需獲取當前客戶區高度
m_wndSplitter3.SetRowInfo(0, totalHeight / 10, 30); // 第 0 行 (EditView) 高 1/10
m_wndSplitter3.SetRowInfo(1, totalHeight * 9 / 10, 100); // 第 1 行 (ListView) 高 9/10
// 觸發重新佈局
m_wndSplitter.RecalcLayout();
return TRUE;
}
SetColumnInfo(int col, int cxIdeal, int cxMin):設定第 `col` 列的**理想寬度** (`cxIdeal`) 與**最小寬度** (`cxMin`)。分割器會嘗試滿足理想寬度,空間不足時壓縮至最小寬度。SetRowInfo(int row, int cyIdeal, int cyMin):同理設定行高。RecalcLayout():強制分割器依最新資訊重新計算子視窗位置與大小,**必須在設定完 ColumnInfo/RowInfo 後呼叫**。在 `CMainFrame::OnSize` 中動態更新比例:
void CMainFrame::OnSize(UINT nType, int cx, int cy) {
CFrameWnd::OnSize(nType, cx, cy);
if (m_wndSplitter.GetSafeHwnd()) {
// 例:左列固定 200px,右列自適應
m_wndSplitter.SetColumnInfo(0, 200, 200);
m_wndSplitter.SetColumnInfo(1, cx - 200, 100);
m_wndSplitter.RecalcLayout();
}
}
MFC 分割器的**視圖尺寸由 `SetColumnInfo`/`SetRowInfo` 決定,而非 `CreateView` 的 `CSize` 參數**。巢狀分割器佈局的正確順序是:**建立結構 → 設定列行資訊 → `RecalcLayout`**。掌握此原理,即可輕鬆實現任意複雜的自適應多視圖介面。