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

VC++ 6.0 對話方塊動態佈局方案 DynamicLayout:控制項隨視窗縮放、分割條拖曳、面板巢狀

     編輯:VC 教學

前言

VC++ 6.0 原生不支援對話方塊控制項隨視窗大小自動調整。`DynamicLayout` 是一個輕量級 MFC 佈局管理器,採用**面板巢狀**模型(參考 .NET `TableLayoutPanel`/`FlowLayoutPanel`),支援控制項錨定(上下左右)、分割條拖曳調整面板大小、最小/最大視窗限制、面板背景色。核心類別 `CDynamicLayout` 透明處理 `WM_SIZE`、`WM_MOUSEMOVE`、`WM_LBUTTONDOWN/UP` 等訊息,使用者僅需在 `OnInitDialog` 宣告式註冊佈局結構。

一、核心概念

  • Panel(面板):容器,可包含控制項或子面板,擁有矩形區域與錨定規則。
  • Anchor(錨定):四個布林值 `bLeft, bTop, bRight, bBottom`,決定面板/控制項邊緣是否跟隨父面板對應邊緣移動/縮放。
  • Splitter(分割條):在兩個相鄰面板之間插入可拖曳分割條,調整兩側面板大小比例。

二、介面宣告


namespace DynamicLayout
{
    class CDynamicLayout
    {
    public:
        void InitLayout(CWnd* pDialog); // 必須最先呼叫
        void SetDialogMinSize(int cx, int cy);
        void SetDialogMaxSize(int cx, int cy);
        void SetBkColor(LPCTSTR pszPanelName, COLORREF clr);

        // 註冊面板:pszName=面板名稱, pszParent=父面板名稱(""=根對話框)
        // bLeft,bTop,bRight,bBottom = 錨定規則
        void AddPanel(LPCTSTR pszName, LPCTSTR pszParent, 
                      BOOL bLeft, BOOL bTop, BOOL bRight, BOOL bBottom);

        // 註冊控制項到面板
        void AddControl(int nCtrlID, LPCTSTR pszPanel,
                        BOOL bLeft, BOOL bTop, BOOL bRight, BOOL bBottom);

        // 顯示/隱藏面板
        void ShowPanel(LPCTSTR pszName, BOOL bShow);

        // 在面板之間插入分割條
        void SetSplit(LPCTSTR pszPanelName); // pszPanelName="" 為根分割條

        // 核心訊息過濾器:必須在對話框 WindowProc 中呼叫
        void FilterDynamicLayoutMessage(UINT msg, WPARAM wp, LPARAM lp);
    };
}

三、使用範例:複雜巢狀佈局


class CMyDialog : public CDialog
{
    DynamicLayout::CDynamicLayout m_layout;
    // ...
    virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
};

BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

    m_layout.InitLayout(this);
    m_layout.SetDialogMinSize(200, 100);
    m_layout.SetDialogMaxSize(800, 600);

    // 根面板分上下
    m_layout.AddPanel(_T("上"), _T(""), 1,1,1,1); // 四邊錨定
    m_layout.AddPanel(_T("下"), _T(""), 0,1,1,1); // 底部錨定

    // 上面板分左右
    m_layout.AddPanel(_T("上左"), _T("上"), 1,1,1,0);
    m_layout.AddPanel(_T("上右"), _T("上"), 1,1,1,1);

    // 上右再分上下
    m_layout.AddPanel(_T("上右下"), _T("上右"), 0,1,1,0);

    // 配置控制項
    m_layout.AddControl(IDC_TREE1, _T("上左"), 1,1,1,1);
    m_layout.AddControl(IDC_LIST_LIST, _T("上右"), 1,1,1,1);
    m_layout.AddControl(IDC_BTN_MODAL, _T("上右下"), 1,1,1,0);
    m_layout.AddControl(IDC_BTN_MODELESS, _T("上右下"), 1,1,1,0);
    m_layout.AddControl(IDOK, _T("上右下"), 1,1,1,0);

    m_layout.AddControl(IDC_LIST2, _T("下左"), 1,1,1,1);
    m_layout.AddControl(IDC_EDIT1, _T("下右"), 1,1,1,1);
    m_layout.AddControl(IDC_STATIC_CMD, _T("下右"), 1,0,1,0);
    m_layout.AddControl(IDC_EDIT2, _T("下右"), 1,0,1,1);

    // 面板背景色區分
    m_layout.SetBkColor(_T("上"), RGB(160,160,255));
    m_layout.SetBkColor(_T("上右"), RGB(128,128,255));
    m_layout.SetBkColor(_T("上右下"), RGB(128,255,128));

    // 分割條
    m_layout.SetSplit(_T(""));     // 根:上/下 分割條
    m_layout.SetSplit(_T("上"));   // 上面板:左/右 分割條
    m_layout.SetSplit(_T("下"));   // 下面板:左/右 分割條

    m_layout.ShowPanel(_T(""), TRUE); // 顯示所有

    return TRUE;
}

LRESULT CMyDialog::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
    m_layout.FilterDynamicLayoutMessage(msg, wp, lp);
    return CDialog::WindowProc(msg, wp, lp);
}

四、執行效果

原始對話框

加入 DynamicLayout

縮放視窗

拖曳分割條 A

拖曳分割條 C

任意佈局

五、實作關鍵點

  • WindowProc 攔截:`FilterDynamicLayoutMessage` 必須在 `WindowProc` 最前呼叫,確保 `WM_NCHITTEST`(分割條游標)、`WM_MOUSEMOVE`(拖曳中)、`WM_LBUTTONUP`(結束拖曳)優先處理。
  • 延遲重繪:拖曳分割條時僅移動分割條視覺標記,滑鼠釋放後一次性 `MoveWindow` 所有受影響控制項,避免閃爍。
  • 錨定邏輯:`OnSize` 計算每個面板新矩形:`left = parent.left + (bLeft?0:parent.Width()*ratio)` 等,再遞歸調整子面板與控制項。
  • 最小尺寸保護:面板/控制項縮小時不小於 `GetSystemMetrics(SM_CXMIN)` 等。

六、擴充建議

  • 序列化儲存:記住使用者調整的分割條位置、面板顯示/隱藏狀態,下次啟動還原。
  • 動畫過渡:分割條釋放時以 Timer 漸變動畫移動控制項至新位置。
  • 高 DPI 感知:佈局計算使用邏輯單位,配合 `GetDpiForWindow` 縮放。
  • 屬性頁支援:`CPropertyPage` 同樣適用,只需在 `OnInitDialog` 初始化。

七、結語

`DynamicLayout` 以宣告式 API 解決 VC++ 6.0 對話方塊佈局痛點,核心代碼約 500 行,零外部依賴,編譯輕量。面板巢狀 + 錨定 + 分割條 模型直觀強大,足以應對多數商業應用的複雜介面需求。現代專案若升級至 VS2010+,可直接使用 MFC 內建 `CMFCDynamicLayout`(`EnableDynamicLayout`),API 設計理念幾乎相同,遷移成本極低。

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