MFC CPropertySheet 預設使用 CTabCtrl 切換頁面,介面傳統且難以客製化。現代應用常採用左側 列表控件 (CListCtrl) 作為導覽,右側顯示對應屬性頁。本文實作 CMyPropertySheet 繼承 CPropertySheet,替換 Tab 為 List,支援圖示、自訂顏色/字型、完整保留原生屬性頁功能。
// 1. 建立兩個 CPropertyPage 衍生頁面
CPageGeneral m_page1;
CPageAdvanced m_page2;
// 2. 建立屬性表並加入頁面
CMyPropertySheet myPS;
myPS.AddPage(&m_page1);
myPS.AddPage(&m_page2);
// 3. 設定列表項圖示 (順序與 AddPage 一致)
myPS.AddIcon(IDI_GLOBAL); // 第 0 頁圖示
myPS.AddIcon(IDI_ADDITION); // 第 1 頁圖示
// 4. 可選外觀調整
myPS.SetSeparatorColor(RGB(180,180,180)); // 分隔線色
myPS.SetCaptionColor(RGB(0,0,128)); // 標題列色
myPS.SetSelectedColor(RGB(200,220,255)); // 選取項背景色
myPS.SetListFont(_T("微軟正黑體"), 10); // 列表字型
// 5. 模態顯示
myPS.DoModal();
BOOL CMyPropertySheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// 1. 取得原始頁面矩形
CRect rectPage, rect, rectTab;
GetPage(0)->GetWindowRect(&rectPage);
GetWindowRect(&rect);
// 2. 擴寬視窗 150px 容納列表,頁面右移
rect.right += 150;
int nPageWidth = rectPage.Width();
rectPage.right = rect.right - 20;
rectPage.left = rectPage.right - nPageWidth;
ScreenToClient(&rectPage);
m_rectPage = rectPage; // 保存供切換時用
MoveWindow(&rect); // 調整外框
GetPage(0)->MoveWindow(&rectPage); // 移動第一頁
// 3. 隱藏原始 TabCtrl
CTabCtrl* pTab = GetTabControl();
pTab->GetWindowRect(&rectTab);
ScreenToClient(&rectTab);
pTab->ShowWindow(SW_HIDE);
// 4. 建立列表控件
m_wndList.Create(WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER,
CRect(10, rectTab.top, 150, rectPage.bottom), this, 0xFFFF);
m_wndList.SetExtendedStyle(LVS_EX_FULLROWSELECT);
m_wndList.SetImageList(&m_imgList, LVSIL_SMALL);
// 5. 初始化列表項 (從頁面標題+圖示)
InitList();
// 6. 設定列表行高 (字型大小決定)
CFont font;
font.CreatePointFont(240, _T("宋體")); // 12pt
m_wndList.SetFont(&font);
// 7. 同步第一頁標題
CString strCaption;
GetPage(0)->GetWindowText(strCaption);
_tcscpy(m_szCaption, strCaption);
return bResult;
}
void CMyPropertySheet::OnNMClick(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMITEMACTIVATE lpItem = reinterpret_cast(pNMHDR);
if (lpItem->iItem >= 0 && lpItem->iItem < m_wndList.GetItemCount())
{
m_nSelectedItem = lpItem->iItem;
CString strCaption = m_wndList.GetItemText(lpItem->iItem, 0);
_tcscpy(m_szCaption, strCaption);
SetActivePage(m_nSelectedItem); // MFC 內建切換邏輯
Invalidate(); // 重繪標題列
GetPage(m_nSelectedItem)->MoveWindow(&m_rectPage); // 確保位置
m_wndList.SetFocus();
}
*pResult = 0;
}
處理 NM_CUSTOMDRAW 繪製:
m_clrSelected、繪製焦點虛線框m_clrSeparatorDrawIconEx + TextOut,文字色 m_clrCaptionAddPage/RemovePage 同步更新列表)。Visual C++ 2005 (VC8) / MFC 8.0,VC6.0 需微調 reinterpret_cast 與 LVS_EX_FULLROWSELECT 相容性。
完整原始碼 MyPropertySheet.h/cpp 可直接加入專案使用。