專案需求:實現類似 QQ 遊戲大廳側邊欄的樹型導航控件,支援自定義項目圖片、展開/摺疊圖示、漸層背景、橫向分隔線、根節點背景等視覺效果。本文記錄 CTreeCtrlBT : public CTreeCtrl 完整設計與實現細節。
struct ITEM_MSG {
HTREEITEM hItem; // 項目句柄
CBitmap ItemBitmap; // 項目圖片
COLORREF crTransparent; // 透明色 (預設白色 RGB(255,255,255))
ITEM_MSG() : hItem(NULL), crTransparent(RGB(255,255,255)) {}
~ITEM_MSG() { if (ItemBitmap.GetSafeHandle()) ItemBitmap.DeleteObject(); }
};
typedef std::map<HTREEITEM, ITEM_MSG*> ITEMMSG_MAP;
std::map 以 HTREEITEM 為鍵儲存每個節點的視覺屬性CBitmap 資源| 訊息/虛函數 | 用途 |
|---|---|
PreSubclassWindow() | 初始化控件樣式、建立預設圖片清單 |
WM_SIZE | 重新計算客戶區大小、捲軸偏移 |
WM_PAINT | 完整自繪:背景、節點、展開按鈕、圖片、文字、分隔線 |
WM_MOUSEMOVE | 追蹤滑鼠懸停節點、展開按鈕熱區 |
// 背景圖片
BOOL SetBackgroudBitmap(UINT nID);
BOOL SetBackgroudBitmap(LPCTSTR lpszRecourceName);
// 節點圖片
void SetItemBitmap(HTREEITEM hItem, UINT nIdBitmap);
void SetItemBitmap(HTREEITEM hItem, LPCTSTR lpszBitmap);
// 展開/摺疊圖示
void SetExpandBitmap(UINT nIdExpand, UINT nIdCollapse, COLORREF crTransparent = RGB(255,255,255));
// 擴充插入項目 (支援圖片與透明色)
HTREEITEM InsertItemEx(HTREEITEM hParent, LPCTSTR lpszItem, UINT nIdBitmap = 0, COLORREF crTransparent = RGB(255,255,255));
HTREEITEM InsertItemEx(HTREEITEM hParent, LPCTSTR lpszItem, LPCTSTR lpszBitmap, COLORREF crTransparent = RGB(255,255,255));
// 視覺選項
void EnableRowLine(BOOL bEnable = TRUE); // 橫向分隔線
BOOL IsEnableRowLine();
void EnableRootBk(BOOL bEnable = TRUE); // 根節點背景
BOOL IsEnableRootBk();
// 內部查詢
ITEM_MSG* GetItemMsg(HTREEITEM hItem);
void CTreeCtrlBT::GradientFillRect(CDC* pDC, CRect& rect, COLORREF col_from, COLORREF col_to, bool vert_grad) {
TRIVERTEX vert[2] = {
{rect.left, rect.top, GetRValue(col_from)<<8, GetGValue(col_from)<<8, GetBValue(col_from)<<8, 0xFFFF},
{rect.right, rect.bottom, GetRValue(col_to)<<8, GetGValue(col_to)<<8, GetBValue(col_to)<<8, 0xFFFF}
};
GRADIENT_RECT gRect = {0, 1};
pDC->GradientFill(vert, 2, &gRect, 1, vert_grad ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);
}
void CTreeCtrlBT::DrawBackgroudBitmap(CDC* pDC) {
if (!m_bmpBackground.GetSafeHandle()) return;
BITMAP bm; m_bmpBackground.GetBitmap(&bm);
CDC memDC; memDC.CreateCompatibleDC(pDC);
CBitmap* pOld = memDC.SelectObject(&m_bmpBackground);
pDC->TransparentBlt(0, 0, m_rectClient.Width(), m_rectClient.Height(),
&memDC, 0, 0, bm.bmWidth, bm.bmHeight, m_crBkTransparent);
memDC.SelectObject(pOld);
}
注意:TransparentBlt 需連結 msimg32.lib。
void CTreeCtrlBT::DrawItem(CDC* pDC) {
// 計算可視範圍、捲軸偏移
// 遍歷可視節點:
// DrawRowLine() // 底部分隔線
// DrawExpand() // 展開/摺疊按鈕 (± 圖示)
// DrawItemBitmap() // 節點圖片
// DrawItemText() // 節點文字 (選中/未選中色)
}
DrawExpand(CRect, state) 根據 state (展開/摺疊/熱區) 繪製對應幀DrawText 配合 DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS,選中項使用高亮色GetScrollPos(SB_VERT/HORZ)void CTreeCtrlBT::Calculate() {
// 取得客戶區矩形 m_rectClient
// 計算所有節點總高度 → 設定垂直捲軸範圍 SetScrollInfo(SB_VERT, ...)
// 計算最寬節點寬度 → 設定水平捲軸範圍
// 記錄首個可見節點索引、可見節點數
}
開發過程參考了 CodeProject 上 VividTree 專案的部分思路:
https://www.codeproject.com/KB/tree/VividTree.aspx
CTreeCtrlBT 透過完全接管 WM_PAINT,實現了高度可定制的樹型導航視覺效果。核心難點在於:
TransparentBlt + msimg32.lib)GradientFill / TRIVERTEX)此控件可直接整合至 MFC 專案,支援資源式與檔案式圖片載入,適合需要強品牌視覺識別的導航介面開發。