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

VC++ 對話方塊整合屬性頁:三種方案比較與 CPropertySheet 進階嵌入技巧

     編輯:VC 教學

前言

當對話方塊控制項過多,分類管理成為必要。MFC 提供 `CPropertySheet`(屬性工作表)與 `CPropertyPage`(屬性頁)標準架構。本文比較三種整合方案:方案一:單一對話方塊 + Tab Control 手動顯示/隱藏、方案二:標準 PropertySheet 模態/非模態彈出、方案三:繼承 CPropertySheet 嵌入主對話方塊並保留自有控制項。重點解析方案三的嵌入技巧與座標計算。

一、方案一:Tab Control 手動切換(不推薦)

  • 所有控制項仍在同一對話方塊資源,編輯器操作擁擠。
  • 需為每個控制項建立變數呼叫 `ShowWindow(SW_HIDE/SHOW)`,類別膨脹。
  • DDX/DDV 難以分組管理。

// 核心邏輯:TCN_SELCHANGE 通知中切換可見性
void CMyDialog::OnSelchangeTab(NMHDR* pNMHDR, LRESULT* pResult)
{
    int nTab = m_tab.GetCurSel();
    m_group1.ShowWindow(nTab == 0 ? SW_SHOW : SW_HIDE);
    m_group2.ShowWindow(nTab == 1 ? SW_SHOW : SW_HIDE);
    *pResult = 0;
}

二、方案二:標準 PropertySheet(彈出視窗)

  1. 新增對話方塊資源,Style=Child, Border=Thin, 勾選 Title Bar。
  2. 建立類別繼承 `CPropertyPage`(如 `CPage1`、`CPage2`)。
  3. 主程式或按鈕處理中建立 Sheet:

void CMainDialog::OnShowPropSheet()
{
    CPropertySheet sheet(_T("設定選項"));
    CPage1 page1; CPage2 page2;
    sheet.AddPage(&page1);
    sheet.AddPage(&page2);
    sheet.DoModal(); // 或 sheet.Create(this) 非模態
}

缺點:無法在主對話方塊保留其他控制項(如工具列、狀態列、常用按鈕)。

三、方案三:繼承 CPropertySheet 嵌入主對話方塊(推薦)

1. 建立自訂 Sheet 類別


// MySheet.h
class CMySheet : public CPropertySheet
{
    DECLARE_DYNAMIC(CMySheet)
public:
    CMySheet(UINT nIDCaption, CWnd* pParent = NULL, UINT iSelectPage = 0);
    CMySheet(LPCTSTR pszCaption, CWnd* pParent = NULL, UINT iSelectPage = 0);
    virtual ~CMySheet();

    CButton m_btnExtra; // 主對話區額外按鈕

protected:
    DECLARE_MESSAGE_MAP()
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    virtual BOOL OnInitDialog();
};

2. OnInitDialog 調整大小、建立額外控制項、子類化分頁


BOOL CMySheet::OnInitDialog()
{
    BOOL bResult = CPropertySheet::OnInitDialog();

    // 1. 取得目前視窗大小
    CRect rcSheet; GetWindowRect(&rcSheet);

    // 2. 擴寬視窗以容納右側額外按鈕區(寬度 +100)
    SetWindowPos(NULL, 0, 0, rcSheet.Width() + 100, rcSheet.Height(),
                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

    // 3. 建立額外按鈕(座標相對擴寬後的客戶區)
    CRect rcBtn(rcSheet.Width() + 10, 20, rcSheet.Width() + 90, 50);
    m_btnExtra.Create(_T("執行"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, rcBtn, this, IDC_BTN_EXTRA);

    // 4. 移除預設的 OK/Cancel/Apply 按鈕(可選)
    GetDlgItem(IDOK)->ShowWindow(SW_HIDE);
    GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);
    GetDlgItem(ID_APPLY_NOW)->ShowWindow(SW_HIDE);

    // 5. 將分頁頁籤移至底部(可選)
    // ModifyStyle(0, PSH_PROPTITLE); // 不同版本行為不同

    return bResult;
}

3. 主對話方塊建立並嵌入 Sheet


void CMainDialog::OnShowEmbeddedSheet()
{
    // 建立非模態 Sheet,父視窗為 this
    if (!m_pSheet)
    {
        m_pSheet = new CMySheet(_T("嵌入式屬性頁"), this);
        m_pSheet->AddPage(&m_page1);
        m_pSheet->AddPage(&m_page2);
        m_pSheet->Create(this, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0);
    }

    // 定位 Sheet 於主對話方塊客戶區特定矩形
    CRect rc; GetClientRect(&rc);
    rc.DeflateRect(10, 10, 10, 40); // 留邊距
    m_pSheet->SetWindowPos(NULL, rc.left, rc.top, rc.Width(), rc.Height(),
                           SWP_NOZORDER | SWP_NOACTIVATE);
    m_pSheet->ShowWindow(SW_SHOW);
}

4. 釋放資源


CMainDialog::~CMainDialog()
{
    if (m_pSheet)
    {
        m_pSheet->DestroyWindow();
        delete m_pSheet;
        m_pSheet = NULL;
    }
}

四、執行效果

嵌入式屬性頁示範

主對話方塊保留標題列、工具列,中間區域嵌入帶分頁的屬性頁,右側保留自訂按鈕,分頁切換流暢,資料交換獨立於各 `CPropertyPage` 衍生類。

五、關鍵技巧總結

技巧說明
Style=Child, Border=Thin屬性頁資源必須設為 Child 模式,否則無法嵌入
Create() 而非 DoModal()非模態建立,傳入父視窗指標
SetWindowPos 定位嵌入後需手動調整大小位置適應主對話區
隱藏預設按鈕IDOK/IDCANCEL/ID_APPLY_NOW 在嵌入模式下通常不需要
訊息路由Sheet 為子視窗,命令訊息自動路由至對應 Page 類別

六、結語

方案三結合了 `CPropertySheet` 的分頁管理優勢與主對話方塊的佈局彈性,是大型對話方塊應用的最佳架構。關鍵在於:頁面資源設為 Child → Sheet 繼承類別建立額外控制項 → 主對話方塊非模態 Create 並定位。此模式廣泛應用於 IDE 選項對話框、資料庫設定精靈、多步驟安裝嚮導等場景。

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