You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用MFC CTreeCtrl替代Stingray控件实现多尺寸图标树?

用MFC CTreeCtrl实现多尺寸图标树控件

完全可以通过自定义CTreeCtrl子类,结合自定义绘制和动态调整树项高度来替代Stingray的多尺寸图标树控件效果,具体实现代码如下:

1. 自定义CTreeCtrl子类定义

class CMultiSizeIconTreeCtrl : public CTreeCtrl
{
    DECLARE_DYNAMIC(CMultiSizeIconTreeCtrl)

public:
    CMultiSizeIconTreeCtrl();
    virtual ~CMultiSizeIconTreeCtrl();

protected:
    // 存储每个树项的图标句柄和对应尺寸
    CMap<HTREEITEM, HTREEITEM, HICON, HICON> m_mapItemIcons;
    CMap<HTREEITEM, HTREEITEM, CSize, CSize> m_mapItemIconSizes;

    // 消息处理函数
    afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
    afx_msg LRESULT OnSetItemHeight(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()

public:
    // 为指定树项设置自定义图标及尺寸
    void SetItemCustomIcon(HTREEITEM hItem, HICON hIcon, const CSize& iconSize);
};

2. 子类实现代码

IMPLEMENT_DYNAMIC(CMultiSizeIconTreeCtrl, CTreeCtrl)

CMultiSizeIconTreeCtrl::CMultiSizeIconTreeCtrl()
{
}

CMultiSizeIconTreeCtrl::~CMultiSizeIconTreeCtrl()
{
    // 释放所有图标资源
    POSITION pos = m_mapItemIcons.GetStartPosition();
    HTREEITEM hItem;
    HICON hIcon;
    while (pos)
    {
        m_mapItemIcons.GetNextAssoc(pos, hItem, hIcon);
        if (hIcon) DestroyIcon(hIcon);
    }
}

BEGIN_MESSAGE_MAP(CMultiSizeIconTreeCtrl, CTreeCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMultiSizeIconTreeCtrl::OnCustomDraw)
    ON_MESSAGE(TVM_SETITEMHEIGHT, &CMultiSizeIconTreeCtrl::OnSetItemHeight)
END_MESSAGE_MAP()

void CMultiSizeIconTreeCtrl::SetItemCustomIcon(HTREEITEM hItem, HICON hIcon, const CSize& iconSize)
{
    if (!hItem || !hIcon) return;

    m_mapItemIcons[hItem] = hIcon;
    m_mapItemIconSizes[hItem] = iconSize;
    RedrawItems(hItem, hItem); // 刷新该项
}

LRESULT CMultiSizeIconTreeCtrl::OnSetItemHeight(WPARAM wParam, LPARAM lParam)
{
    // 拦截默认统一设置项高度的消息,改为动态控制每个项高度
    return 0;
}

void CMultiSizeIconTreeCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
    NMTVCUSTOMDRAW* pTVCD = reinterpret_cast<NMTVCUSTOMDRAW*>(pNMHDR);
    *pResult = CDRF_DODEFAULT;

    switch (pTVCD->nmcd.dwDrawStage)
    {
        case CDDS_PREPAINT:
            *pResult = CDRF_NOTIFYITEMDRAW;
            break;

        case CDDS_ITEMPREPAINT:
        {
            HTREEITEM hItem = pTVCD->nmcd.dwItemSpec;
            CSize iconSize;
            if (m_mapItemIconSizes.Lookup(hItem, iconSize))
            {
                // 调整当前项高度为图标高度加边距
                pTVCD->nmcd.rcItem.bottom = pTVCD->nmcd.rcItem.top + iconSize.cy + 4;
            }
            *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_SKIPDEFAULT;
            break;
        }

        case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
        {
            HTREEITEM hItem = pTVCD->nmcd.dwItemSpec;
            HICON hIcon;
            CSize iconSize;
            if (m_mapItemIcons.Lookup(hItem, hIcon) && m_mapItemIconSizes.Lookup(hItem, iconSize))
            {
                // 计算图标居中绘制位置
                int x = pTVCD->nmcd.rcItem.left + (pTVCD->nmcd.rcItem.right - pTVCD->nmcd.rcItem.left - iconSize.cx) / 2;
                int y = pTVCD->nmcd.rcItem.top + (pTVCD->nmcd.rcItem.bottom - pTVCD->nmcd.rcItem.top - iconSize.cy) / 2;
                
                // 绘制图标
                DrawIcon(pTVCD->nmcd.hdc, x, y, hIcon);

                // 绘制树项文本(可根据需求调整位置)
                CString strText;
                GetItemText(hItem, strText);
                RECT textRect = pTVCD->nmcd.rcItem;
                textRect.left += iconSize.cx + 8; // 图标与文本间距8px
                DrawText(pTVCD->nmcd.hdc, strText, strText.GetLength(), &textRect, DT_VCENTER | DT_SINGLELINE);
            }
            *pResult = CDRF_SKIPDEFAULT;
            break;
        }
    }
}

3. 实际使用示例

在对话框或窗口中集成该控件:

// 对话框类中声明成员变量
CMultiSizeIconTreeCtrl m_multiSizeTree;

// 在OnInitDialog中初始化
BOOL CYourDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 方式1:动态创建控件
    m_multiSizeTree.Create(WS_VISIBLE | WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, 
                          CRect(20, 20, 350, 450), this, IDC_MULTI_SIZE_TREE);

    // 方式2:绑定资源编辑器中的树控件(需在类向导中关联自定义子类)

    // 添加带不同尺寸图标的树项
    HTREEITEM hBigItem = m_multiSizeTree.InsertItem(_T("大图标节点"));
    HICON hBigIcon = AfxGetApp()->LoadIcon(IDI_ICON_32); // 32x32尺寸图标
    m_multiSizeTree.SetItemCustomIcon(hBigItem, hBigIcon, CSize(32, 32));

    HTREEITEM hSmallItem = m_multiSizeTree.InsertItem(_T("小图标节点"));
    HICON hSmallIcon = AfxGetApp()->LoadIcon(IDI_ICON_16); // 16x16尺寸图标
    m_multiSizeTree.SetItemCustomIcon(hSmallItem, hSmallIcon, CSize(16, 16));

    return TRUE;
}

关键实现说明

  • 通过NM_CUSTOMDRAW消息接管树控件的绘制逻辑,跳过默认绘制,自主控制图标和文本的渲染
  • 用CMap存储每个树项的图标和尺寸信息,实现不同节点的差异化显示
  • 动态调整每个树项的绘制区域高度,确保大图标能完整展示
  • 在析构函数中释放图标资源,避免内存泄漏

内容的提问来源于stack exchange,提问作者Graphman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:42:12