在 Windows XP 的資源管理器左側目錄樹中,當滑鼠游標移動到某個項目上時,該項目標籤的字體與顏色會發生變化,使選擇目標更加醒目。本文介紹如何在基於 MFC 的 CTreeCtrl 控件衍生類 CDirTreeCtrl 中實現類似效果。核心思路是:重載滑鼠移動訊息 (WM_MOUSEMOVE) 以偵測游標是否進入項目標籤區域,並記錄需變更的顏色與字體屬性;再重載繪製訊息 (WM_PAINT),在裝置環境中建立帶有底線屬性的邏輯字體,並以設定的顏色重繪項目文字。
在 CDirTreeCtrl 的標頭檔中宣告以下變數:
HTREEITEM hItem_bc; // 記錄目前游標所在的項目控制代碼
COLORREF ItemText_OldColor; // 系統預設項目文字顏色,於初始化時取得
LOGFONT logfont_old; // 系統預設邏輯字體屬性,於初始化時取得
struct Color_Font {
COLORREF color;
LOGFONT logfont;
};
// 以項目控制代碼為鍵,儲存對應的顏色與字體設定
CMap<void*, void*, Color_Font, Color_Font&> m_mapColorFont;
void CDirTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
CRect rc;
TVHITTESTINFO tvInfo;
tvInfo.pt = point;
HTREEITEM hItem = HitTest(&tvInfo); // 取得游標下的項目控制代碼
GetItemRect(hItem, &rc, TRUE);
BOOL yn = rc.PtInRect(point); // 判斷游標是否在項目標籤矩形內
Color_Font cf;
if (yn) { // 游標進入項目標籤
cf.color = RGB(0, 0, 150); // 設定高亮顏色(深藍)
SetCursor(hIcon_sx); // 切換為手型游標
hItem_bc = hItem;
} else { // 游標離開
cf.color = ItemText_OldColor; // 恢復預設顏色
}
m_mapColorFont[hItem] = cf; // 更新對應項目的顏色/字體記錄
Invalidate(FALSE); // 觸發重繪
CTreeCtrl::OnMouseMove(nFlags, point);
}
void CDirTreeCtrl::OnPaint()
{
CPaintDC dc(this);
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CRect rcClip, rcClient;
dc.GetClipBox(&rcClip);
GetClientRect(&rcClient);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
memDC.SelectObject(&bitmap);
// 讓預設繪製流程先在記憶體 DC 上執行
CWnd::DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0);
if (hItem_bc != NULL) {
CRect rect;
UINT selflog = TVIS_DROPHILITED | TVIS_SELECTED;
Color_Font cf;
// 若項目非拖放高亮也非選中狀態,且有自訂字體記錄
if (!(GetItemState(hItem_bc, selflog) & selflog) && m_mapColorFont.Lookup(hItem_bc, cf)) {
CFont fontDC;
LOGFONT logfont = logfont_old;
logfont.lfUnderline = TRUE; // 加底線
fontDC.CreateFontIndirect(&logfont);
CFont* pOldFont = memDC.SelectObject(&fontDC);
memDC.SetTextColor(cf.color);
CString sItem = GetItemText(hItem_bc);
GetItemRect(hItem_bc, &rect, TRUE);
memDC.TextOut(rect.left + 2, rect.top + 2, sItem); // 繪製文字
memDC.SelectObject(pOldFont);
fontDC.DeleteObject();
}
}
// 將記憶體 DC 內容複製到螢幕 DC
dc.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(),
&memDC, rcClip.left, rcClip.top, SRCCOPY);
bitmap.DeleteObject();
hItem_bc = NULL; // 重置
}
透過雙重緩衝繪製與字體屬性的動態切換,即可在樹狀控件中實現類似 Windows XP 資源管理器的項目高亮效果,提升使用者介面的視覺回饋。