首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

CSplitterWnd 任意分割視窗完全指南:靜態/動態/巢狀/對話框/客製化

     編輯:關於VC++

核心類別與建立函數

函數用途關鍵參數
Create動態分割 (可拖曳分割列)max rows/cols ≤ 2x2、共用同一 View 類別
CreateStatic靜態分割 (固定行列)rows/cols ≤ 16x16、每格可填不同 View
CreateView填充靜態分割格row, col, RUNTIME_CLASS, size, pContext

本質:CSplitterWnd 繼承自 CWnd,內部建立一系列 MDI 子視窗 (_afxWndMDIFrame)。

巢狀分割實作:CuteFTP 風格三層分割

目標佈局:

+-------------------+
|   CCuteFTPView    |  Row 0
+-------------------+
|  CView2 | CView3  |  Row 1 → 巢狀分割器 1x2
+-------------------+
|      CView4       |  Row 2
+-------------------+

CMainFrame 成員


CSplitterWnd m_wndSplitter1; // 主分割器 3x1
CSplitterWnd m_wndSplitter2; // 巢狀分割器 1x2

OnCreateClient 實作


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 指標

對話框專案使用 CSplitterWnd

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。

客製化擴展

1. 鎖定分割列 (禁止拖曳)


void CMySplitter::OnLButtonDown(UINT, CPoint) { CWnd::OnLButtonDown(...); } // 吞噬訊息
void CMySplitter::OnMouseMove(UINT, CPoint)   { CWnd::OnMouseMove(...); }
void CMySplitter::OnSetCursor(CWnd*, UINT, UINT) { CWnd::OnSetCursor(...); }

2. 分割列/邊框/交叉點繪製客製化


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);
}

總結

  • 靜態分割 + 巢狀分割器 = 任意複雜佈局
  • 視圖通訊:有文件走文件,無文件走框架/分割器
  • 對話框專案重寫 6 個虛函數即可相容
  • 繪製虛函數開放完整視覺客製化

作者:張中慶 (tingya@njpost.com.cn) 南京郵政局計算機中心。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved