MFC 自定义树控件:树节点的样式与交互

在本教程中,将介绍如何在 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::map<HTREEITEM, COLORREF> m_itemTextColors;///< 节点文本颜色存储
    std::map<HTREEITEM, COLORREF> m_itemBkColors;  ///< 节点背景色存储
    std::map<HTREEITEM, BADGE> m_badges;           ///< 存储每个树项目的徽章信息
    std::map<HTREEITEM, HICON> m_icons;            ///< 存储每个树项目的图标信息
    std::vector<HTREEITEM> 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) {  // 检测定时器 ID
        for (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 抗锯齿效果不理想,考虑使用 GDI+(Graphics)来绘制圆形和文本,它提供了更高质量的抗锯齿效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/925418.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vision Transformer(vit)的主干

图解&#xff1a; 代码&#xff1a; class VisionTransformer(nn.Module):def __init__(self, img_size224, patch_size16, in_c3, num_classes1000,embed_dim768, depth12, num_heads12, mlp_ratio4.0, qkv_biasTrue,qk_scaleNone, representation_sizeNone, distilledFalse,…

无人机的起降装置:探索起飞和降落的秘密 !

一、起降系统的运行方式 起飞方式 垂直起飞&#xff1a;小型无人机通常采用垂直起飞方式&#xff0c;利用螺旋桨产生的升力直接从地面升起。这种方式适用于空间有限或需要快速起飞的场景。 跑道起飞&#xff1a;大型无人机或需要较长起飞距离的无人机&#xff0c;可能会采用…

【VUE3】npm : 无法加载文件 D:\Program\nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本。

npm : 无法加载文件 D:\Program\nodejs\npm.ps1。未对文件 D:\Program\nodejs\npm.ps1 进行数字签名。无法在当前系统上运行该脚本。有关运行脚本和设置执行策略的详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_ Execution_Policies。…

跨平台应用开发框架(4)----Qt(系统篇)

目录 1.Qt事件 1.事件来源 2.事件处理 3.按键事件 1.组合按键 4.鼠标事件 1.鼠标单击事件 2.鼠标释放事件 3.鼠标双击事件 4.鼠标移动事件 5.滚轮事件 5.定时器 1.QTimerEvent类 2.QTimer 类 3.获取系统日期及时间 6.事件分发器 7.事件过滤器 2.Qt文件 1.输入…

Mybatis集成篇(一)

Spring 框架集成Mybatis 目前主流Spring框架体系中&#xff0c;可以集成很多第三方框架&#xff0c;方便开发者利用Spring框架机制使用第三方框架的功能。就例如本篇Spring集成Mybatis 简单集成案例&#xff1a; Config配置&#xff1a; Configuration MapperScan(basePack…

【Debug】hexo-github令牌认证 Support for password authentication was removed

title: 【Debug】hexo-github令牌认证 date: 2024-07-19 14:40:54 categories: bug解决日记 description: “Support for password authentication was removed on August 13, 2021.” cover: https://pic.imgdb.cn/item/669b38ebd9c307b7e9f3e5e0.jpg 第一章 第一篇博客记录一…

算法笔记:力扣105从前序与中序遍历序列构造二叉树

首先重要的是要明白前序遍历&#xff0c;和中序遍历的含义&#xff1b; 前序遍历&#xff1a;根左右中序遍历&#xff1a;左根右 那么在前序遍历的数组中&#xff0c;第一位一定是根节点&#xff0c;而中序遍历数组中&#xff0c;根节点的左边部分就是该节点的左子树&#xf…

el-selet下拉菜单自定义内容,下拉内容样式类似表格

<el-form-item label"角色:" prop"username"><el-selectv-model"value"placeholder"Select"popper-class"role_select"><el-option disabled><div class"flex"><div style"width…

数据追踪技术有哪些?如何实现的?

在数字化时代&#xff0c;数据成为了业务决策和市场营销的关键资源。用户行为分析作为数据驱动的一部分&#xff0c;通过数据追踪技术帮助企业了解用户行为、趋势和偏好&#xff0c;从而制定更加精准的战略。本文将深入探讨数据追踪技术在用户行为分析中的神秘面纱&#xff0c;…

2024年信号处理与神经网络应用会

重要信息 会议时间&#xff1a;2024年12月13-15日 会议地点&#xff1a;中国武汉 会议官网&#xff1a;www.spnna.org 会议简介 2024年信号处理与神经网络应用&#xff08;SPNNA 2024&#xff09;将于2024年12月13日至15日在中国武汉召开。在为全球研究人员、工程师、学者和…

C++11-lambda表达式

目录 1.labmda的表达式 1.1.仿函数的使用 1.2lambda表达式的书写 1.3 lambda的捕获列表 1.3.1传值捕捉 1.3.2mutable可以修改拷贝对象 1.3.3 引用捕获 1.3.4混合捕捉 1.4 函数对象与lambda表达式 1.5 lambda和仿函数的比较 &#x1f33c;&#x1f33c;前言&#xff1a;从…

蓝桥杯模拟题不知名题目

题目:p是一个质数&#xff0c;但p是n的约数。将p称为是n的质因数。求2024最大质因数。 #include<iostream> #include<algorithm> using namespace std; bool fun(int x) {for(int i 2 ; i * i < x ; i){if(x % i 0)return false;}return true; } int main() …

mac访达打开终端

选择文件夹打开 选中文件夹&#xff0c;然后右键即可&#xff1a; 在当前文件夹打开 在访达的当前文件夹长按option键 左下角出现当前文件夹路径 右键即可打开终端

永久免费的PDF万能水印删除工具

永久免费的PDF万能水印删除工具 1.简介 PDF万能水印删除工具&#xff0c;可以去除99.9%的PDF水印。例如&#xff1a;XObject水印&#xff08;含图片水印&#xff09;、文本水印、绘图水印/曲线水印、注释水印、工件水印、剪切路径水印等等。本软件是永久免费&#xff0c;无有…

小程序 - 本地生活

小程序页面和样式练习 - 本地生活小程序开发笔记 目录 本地生活 准备工作 加载图片素材 页面开发 页面样式开发 功能实现截图 总结 本地生活 本地生活”微信小程序是一个介绍本地美食、装修、工作等信息的微信小程序&#xff0c;该微信小程序的首页包含轮播图区域和九宫…

基于SpringBoot实现的编程训练系统(代码+论文)

&#x1f389;博主介绍&#xff1a;Java领域优质创作者&#xff0c;阿里云博客专家&#xff0c;计算机毕设实战导师。专注Java项目实战、毕设定制/协助 &#x1f4e2;主要服务内容&#xff1a;选题定题、开题报告、任务书、程序开发、项目定制、论文辅导 &#x1f496;精彩专栏…

泷羽sec-shell (3)脚本参数传递与数学运算

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

QT实战--qt各种按钮实现

本篇介绍qt一些按钮的实现&#xff0c;包括正常按钮&#xff1b;带有下拉箭头的按钮的各种实现&#xff1b;按钮和箭头两部分分别响应&#xff1b;图片和按钮大小一致&#xff1b;图片和按钮大小不一致的处理&#xff1b;文字和图片位置的按钮 效果图如下&#xff1a; 详细实现…

MTK主板_小型联发科安卓主板_行业智能终端主板基于联发科方案

MTK安卓主板是一款小巧而高效的科技产品&#xff0c;其尺寸仅为43.4mm x 57.6mm。采用了先进的联发科12nm制程工艺&#xff0c;这款主板搭载四核或八核64位A53架构的CPU&#xff0c;主频高达2.0GHz&#xff0c;不但保证了出色的计算能力&#xff0c;还实现了超低功耗的特点。系…

Web day02 Js Vue Ajax

目录 1.javascript: 1.js的引入方式&#xff1a; 2.js变量 & 数据类型 & 输出语句&#xff1a; 模板字符串&#xff1a; 3.函数 & 自定义对象&#xff1a; 4. json 字符串 & DOM操作&#xff1a; 5. js事件监听&#xff1a; 6.js的模块化导入或者导出&a…