| 函數 | 用途 | 關鍵參數 |
|---|---|---|
Create | 動態分割 (可拖曳分割列) | max rows/cols ≤ 2x2、共用同一 View 類別 |
CreateStatic | 靜態分割 (固定行列) | rows/cols ≤ 16x16、每格可填不同 View |
CreateView | 填充靜態分割格 | row, col, RUNTIME_CLASS, size, pContext |
本質:CSplitterWnd 繼承自 CWnd,內部建立一系列 MDI 子視窗 (_afxWndMDIFrame)。
目標佈局:
+-------------------+ | CCuteFTPView | Row 0 +-------------------+ | CView2 | CView3 | Row 1 → 巢狀分割器 1x2 +-------------------+ | CView4 | Row 2 +-------------------+
CSplitterWnd m_wndSplitter1; // 主分割器 3x1
CSplitterWnd m_wndSplitter2; // 巢狀分割器 1x2
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext) {
// 1. 主分割器 3 行 1 列
if (!m_wndSplitter1.CreateStatic(this, 3, 1)) return FALSE;
m_wndSplitter1.CreateView(0, 0, RUNTIME_CLASS(CCuteFTPView), CSize(100,100), pContext);
m_wndSplitter1.CreateView(2, 0, RUNTIME_CLASS(CView4), CSize(100,100), pContext);
// 2. 第 1 列 (索引 1) 再分 1 行 2 列
if (!m_wndSplitter2.CreateStatic(&m_wndSplitter1, 1, 2,
WS_CHILD | WS_VISIBLE, m_wndSplitter1.IdFromRowCol(1, 0)))
return FALSE;
m_wndSplitter2.CreateView(0, 0, RUNTIME_CLASS(CView2), CSize(400,300), pContext);
m_wndSplitter2.CreateView(0, 1, RUNTIME_CLASS(CView3), CSize(400,300), pContext);
return TRUE;
}
關鍵:巢狀分割器的父視窗是**外層分割器**,ID 用 IdFromRowCol 取得。
| 情境 | 路徑 | 關鍵代碼 |
|---|---|---|
| 文件關聯視圖 ↔ 文件關聯視圖 | 透過 CDocument 指標清單 | pDoc->GetNextView(pos) + IsKindOf 取得指標 |
| 無文件視圖 → 文件關聯視圖 | 取得 MainFrame → ActiveDocument → 文件視圖清單 | (CMainFrame*)GetParent()->GetParent() → GetActiveDocument() |
| 無文件視圖 ↔ 無文件視圖 | 取得 MainFrame → 分割器 GetPane(row,col) | m_wndSplitter1.GetPane(2,0) 直接取得 CView4 指標 |
MFC 預設 GetParentFrame() 尋找框架視窗,對話框無框架 → 重寫關鍵虛函數:
class CxSplitterWnd : public CSplitterWnd {
virtual CWnd* GetActivePane(int*, int*); // 用 GetFocus() 替代框架視窗
virtual void SetActivePane(int, int, CWnd*); // 用 SetFocus() 替代框架視窗
virtual void StartTracking(int); // 移除框架視窗相關碼
virtual BOOL OnCommand(WPARAM, LPARAM); // 轉發給 GetParent()
virtual BOOL OnNotify(WPARAM, LPARAM, LRESULT*); // 轉發給 GetParent()
virtual BOOL OnWndMsg(UINT, WPARAM, LPARAM, LRESULT*); // 標準路由
};
完整代碼見原文,核心替換:GetParentFrame() → GetParent()、SetActiveView → SetFocus。
void CMySplitter::OnLButtonDown(UINT, CPoint) { CWnd::OnLButtonDown(...); } // 吞噬訊息
void CMySplitter::OnMouseMove(UINT, CPoint) { CWnd::OnMouseMove(...); }
void CMySplitter::OnSetCursor(CWnd*, UINT, UINT) { CWnd::OnSetCursor(...); }
void CMySplitter::OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect) {
if (!pDC) { RedrawWindow(rect, NULL, RDW_INVALIDATE|RDW_NOCHILDREN); return; }
switch (nType) {
case splitBorder: // 邊框紅色
pDC->Draw3dRect(rect, RGB(255,0,0), RGB(255,0,0));
rect.DeflateRect(CX_BORDER, CY_BORDER);
pDC->Draw3dRect(rect, RGB(255,0,0), RGB(255,0,0));
break;
case splitBox: // 分割盒黑色
pDC->Draw3dRect(rect, RGB(0,0,0), RGB(0,0,0));
break;
case splitBar: // 分割列綠色背景 + 紅框
pDC->FillSolidRect(rect, RGB(255,255,255));
rect.DeflateRect(5,5);
pDC->Draw3dRect(rect, RGB(255,0,0), RGB(255,0,0));
break;
}
}
void CMySplitter::OnInvertTracker(CRect& rect) { // 拖曳軌跡反相
rect.InflateRect(2,2);
CDC* pDC = GetDC();
CBrush* pBrush = CDC::GetHalftoneBrush();
HBRUSH hOld = (HBRUSH)SelectObject(pDC->m_hDC, pBrush->m_hObject);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), DSTINVERT);
SelectObject(pDC->m_hDC, hOld); ReleaseDC(pDC);
}
作者:張中慶 (tingya@njpost.com.cn) 南京郵政局計算機中心。