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

MFC 對話框控件自適應大小類 ClxDialog 完整實作

     編輯:關於VC++

問題動機

在開發可調整大小的對話框時,若控件眾多,在 OnSize 中逐一計算座標極其繁瑕。本文提供一個可重複使用的 ClxDialog 類別,只需在 OnInitDialog 中以資料驅動方式宣告控件的錨點與彈性規則,即可自動處理控件的位置與大小隨對話框縮放而變化。

核心設計

控件行為旗標 (iFlag)

旗標值行為說明
MOVEX0水平移動
MOVEY1垂直移動
MOVEXY2水平與垂直同時移動
ELASTICX3寬度隨對話框寬度變化
ELASTICY4高度隨對話框高度變化
ELASTICXY5寬高同時隨對話框變化

百分比參數 (iPercent)

表示控件變化量佔對話框客戶區變化量的百分比 (0~100)。例如 iPercent=100, iFlag=MOVEX 表示對話框寬度每變化 1 單位,控件水平移動 1 單位;iPercent=50, iFlag=ELASTICX 表示控件寬度變化量為對話框寬度變化量的一半。

使用範例

// 在 OnInitDialog 中設定
static DLGCTLINFO dcMenuGroup[] =
{
    {IDOK, MOVEX, 100},           // 靠右對齊
    {IDCANCEL, MOVEX, 100},       // 靠右對齊
    {IDC_BUTTON1, MOVEX, 50},     // 水平置中
    {IDC_BUTTON1, MOVEY, 100},    // 靠底對齊
    {IDC_EDIT1, ELASTICX, 100},   // 寬度隨對話框全幅變化
    {IDC_EDIT2, ELASTICX, 50},    // 寬度隨對話框半幅變化
    {IDC_EDIT3, ELASTICX, 50},
    {IDC_EDIT3, MOVEX, 50},       // 同一控件可設定多條規則
    {IDC_EDIT4, ELASTICY, 100},   // 高度隨對話框全幅變化
    {IDC_EDIT5, ELASTICX, 100},
    {IDC_EDIT5, ELASTICY, 50},
    {IDC_EDIT6, ELASTICX, 100},
    {IDC_EDIT6, ELASTICY, 50},
    {IDC_EDIT6, MOVEY, 50},
};
SetControlProperty(dcMenuGroup, sizeof(dcMenuGroup)/sizeof(DLGCTLINFO));

類別結構重點

  • 繼承自 CDialog,覆寫 OnInitDialog、OnSize、OnSizing。
  • 成員變數:記錄初始客戶區大小 (m_iClientWidth/m_iClientHeight)、最小尺寸限制 (m_iMinWidth/m_iMinHeight)、控件規則陣列指標 (m_pControlArray)、規則數量 (m_iControlNumber)、尺寸圖示顯示旗標 (m_bShowSizeIcon) 等。
  • 關鍵方法:
    • SetControlProperty:注入控件規則陣列。
    • ShowSizeIcon:控制右下角尺寸調整圖示顯示。
    • OnInitDialog:啟用 WS_SIZEBOX、記錄初始尺寸、建立尺寸圖示靜態控件。
    • OnSize:依規則計算增量並呼叫 MoveWindow 調整每個控件,最後移動尺寸圖示至右下角。
    • OnSizing:限制對話框不得小於初始尺寸。

完整原始碼架構

lxDialog.h

class ClxDialog : public CDialog
{
public:
    ClxDialog(UINT nID, CWnd* pParent = NULL);
    typedef struct _dlgControlTag { int iId; int iFlag; int iPercent; } DLGCTLINFO, *PDLGCTLINFO;
    enum { MOVEX=0, MOVEY, MOVEXY, ELASTICX, ELASTICY, ELASTICXY };
    BOOL SetControlProperty(PDLGCTLINFO lp, int nElements);
    void ShowSizeIcon(BOOL bShow = TRUE);
protected:
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnSizing(UINT nSide, LPRECT lpRect);
    DECLARE_MESSAGE_MAP()
private:
    int m_iClientWidth, m_iClientHeight, m_iMinWidth, m_iMinHeight;
    PDLGCTLINFO m_pControlArray; int m_iControlNumber; BOOL m_bShowSizeIcon;
    CStatic m_wndSizeIcon; CBitmap m_bmpSizeIcon; BITMAP m_bitmap;
};

lxDialog.cpp 關鍵實作

BOOL ClxDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    ModifyStyle(0, WS_SIZEBOX); // 允許調整大小
    CRect rectDlg; GetWindowRect(rectDlg);
    m_iMinWidth = rectDlg.Width(); m_iMinHeight = rectDlg.Height();
    CRect rectClient; GetClientRect(rectClient);
    m_iClientWidth = rectClient.Width(); m_iClientHeight = rectClient.Height();
    // 載入系統 OBM_SIZE 圖示並建立右下角靜態控件
    m_bmpSizeIcon.LoadOEMBitmap(OBM_SIZE);
    m_bmpSizeIcon.GetBitmap(&m_bitmap);
    m_wndSizeIcon.Create(NULL, WS_CHILD|WS_VISIBLE|SS_BITMAP,
        CRect(0,0,m_bitmap.bmWidth,m_bitmap.bmHeight), this, 0);
    m_wndSizeIcon.SetBitmap(m_bmpSizeIcon);
    m_wndSizeIcon.MoveWindow(m_iClientWidth-m_bitmap.bmWidth, m_iClientHeight-m_bitmap.bmHeight,
        m_bitmap.bmWidth, m_bitmap.bmHeight);
    m_wndSizeIcon.ShowWindow(m_bShowSizeIcon);
    return TRUE;
}

void ClxDialog::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);
    int iIncrementX = cx - m_iClientWidth;
    int iIncrementY = cy - m_iClientHeight;
    if (nType == SIZE_MINIMIZED) iIncrementX = iIncrementY = 0;

    for (int i=0; i<m_iControlNumber; ++i)
    {
        // 取得控件指標、旗標、百分比
        // 依旗標計算新 left/top/width/height
        // 呼叫 pWndCtrl->MoveWindow(...)
    }
    // 移動尺寸圖示至新右下角
    if (IsWindow(m_wndSizeIcon.GetSafeHwnd()))
        m_wndSizeIcon.MoveWindow(cx-m_bitmap.bmWidth, cy-m_bitmap.bmHeight, m_bitmap.bmWidth, m_bitmap.bmHeight);
    if (nType != SIZE_MINIMIZED) { m_iClientWidth = cx; m_iClientHeight = cy; }
}

void ClxDialog::OnSizing(UINT nSide, LPRECT lpRect)
{
    CDialog::OnSizing(nSide, lpRect);
    int w = lpRect->right - lpRect->left;
    int h = lpRect->bottom - lpRect->top;
    if (w <= m_iMinWidth)  lpRect->right = lpRect->left + m_iMinWidth;
    if (h <= m_iMinHeight) lpRect->bottom = lpRect->top + m_iMinHeight;
}

效果展示

調整前 調整後

兩張截圖對比直觀呈現控件隨對話框縮放而自動重新佈局的效果。

總結

ClxDialog 以資料驅動方式封裝了對話框控件自適應邏輯,開發者只需宣告規則陣列即可輕鬆實現複雜的響應式佈局,大幅減少重複程式碼,提升維護性。

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