CTabCtrl 提供「分頁介面」,每個分頁對應一組子控件。切換分頁時,**開發者需自行顯示/隱藏對應子控件**,控件本身不管理子視窗生命週期。
// 建立
m_tab.Create(WS_CHILD | WS_VISIBLE | TCS_SINGLELINE,
CRect(10,10,300,200), this, IDC_TAB_MAIN);
// 分頁樣式
// TCS_TABS 標準分頁(預設)
// TCS_BUTTONS 按鈕外觀分頁
// TCS_MULTILINE 多行分頁
// TCS_SINGLELINE 單行分頁(預設)
// TCS_RIGHTJUSTIFY 右對齊
// TCS_FIXEDWIDTH 固定寬度分頁
// TCS_OWNERDRAWFIXED 自訂繪製固定高度
// 純文字
m_tab.InsertItem(0, _T("基本設定"));
m_tab.InsertItem(1, _T("進階選項"));
m_tab.InsertItem(2, _T("關於"));
// 帶圖示(需先設定 ImageList)
CImageList imgList;
imgList.Create(IDB_TAB_ICONS, 16, 1, RGB(255,0,255)); // 16x16, 透明色
m_tab.SetImageList(&imgList);
m_tab.InsertItem(0, _T("基本"), 0); // 圖示索引 0
m_tab.InsertItem(1, _T("進階"), 1);
m_tab.InsertItem(2, _T("關於"), 2);
// 訊息映射
ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_MAIN, OnTabSelChange)
ON_NOTIFY(TCN_SELCHANGING, IDC_TAB_MAIN, OnTabSelChanging)
// 目前選中分頁索引
int nCur = m_tab.GetCurSel();
m_tab.SetCurSel(1); // 程式切換至第 2 頁
// 刪除分頁
m_tab.DeleteItem(0);
m_tab.DeleteAllItems();
// 成員變量:每個分頁對應一個子對話框或控件群組
CEdit m_editPage1;
CListCtrl m_listPage2;
CButton m_btnPage3;
void CParentWnd::OnTabSelChange(NMHDR* pNMHDR, LRESULT* pResult)
{
int nPage = m_tab.GetCurSel();
// 隱藏所有
m_editPage1.ShowWindow(SW_HIDE);
m_listPage2.ShowWindow(SW_HIDE);
m_btnPage3.ShowWindow(SW_HIDE);
// 顯示對應頁面
switch (nPage)
{
case 0: m_editPage1.ShowWindow(SW_SHOW); break;
case 1: m_listPage2.ShowWindow(SW_SHOW); break;
case 2: m_btnPage3.ShowWindow(SW_SHOW); break;
}
*pResult = 0;
}
// 切換前可攔截(如未儲存資料詢問)
void CParentWnd::OnTabSelChanging(NMHDR* pNMHDR, LRESULT* pResult)
{
if (m_bDataDirty && AfxMessageBox(_T("資料未儲存,確定切換?"), MB_YESNO) != IDYES)
{
*pResult = TRUE; // 取消切換
return;
}
*pResult = FALSE; // 允許切換
}
將每個分頁內容封裝為 CDialog 派生類(Style=Child, Border=None),作為 Tab Control 的子視窗:
// 頁面對話框類別
class CPage1Dlg : public CDialog
{
DECLARE_DYNCREATE(CPage1Dlg)
public:
CPage1Dlg() : CDialog(IDD_PAGE1) {}
// DDX/DDV、訊息映射...
};
// 父視窗建立頁面
void CParentWnd::CreatePages()
{
m_page1.Create(IDD_PAGE1, &m_tab);
m_page2.Create(IDD_PAGE2, &m_tab);
m_page3.Create(IDD_PAGE3, &m_tab);
// 定位於 Tab Control 客戶區
CRect rc;
m_tab.GetClientRect(&rc);
m_tab.AdjustRect(FALSE, &rc); // 取得分頁內容區域
m_page1.MoveWindow(&rc);
m_page2.MoveWindow(&rc);
m_page3.MoveWindow(&rc);
m_page1.ShowWindow(SW_SHOW);
m_page2.ShowWindow(SW_HIDE);
m_page3.ShowWindow(SW_HIDE);
}
void CParentWnd::OnTabSelChange(NMHDR*, LRESULT* pResult)
{
int n = m_tab.GetCurSel();
m_page1.ShowWindow(n==0 ? SW_SHOW : SW_HIDE);
m_page2.ShowWindow(n==1 ? SW_SHOW : SW_HIDE);
m_page3.ShowWindow(n==2 ? SW_SHOW : SW_HIDE);
*pResult = 0;
}
| 函式 | 用途 |
|---|---|
GetCurSel() | 取得目前分頁索引(-1 表無選中) |
SetCurSel(n) | 設定目前分頁 |
GetItemCount() | 分頁總數 |
GetItem(n, TCITEM*) | 取得分頁屬性(文字、圖示、資料指標) |
SetItem(n, TCITEM*) | 修改分頁屬性 |
GetItemRect(n, LPRECT) | 取得分頁標籤矩形 |
AdjustRect(BOOL bLarger, LPRECT) | 計算內容區域(bLarger=FALSE 縮小) |
SetImageList(CImageList*) | 設定圖示清單 |
RemoveImage(n) | 移除指定圖示 |
TCS_OWNERDRAWFIXED 樣式。WM_DRAWITEM(ON_WM_DRAWITEM())。
void CParentWnd::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDIS)
{
if (nIDCtl == IDC_TAB_MAIN && lpDIS->CtlType == ODT_TAB)
{
CDC dc; dc.Attach(lpDIS->hDC);
CRect rc = lpDIS->rcItem;
int nIndex = lpDIS->itemID;
BOOL bSelected = lpDIS->itemState & ODS_SELECTED;
// 背景
dc.FillSolidRect(rc, bSelected ? RGB(255,255,255) : RGB(230,230,230));
// 邊框
dc.DrawEdge(rc, bSelected ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT);
// 文字
CString str = m_tab.GetItemText(nIndex);
dc.SetBkMode(TRANSPARENT);
dc.DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.Detach();
return;
}
CWnd::OnDrawItem(nIDCtl, lpDIS);
}
WS_CLIPCHILDREN | WS_CLIPSIBLINGS 父視窗樣式,或雙緩衝繪製。PreTranslateMessage 需正確轉發,或確保子對話框 Style=Child。ImageList 色深與螢幕不符、圖示尺寸不匹配(通常 16x16)、透明色設定錯誤。TCITEM::lParam 指標,而非依賴索引。