技术支持 重庆网站,营销型网站商城,南通网站排名,个人网站空间一般多大在本教程中#xff0c;将介绍如何在 MFC 应用程序中使用树控件 (CTreeCtrl) 进行高级定制#xff0c;包括设置字体、颜色、徽章、图标、节点的高度等。通过这些自定义设置#xff0c;可以显著提升用户界面的交互性和视觉效果。
1. 树控件基本设置
首先#xff0c;我们需要…在本教程中将介绍如何在 MFC 应用程序中使用树控件 (CTreeCtrl) 进行高级定制包括设置字体、颜色、徽章、图标、节点的高度等。通过这些自定义设置可以显著提升用户界面的交互性和视觉效果。
1. 树控件基本设置
首先我们需要初始化树控件并进行一些基础的设置。以下代码展示了如何设置树控件的字体、背景色、选中项、悬停项等。
// 设置树控件的背景色、选中项、悬停项及文本颜色
m_treeCtrl.UpdateFont(FontType::FONT_SEGOE_UI, 16); // 设置字体
m_treeCtrl.SetDefaultBkColor(RGB(240, 240, 240)); // 浅灰色背景
m_treeCtrl.SetSelectedBkColor(RGB(0, 0, 255)); // 蓝色选中项
m_treeCtrl.SetHoverBkColor(RGB(255, 255, 0)); // 黄色悬停项
m_treeCtrl.SetDefaultTextColor(RGB(0, 0, 0)); // 默认黑色文本
m_treeCtrl.SetSelectedTextColor(RGB(255, 0, 255)); // 白色选中文本
m_treeCtrl.SetHoverTextColor(RGB(255, 0, 0)); // 红色悬停项文本在这里我们设置了树控件的字体为 Segoe UI字号为 16。然后定义了不同状态下的背景色和文本颜色包括默认状态、选中状态和悬停状态的颜色。
2. 设置树项的高度
通过调用 SetItemHeight 方法我们可以设置树控件中每个树项的高度。此方法允许我们对树项进行自定义布局使界面更符合设计要求。
// 设置树项高度
m_treeCtrl.SetItemHeight(50);这段代码将树项的高度设置为 50使得每个节点的显示区域更大适合显示更多内容。
3. 插入节点并设置样式
树控件允许我们动态插入节点并为每个节点设置不同的样式。以下是如何插入根节点和子节点并为其设置背景色、图标、徽章等。
插入根节点
// 插入根节点及其子节点
HTREEITEM hRoot m_treeCtrl.InsertItem(_T(Root Node));
m_treeCtrl.SetNodeBkColor(hRoot, RGB(100, 100, 255)); // 蓝色背景
m_treeCtrl.SetItemIcon(hRoot, m_hIcon); // 设置图标在此我们插入了一个名为“Root Node”的根节点并设置了它的背景色为蓝色以及图标。
插入子节点并设置徽章
// 插入子节点及设置徽章
HTREEITEM hChild m_treeCtrl.InsertItem(_T(Child Node), hRoot);
m_treeCtrl.SetItemBadge(hChild, 20, 20, RGB(255, 0, 0), RGB(255, 255, 255)); // 红色徽章背景白色徽章前景
m_treeCtrl.ShowItemBadgeNumber(hChild, 5); // 显示数字 5
m_treeCtrl.SetNodeTextColor(hChild, RGB(255, 0, 0));此段代码展示了如何插入一个子节点并为其设置一个徽章徽章的背景色为红色前景色为白色并且显示了数字 5。此外还修改了子节点的文本颜色为红色。
插入圆点徽章节点
// 插入圆点徽章节点
HTREEITEM hDotNode m_treeCtrl.InsertItem(_T(Dot Node), hRoot);
m_treeCtrl.SetItemBadge(hDotNode, 20, 20, RGB(255, 0, 0), RGB(255, 255, 255)); // 设置徽章背景色和前景色
m_treeCtrl.ShowItemBadgeDotMode(hDotNode, 100, RGB(255, 0, 0), RGB(255, 255, 255)); // 显示圆点徽章在这里我们插入了一个带圆点徽章的节点并为其设置了一个红色的圆点徽章背景色为红色前景色为白色。
插入加粗字体节点
// 插入加粗字体节点
HTREEITEM hBoldNode m_treeCtrl.InsertItem(_T(Bold Node), hRoot);
m_treeCtrl.SetItemBold(hBoldNode); // 设置加粗字体我们还展示了如何将某个节点的字体设置为加粗样式突出显示该节点。
4. 展开根节点
插入完所有节点后我们可以使用 Expand 方法展开根节点显示所有子节点。
// 展开根节点
m_treeCtrl.Expand(hRoot, TVE_EXPAND);这会将根节点展开显示其子节点使得树结构更加清晰可见。
5. 头文件和源文件
以下是一些需要添加的代码结构用于实现上述功能
头文件代码
#if !defined(AFX_APREDTREECTRL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
#define AFX_APREDTREECTRL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_#if _MSC_VER 1000
#pragma once
#endif // _MSC_VER 1000#include afxcmn.h
#include map
#include vector/*** brief 消息宏用于向父窗口发送树项目单击的通知。* * 当用户单击树控件的项目时父窗口会收到该消息。*/
#define ID_MSG_TREE_CLICK_ITEM WM_USER 2080/*** brief 字体类型枚举。*/
enum class FontType {FONT_TAHOMA,FONT_SEGOE_UI,FONT_MS_SANS_SERIF,FONT_ARIAL,FONT_WINGDINGS// 可以继续扩展其他字体
};/*** brief CApredTreeCtrl 类* * CApredTreeCtrl 是一个继承自 MFC 的 CTreeCtrl 的自定义控件扩展了以下功能* - 为树控件的每个项目添加徽章数字徽章、小圆点等* - 支持项目的加粗显示* - 支持设置项目图标* - 自定义项目的背景颜色、文本颜色和悬停效果*/
class CApredTreeCtrl : public CTreeCtrl {
public:/*** brief 树项目徽章的定义结构体。* * 描述了项目上的徽章类型、颜色及显示时间。*/typedef struct tagBADGE {HTREEITEM hTreeItem; /// 树项目的句柄COLORREF badgeBackground; /// 徽章背景颜色COLORREF badgeForeground; /// 徽章前景文字颜色int width; /// 徽章宽度int height; /// 徽章高度int type; /// 徽章类型0(隐藏)1(小圆点)2(数字)int number; /// 数字徽章显示的数字仅当类型为数字时有效int showTime; /// 徽章的显示时间单位为秒0 表示永久显示} BADGE;/*** brief 动态声明宏用于支持动态创建和 RTTI。*/DECLARE_DYNAMIC(CApredTreeCtrl)/*** brief 构造函数初始化控件的默认样式和资源。*/CApredTreeCtrl();/*** brief 析构函数释放分配的资源。*/virtual ~CApredTreeCtrl();/*** brief 设置树项目的徽章。* * param hItem 树项目句柄* param width 徽章的宽度* param height 徽章的高度* param badgeBackground 徽章的背景颜色* param badgeForeground 徽章的前景文字颜色*/void SetItemBadge(HTREEITEM hItem, int width 20, int height 20, COLORREF badgeBackground RGB(255, 255, 255), COLORREF badgeForeground RGB(0, 0, 0));/*** brief 显示树项目的数字徽章。* * param hItem 树项目句柄* param number 徽章中显示的数字* param badgeBackground 徽章的背景颜色默认为白色* param badgeForeground 徽章的前景字体颜色默认为黑色*/void ShowItemBadgeNumber(HTREEITEM hItem, int number, COLORREF badgeBackground RGB(255, 255, 255), COLORREF badgeForeground RGB(0, 0, 0));/*** brief 显示树项目的小圆点徽章。* * param hItem 树项目句柄* param nSecond 徽章显示的持续时间单位为秒0 表示永久显示 * param badgeBackground 徽章的背景颜色默认为白色* param badgeForeground 徽章的前景字体颜色默认为黑色*/void ShowItemBadgeDotMode(HTREEITEM hItem, int nSecond 0, COLORREF badgeBackground RGB(255, 255, 255), COLORREF badgeForeground RGB(0, 0, 0));/*** brief 隐藏树项目的徽章。* * param hItem 树项目句柄*/void HideItemBadge(HTREEITEM hItem);/*** brief 将树项目设置为加粗显示。* * param item 树项目句柄*/void SetItemBold(HTREEITEM item);/*** brief 取消树项目的加粗显示。* * param item 树项目句柄*/void CancelItemBold(HTREEITEM item);/*** brief 检查树项目是否为加粗状态。* * param item 树项目句柄* return BOOL 如果项目是加粗的返回 TRUE否则返回 FALSE。*/BOOL FindBoldItem(HTREEITEM item);/*** brief 设置树项目的图标。* * param hItem 树项目句柄* param hIcon 图标句柄*/void SetItemIcon(HTREEITEM hItem, HICON hIcon);/*** brief 设置默认背景色。* * param color 背景色值*/void SetDefaultBkColor(COLORREF color);/*** brief 设置选中项的背景色。* * param color 选中项的背景色值*/void SetSelectedBkColor(COLORREF color);/*** brief 设置悬停项的背景色。* * param color 悬停项的背景色值*/void SetHoverBkColor(COLORREF color);/*** brief 设置默认文本颜色。* * param color 文本颜色值*/void SetDefaultTextColor(COLORREF color);/*** brief 设置选中文本的颜色。* * param color 选中文本的颜色值*/void SetSelectedTextColor(COLORREF color);/*** brief 设置悬停项的文本颜色。* * param color 悬停项文本的颜色值*/void SetHoverTextColor(COLORREF color);/*** brief 为特定节点设置背景色。* * param hItem 树项目句柄* param color 节点的背景色*/void SetNodeBkColor(HTREEITEM hItem, COLORREF color);/*** brief 为特定节点设置文本颜色。** param hItem 树项目句柄* param color 节点的文本颜色*/void CApredTreeCtrl::SetNodeTextColor(HTREEITEM hItem, COLORREF color);/*** brief 刷新树控件更新颜色显示。*/void Refresh();/*** brief 刷新指定的树项目更新颜色显示。** param hItem 树项目句柄*/void RefreshItem(HTREEITEM hItem);/*** brief 设置字体。** param eFont 字体类型* param nSize 字体大小*/void UpdateFont(FontType eFont, int nSize);protected:/*** brief 绘制树项目右侧的展开/收起按钮。* * param hItem 树项目句柄* param hDC 设备上下文句柄* param pRect 按钮的绘制区域*/void DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect);/*** brief 绘制树控件中的所有可见项目。* * param hDC 设备上下文句柄*/void DrawItems(HDC hDC);/*** brief 绘制树项目的背景。** param hDC 设备上下文句柄* param hItem 树项目句柄* param pRect 绘制区域*/void DrawBadge(HTREEITEM hItem, HDC hDC, CRect* pRect, const BADGE badge);// 私有数据成员std::mapHTREEITEM, COLORREF m_itemTextColors;/// 节点文本颜色存储std::mapHTREEITEM, COLORREF m_itemBkColors; /// 节点背景色存储std::mapHTREEITEM, BADGE m_badges; /// 存储每个树项目的徽章信息std::mapHTREEITEM, HICON m_icons; /// 存储每个树项目的图标信息std::vectorHTREEITEM m_itemBolds; /// 存储需要加粗显示的树项目HBRUSH m_hBrushItem[3]; /// 树项目背景刷子默认、选中、悬停COLORREF m_crItemBk[3]; /// 树项目背景颜色默认、选中、悬停COLORREF m_crText[3]; /// 树项目文本颜色默认、选中、悬停HBRUSH m_hBrushBtn[3]; /// 按钮的背景刷子HPEN m_hPenItem[3]; /// 树项目边框画笔默认、选中、悬停HTREEITEM m_hHoverItem; /// 当前鼠标悬停的树项目句柄BOOL m_bTracking; /// 鼠标是否正在跟踪树项目用于悬停效果CFont m_font; /// 树控件的字体public:// MFC 消息映射DECLARE_MESSAGE_MAP()/*** brief 重载的 WM_PAINT 消息处理函数用于自定义绘制树控件。*/afx_msg void OnPaint();/*** brief 重载的 WM_MOUSEMOVE 消息处理函数用于处理鼠标移动事件。*/afx_msg void OnMouseMove(UINT nFlags, CPoint point);/*** brief 重载的 WM_MOUSEHOVER 消息处理函数用于处理鼠标悬停事件。*/afx_msg void OnMouseHover(UINT nFlags, CPoint point);/*** brief 重载的 WM_MOUSELEAVE 消息处理函数用于处理鼠标移出控件的事件。*/afx_msg void OnMouseLeave();/*** brief 重载的 WM_TIMER 消息处理函数用于徽章的显示时间控制。*/afx_msg void OnTimer(UINT_PTR nIDEvent);/*** brief 重载的 WM_LBUTTONDOWN 消息处理函数用于处理鼠标左键单击事件。*/afx_msg void OnLButtonDown(UINT nFlags, CPoint point);/*** brief 重载的 WM_CREATE 消息处理函数用于初始化定时器。*/afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);/*** brief 重载的 PreSubclassWindow 函数用于初始化定时器和其他资源。*/virtual void PreSubclassWindow();/*** brief 重载的 WM_DESTROY 消息处理函数用于释放资源。*/afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
};#endif // !defined(AFX_APREDTREECTRL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)源文件代码
#include pch.h
#include ApredTreeCtrl.h#define ROFFSET 7 // 按钮距离右侧的偏移量
#define WIDE 10 // 按钮宽度
#define WIDE2 5 // 按钮宽度的一半
#define EXPANDED_WIDE 8 // 按钮展开时的宽度// 徽章类型定义
#define BADGE_HIDE 0 // 不显示徽章
#define BADGE_DOT 1 // 小圆点徽章
#define BADGE_NUMBER 2 // 数字徽章
#define BADGE_RECTANGLE 3 // 矩形徽章
#define BADGE_TRIANGLE 4 // 三角形徽章IMPLEMENT_DYNAMIC(CApredTreeCtrl, CTreeCtrl)/*** brief 构造函数初始化控件默认样式和资源。*/
CApredTreeCtrl::CApredTreeCtrl() {// 初始化背景刷子、边框画笔和颜色for (int i 0; i 3; i) {m_hBrushItem[i] nullptr;m_hBrushBtn[i] nullptr;m_hPenItem[i] nullptr;}// 默认、选中、悬停的背景和文本颜色m_crItemBk[0] RGB(255, 255, 255);m_crItemBk[1] RGB(228, 229, 234);m_crItemBk[2] RGB(236, 237, 241);m_crText[0] RGB(28, 28, 28);m_crText[1] RGB(28, 28, 28);m_crText[2] RGB(28, 28, 28);m_hHoverItem nullptr; // 当前没有悬停的项目m_bTracking FALSE; // 鼠标未开始跟踪
}/*** brief 析构函数释放资源。*/
CApredTreeCtrl::~CApredTreeCtrl() {for (int i 0; i 3; i) {if (m_hBrushItem[i] ! nullptr) ::DeleteObject(m_hBrushItem[i]);if (m_hBrushBtn[i] ! nullptr) ::DeleteObject(m_hBrushBtn[i]);if (m_hPenItem[i] ! nullptr) ::DeleteObject(m_hPenItem[i]);}
}BEGIN_MESSAGE_MAP(CApredTreeCtrl, CTreeCtrl)ON_WM_PAINT()ON_WM_MOUSEMOVE()ON_WM_MOUSEHOVER()ON_WM_MOUSELEAVE()ON_WM_TIMER()ON_WM_LBUTTONDOWN()ON_WM_CREATE()ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()/*** brief 绘制展开/收起按钮。*/
void CApredTreeCtrl::DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect) {POINT pt[3]; // 三角形顶点if ((GetItemState(hItem, TVIS_EXPANDED) TVIS_EXPANDED) TVIS_EXPANDED) {// 项目已展开绘制向下的三角形int nBottomOffset (pRect-Height() - EXPANDED_WIDE) / 2;pt[0].x pRect-right - ROFFSET - EXPANDED_WIDE;pt[0].y pRect-bottom - nBottomOffset;pt[1].x pRect-right - ROFFSET;pt[1].y pRect-bottom - nBottomOffset;pt[2].x pRect-right - ROFFSET;pt[2].y pRect-bottom - nBottomOffset - EXPANDED_WIDE;} else {// 项目未展开绘制向右的三角形int nBottomOffset (pRect-Height() - WIDE) / 2;pt[0].x pRect-right - ROFFSET - WIDE2;pt[0].y pRect-bottom - nBottomOffset - WIDE;pt[1].x pRect-right - ROFFSET - WIDE2;pt[1].y pRect-bottom - nBottomOffset;pt[2].x pRect-right - ROFFSET;pt[2].y pRect-bottom - nBottomOffset - WIDE2;}::Polygon(hDC, pt, 3); // 绘制三角形
}/*** brief 自定义绘制树控件。*/
void CApredTreeCtrl::OnPaint() {HDC hDC, hMemDC;HBITMAP hBitmap;RECT rcClient;for (int i 0; i 3; i) {m_hBrushItem[i] CreateSolidBrush(m_crItemBk[i]);m_hBrushBtn[i] CreateSolidBrush(m_crText[i]);m_hPenItem[i] ::CreatePen(PS_SOLID, 1, m_crText[i]);}PAINTSTRUCT ps;hDC ::BeginPaint(m_hWnd, ps); // 开始绘制GetClientRect(rcClient);// 创建内存设备上下文和兼容位图hMemDC ::CreateCompatibleDC(hDC);hBitmap ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);::SelectObject(hMemDC, hBitmap);// 设置背景色HBRUSH hBrushBK CreateSolidBrush(m_crItemBk[0]);::FillRect(hMemDC, rcClient, hBrushBK);DeleteObject(hBrushBK);// 绘制子项DrawItems(hMemDC);// 将内存 DC 中的内容复制到窗口 DC::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hMemDC, 0, 0, SRCCOPY);// 清理资源::EndPaint(m_hWnd, ps);::DeleteObject(hBitmap);::DeleteDC(hMemDC);
}/*** brief 绘制树控件的所有可见子项。*/
void CApredTreeCtrl::DrawItems(HDC hDC) {HTREEITEM hCurrentItem;int itemState;CRect rcClient, rcItem, rcText;GetClientRect(rcClient);//Gdiplus::Graphics graphics(hDC);//graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);// 统一设置透明背景::SetBkMode(hDC, TRANSPARENT);// 获取第一个可见项hCurrentItem GetFirstVisibleItem();do {if (GetItemRect(hCurrentItem, rcItem, FALSE) GetItemRect(hCurrentItem, rcText, TRUE)) {rcText.right rcItem.right - 16;rcText.left 16;CRect fillRect(0, rcItem.top, rcClient.right, rcItem.bottom);if (rcItem.top rcClient.bottom) {break;}// 背景颜色COLORREF nodeBackgroundColor m_crItemBk[0]; // 默认背景色if (m_itemBkColors.find(hCurrentItem) ! m_itemBkColors.end()) {nodeBackgroundColor m_itemBkColors[hCurrentItem]; // 特定节点背景色}// 绘制背景根据选中、悬停或特定节点背景色填充itemState GetItemState(hCurrentItem, TVIF_STATE);COLORREF crText m_crText[0];if (m_itemTextColors.find(hCurrentItem) ! m_itemTextColors.end()) {crText m_itemTextColors[hCurrentItem]; // 特定节点背景色}if (itemState TVIS_SELECTED) {crText m_crText[1];HRGN hRgn CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4);::FillRgn(hDC, hRgn, m_hBrushItem[1]);::DeleteObject(hRgn);}else if (hCurrentItem m_hHoverItem) {crText m_crText[2];HRGN hRgn CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4);::FillRgn(hDC, hRgn, m_hBrushItem[2]);::DeleteObject(hRgn);}else if (nodeBackgroundColor ! m_crItemBk[0]) {// 为特定节点设置背景色HRGN hRgn CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4);::FillRgn(hDC, hRgn, ::CreateSolidBrush(nodeBackgroundColor));::DeleteObject(hRgn);}// 处理根节点的三角形按钮和文本if (hCurrentItem GetRootItem()) {::SetBkMode(hDC, TRANSPARENT); // 确保背景透明CRect rcBtn rcText;rcBtn.right rcText.left - 2;rcBtn.left rcBtn.right - 30;rcBtn.left - 8;DrawItemButton(hCurrentItem, hDC, rcBtn); // 绘制展开/收起按钮}// 处理其他节点的展开按钮else if (ItemHasChildren(hCurrentItem)) {CRect rcBtn rcText;rcBtn.right rcText.left - 2;rcBtn.left rcBtn.right - 30;rcBtn.left - 8;DrawItemButton(hCurrentItem, hDC, rcBtn); // 绘制展开/收起按钮}// 绘制图标如果存在if (m_icons.find(hCurrentItem) ! m_icons.end()) {HICON hIcon m_icons[hCurrentItem];DrawIconEx(hDC, 16, (rcItem.bottom - rcItem.top - 32) / 2, hIcon, 32, 32, 0, 0, DI_NORMAL);rcText.left 32;}// 创建加粗字体LOGFONT lf;::GetObject(m_font, sizeof(LOGFONT), lf);lf.lfWeight FW_BOLD; // 设置加粗HFONT hFontBold CreateFontIndirect(lf); // 创建加粗字体// 绘制文本::SetTextColor(hDC, crText);CString strText GetItemText(hCurrentItem);// 如果是加粗文本选择加粗字体if (FindBoldItem(hCurrentItem)) {::SelectObject(hDC, hFontBold);::DrawText(hDC, strText, strText.GetLength(), rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);::DeleteObject(hFontBold); // 释放加粗字体资源}else {::SelectObject(hDC, m_font);::DrawText(hDC, strText, strText.GetLength(), rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);}// 是否有小圆点如徽章if (m_badges.find(hCurrentItem) ! m_badges.end()) {BADGE badge m_badges[hCurrentItem];int x rcItem.right - 18 - badge.width;int y rcItem.top (rcItem.Height() - badge.height) / 2;CRect rcBadge;rcBadge.left x;rcBadge.right rcBadge.left badge.width;rcBadge.top y;rcBadge.bottom rcBadge.top badge.height;if (badge.type BADGE_NUMBER) {::SetTextColor(hDC, badge.badgeForeground);char szBuffer[32];sprintf_s(szBuffer, 32, %d%s, min(badge.number, 9), badge.number 9 ? : );DrawText(hDC, CString(szBuffer), (int)strlen(szBuffer), rcBadge, DT_CENTER | DT_VCENTER | DT_SINGLELINE);}else {DrawBadge(hCurrentItem, hDC, rcBadge, badge);}}}} while ((hCurrentItem GetNextVisibleItem(hCurrentItem)) ! NULL);
}void CApredTreeCtrl::DrawBadge(HTREEITEM hItem, HDC hDC, CRect* pRect, const BADGE badge) {int x pRect-left; // 徽章的 X 坐标int y pRect-top; // 徽章的 Y 坐标// 调试输出徽章位置TRACE(Badge position: x %d, y %d, width %d, height %d\n, x, y, badge.width, badge.height);// 启用抗锯齿GDI中的高级图形模式SetGraphicsMode(hDC, GM_ADVANCED); // 启用高级图形模式SetBkMode(hDC, TRANSPARENT); // 背景透明// 设置笔和画刷创建抗锯齿效果HBRUSH hBrush CreateSolidBrush(badge.badgeBackground);HPEN hPen CreatePen(PS_SOLID, 1, badge.badgeForeground); // 设置边框颜色SelectObject(hDC, hBrush);SelectObject(hDC, hPen);// GDI 绘制抗锯齿设置SetGraphicsMode(hDC, GM_ADVANCED); // 启用高级图形模式SetBkMode(hDC, TRANSPARENT); // 背景透明// 根据徽章的类型绘制图形switch (badge.type) {case BADGE_DOT:{// 绘制圆形徽章Ellipse(hDC, x, y, x badge.width, y badge.height); // 绘制一个圆形// 使用 m_font 绘制文本::SelectObject(hDC, m_font.GetSafeHandle()); // 设置字体::SetTextColor(hDC, badge.badgeForeground);RECT rcText { x, y, x badge.width, y badge.height };::SetBkMode(hDC, TRANSPARENT); // 设置透明背景char szBuffer[32];sprintf_s(szBuffer, 32, %d, badge.showTime); // 显示时间或其他数字DrawText(hDC, CString(szBuffer), -1, rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE);}break;case BADGE_RECTANGLE:{// 绘制矩形徽章Rectangle(hDC, x, y, x badge.width, y badge.height); // 绘制矩形}break;case BADGE_TRIANGLE:{// 绘制三角形徽章DrawItemButton(hItem, hDC, pRect);}break;default:// 如果没有匹配的类型可以选择默认行为break;}// 清理资源DeleteObject(hBrush);DeleteObject(hPen);// 恢复图形模式SetGraphicsMode(hDC, GM_COMPATIBLE); // 恢复为兼容模式关闭抗锯齿
}/*** brief 鼠标移动事件处理更新当前悬停的项目。*/
void CApredTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) {HTREEITEM hHoverItem HitTest(point); // 根据鼠标位置获取悬停的树项目// 检查是否需要更新悬停项if (m_hHoverItem ! hHoverItem) {// 如果之前有悬停项恢复它的状态if (m_hHoverItem ! NULL) {// 恢复之前悬停项的状态比如取消高亮RefreshItem(m_hHoverItem);}m_hHoverItem hHoverItem; // 更新当前悬停项目if (m_hHoverItem ! NULL) {// 更新当前悬停项的状态比如高亮RefreshItem(m_hHoverItem);}}// 追踪鼠标事件只在第一次进入时设置if (!m_bTracking) {TRACKMOUSEEVENT tme {};tme.cbSize sizeof(tme);tme.dwFlags TME_HOVER | TME_LEAVE; // 追踪鼠标悬停和离开事件tme.hwndTrack m_hWnd;tme.dwHoverTime 10; // 鼠标停留时间阈值m_bTracking _TrackMouseEvent(tme);}CTreeCtrl::OnMouseMove(nFlags, point); // 调用基类的默认处理
}/*** brief 鼠标悬停事件处理可在此添加需要悬停时的逻辑。*/
void CApredTreeCtrl::OnMouseHover(UINT nFlags, CPoint point) {// 此处可以添加鼠标悬停特定逻辑CTreeCtrl::OnMouseHover(nFlags, point);
}/*** brief 鼠标移出控件事件处理重置悬停状态。*/
void CApredTreeCtrl::OnMouseLeave() {if (m_hHoverItem ! nullptr) {// 清空悬停项目并刷新HTREEITEM hPreviousHoverItem m_hHoverItem;m_hHoverItem nullptr; // 清除悬停项// 刷新之前悬停项的区域CRect rcItem;if (GetItemRect(hPreviousHoverItem, rcItem, FALSE)) {InvalidateRect(rcItem, FALSE); // 仅刷新该项的区域}}m_bTracking FALSE; // 停止鼠标事件追踪CTreeCtrl::OnMouseLeave(); // 调用基类的处理方法
}/*** brief 定时器事件处理控制徽章显示时间。*/
void CApredTreeCtrl::OnTimer(UINT_PTR nIDEvent) {if (nIDEvent 1) { // 检测定时器 IDfor (auto item : m_badges) {if (item.second.showTime 0) { // 如果有计时的徽章item.second.showTime--; // 减少显示时间if (item.second.showTime 0) { // 时间到达时隐藏徽章item.second.type BADGE_HIDE;}// 只刷新当前徽章所在的项RefreshItem(item.first);}}}CTreeCtrl::OnTimer(nIDEvent);
}/*** brief 鼠标左键点击事件处理选中项目并通知父窗口。*/
void CApredTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) {HTREEITEM hItem HitTest(point); // 根据鼠标位置获取点击的树项目if (hItem ! nullptr) {SelectItem(hItem); // 选中点击的项目GetParent()-SendMessage(ID_MSG_TREE_CLICK_ITEM, (WPARAM)hItem, 0); // 通知父窗口}CTreeCtrl::OnLButtonDown(nFlags, point); // 调用基类的默认处理
}/*** brief 鼠标左键双击事件处理展开或收起树项目。*/
void CApredTreeCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{HTREEITEM hItem HitTest(point);if (hItem ! NULL ItemHasChildren(hItem)) {// 获取该节点的矩形区域CRect rcItem;GetItemRect(hItem, rcItem, TRUE); // 获取包含文本区域的矩形if (!rcItem.PtInRect(point)) {// 检查节点是否已经展开UINT nState GetItemState(hItem, TVIS_EXPANDED); // 获取项的状态if (nState TVIS_EXPANDED) {Expand(hItem, TVE_COLLAPSE);}else {Expand(hItem, TVE_EXPAND);}// 在展开或收起后刷新该项RefreshItem(hItem);}}// 调用父类的处理方法CTreeCtrl::OnLButtonDblClk(nFlags, point);
}/*** brief 设置树项目的徽章背景色和前景色。*/
void CApredTreeCtrl::SetItemBadge(HTREEITEM hItem, int width /* 20*/, int height /* 20*/, COLORREF badgeBackground /* RGB(255, 255, 255)*/, COLORREF badgeForeground /* RGB(255, 255, 255)*/) {if (m_badges.find(hItem) m_badges.end()) {// 如果项目没有徽章创建一个新的徽章记录BADGE badge {hItem, // 树项目句柄badgeBackground, // 背景颜色badgeForeground, // 前景颜色width, // 徽章宽度height, // 徽章高度BADGE_DOT, // 徽章类型小圆点0, // 数字徽章的数字0 // 显示时间0 表示永久显示};m_badges[hItem] badge;} else {// 更新现有徽章的信息BADGE badge m_badges[hItem];badge.badgeBackground badgeBackground;badge.badgeForeground badgeForeground;badge.width width;badge.height height;}
}/*** brief 显示树项目的数字徽章。*/
void CApredTreeCtrl::ShowItemBadgeNumber(HTREEITEM hItem, int number, COLORREF badgeBackground /* RGB(255, 255, 255)*/, COLORREF badgeForeground /* RGB(0, 0, 0)*/) {if (m_badges.find(hItem) ! m_badges.end()) {BADGE badge m_badges[hItem];badge.type BADGE_NUMBER;badge.number number;badge.badgeBackground badgeBackground;badge.badgeForeground badgeForeground;InvalidateRect(NULL, TRUE); // 刷新控件}
}/*** brief 显示树项目的小圆点徽章。*/
void CApredTreeCtrl::ShowItemBadgeDotMode(HTREEITEM hItem, int nSecond /* 0*/, COLORREF badgeBackground /* RGB(255, 255, 255)*/, COLORREF badgeForeground /* RGB(0, 0, 0)*/) {if (m_badges.find(hItem) ! m_badges.end()) {BADGE badge m_badges[hItem];badge.type BADGE_DOT;badge.showTime nSecond; // 设置显示时长badge.badgeBackground badgeBackground;badge.badgeForeground badgeForeground;InvalidateRect(NULL, TRUE);}
}/*** brief 隐藏树项目的徽章。*/
void CApredTreeCtrl::HideItemBadge(HTREEITEM hItem) {if (m_badges.find(hItem) ! m_badges.end()) {BADGE badge m_badges[hItem];badge.type BADGE_HIDE;badge.showTime 0;InvalidateRect(NULL, TRUE);}
}/*** brief 将树项目设置为加粗显示。*/
void CApredTreeCtrl::SetItemBold(HTREEITEM item) {if (!FindBoldItem(item)) {m_itemBolds.push_back(item); // 加入加粗列表InvalidateRect(NULL, TRUE); // 刷新控件}
}/*** brief 取消树项目的加粗显示。*/
void CApredTreeCtrl::CancelItemBold(HTREEITEM item) {for (auto iter m_itemBolds.begin(); iter ! m_itemBolds.end(); iter) {if (*iter item) {m_itemBolds.erase(iter); // 从加粗列表中移除break;}}InvalidateRect(NULL, TRUE); // 刷新控件
}/*** brief 检查树项目是否为加粗状态。*/
BOOL CApredTreeCtrl::FindBoldItem(HTREEITEM item) {return std::find(m_itemBolds.begin(), m_itemBolds.end(), item) ! m_itemBolds.end();
}/*** brief 初始化时钟或其他资源。*/
int CApredTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) {if (CTreeCtrl::OnCreate(lpCreateStruct) -1) {return -1;}SetTimer(1, 1000, nullptr); // 设置定时器间隔 1 秒return 0;
}/*** brief 设置图标到树项目。*/
void CApredTreeCtrl::SetItemIcon(HTREEITEM hItem, HICON hIcon) {m_icons[hItem] hIcon; // 存储图标InvalidateRect(NULL, TRUE); // 刷新控件
}/*** brief 子类化窗口前的初始化。*/
void CApredTreeCtrl::PreSubclassWindow() {SetTimer(1, 1000, nullptr); // 初始化定时器CTreeCtrl::PreSubclassWindow();
}/*** brief 设置默认背景色。*/
void CApredTreeCtrl::SetDefaultBkColor(COLORREF color) {m_crItemBk[0] color;if (m_hBrushItem[0]) {::DeleteObject(m_hBrushItem[0]);}m_hBrushItem[0] CreateSolidBrush(color);Refresh();
}/*** brief 设置选中项背景色。*/
void CApredTreeCtrl::SetSelectedBkColor(COLORREF color) {m_crItemBk[1] color;if (m_hBrushItem[1]) {::DeleteObject(m_hBrushItem[1]);}m_hBrushItem[1] CreateSolidBrush(color);Refresh();
}/*** brief 设置悬停项背景色。*/
void CApredTreeCtrl::SetHoverBkColor(COLORREF color) {m_crItemBk[2] color;if (m_hBrushItem[2]) {::DeleteObject(m_hBrushItem[2]);}m_hBrushItem[2] CreateSolidBrush(color);Refresh();
}/*** brief 设置默认文本色。*/
void CApredTreeCtrl::SetDefaultTextColor(COLORREF color) {m_crText[0] color;Refresh();
}/*** brief 设置选中文本色。*/
void CApredTreeCtrl::SetSelectedTextColor(COLORREF color) {m_crText[1] color;Refresh();
}/*** brief 设置悬停文本色。*/
void CApredTreeCtrl::SetHoverTextColor(COLORREF color) {m_crText[2] color;Refresh();
}/*** brief 为特定节点设置背景色。*/
void CApredTreeCtrl::SetNodeBkColor(HTREEITEM hItem, COLORREF color) {m_itemBkColors[hItem] color;Refresh();
}/*** brief 为特定节点设置文本颜色。*/
void CApredTreeCtrl::SetNodeTextColor(HTREEITEM hItem, COLORREF color)
{m_itemTextColors[hItem] color; // 设置指定节点的文本颜色Refresh(); // 刷新树控件以应用更改
}/*** brief 刷新控件。*/
void CApredTreeCtrl::Refresh() {InvalidateRect(NULL, TRUE); // 强制重绘控件
}void CApredTreeCtrl::RefreshItem(HTREEITEM hItem)
{CRect rcItem;if (GetItemRect(hItem, rcItem, FALSE)) { // 改为 FALSE获取整个项的矩形区域InvalidateRect(rcItem, FALSE); // 刷新整个项的区域}
}void CApredTreeCtrl::UpdateFont(FontType eFont, int nSize)
{LOGFONT logFont { 0 };// 根据 FontType 枚举选择对应的字体switch (eFont){case FontType::FONT_TAHOMA:_tcscpy_s(logFont.lfFaceName, _T(Tahoma));break;case FontType::FONT_SEGOE_UI:_tcscpy_s(logFont.lfFaceName, _T(Segoe UI));break;case FontType::FONT_MS_SANS_SERIF:_tcscpy_s(logFont.lfFaceName, _T(MS Sans Serif));break;case FontType::FONT_ARIAL:_tcscpy_s(logFont.lfFaceName, _T(Arial));break;case FontType::FONT_WINGDINGS:_tcscpy_s(logFont.lfFaceName, _T(Wingdings));break;// 可以继续添加其他字体default:_tcscpy_s(logFont.lfFaceName, _T(Segoe UI)); // 默认字体break;}// 设置字体的大小logFont.lfHeight -nSize; // 字体大小为负值表示像素大小logFont.lfQuality CLEARTYPE_QUALITY; // 清晰字体质量// 删除旧字体并创建新字体m_font.DeleteObject();m_font.CreateFontIndirect(logFont);// 设置新的字体到控件SetFont(m_font);
}总结
通过上述代码我们可以轻松实现树控件的个性化定制设置节点的字体、颜色、徽章、图标等样式。这些功能不仅提升了用户体验还能够增强界面的可操作性和美观度。你可以根据项目需求灵活调整设置以达到最佳效果。如果 GDI 抗锯齿效果不理想考虑使用 GDIGraphics来绘制圆形和文本它提供了更高质量的抗锯齿效果。