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

MFC虚拟列表控件中如何替代SetItemData实现自绘行?

解决虚拟CListCtrl无法使用SetItemData的替代方案

我之前在把自绘CListCtrl转虚拟列表时,完全懂你碰到的这个痛点——虚拟列表的设计就是不依赖控件存储数据,所以SetItemData确实用不了,但咱们可以通过自己管理数据的方式完美替代这个功能,下面是几个经过实践验证的方案:

方案1:维护独立的数据存储容器(最推荐)

虚拟列表的核心逻辑是按需提供数据,所以我们可以自己创建一个容器,把原来存在SetItemData里的值和每行的显示数据绑定在一起,之后在自绘或数据回调时通过行索引直接取用。

步骤示例:

  1. 定义包含ItemData的行数据结构体:
// 假设你原来用SetItemData存DWORD类型的值,同时需要存储各列文本
struct ListRowData {
    DWORD dwItemData;   // 原来SetItemData保存的内容
    CString strColumn1; // 第一列显示文本
    CString strColumn2; // 第二列显示文本
    // 其他列数据按需添加
};
  1. 在你的列表控件类中声明容器成员:
std::vector<ListRowData> m_vRowData; // 存储所有行的完整数据
  1. 初始化时填充这个容器,并通知虚拟列表总行数:
// 假设你从数据源加载了40000条数据
m_vRowData.resize(40000);
for (int i = 0; i < 40000; ++i) {
    m_vRowData[i].dwItemData = GetMyItemData(i); // 原来给SetItemData的数值
    m_vRowData[i].strColumn1 = GetColumnText(i, 0);
    // ... 填充其他列
}
SetItemCountEx(40000, LVSICF_NOSCROLL); // 通知控件总行数
  1. 在自绘逻辑(比如NM_CUSTOMDRAW)中获取ItemData:
void CMyVirtualList::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {
    NMCUSTOMDRAW* pDrawInfo = reinterpret_cast<NMCUSTOMDRAW*>(pNMHDR);
    
    if (pDrawInfo->dwDrawStage == CDDS_ITEMPREPAINT) {
        int nRow = pDrawInfo->dwItemSpec;
        DWORD dwItemData = m_vRowData[nRow].dwItemData;
        
        // 这里就可以用dwItemData执行原来的自绘逻辑了
        // 比如根据值设置行背景色、字体样式等
        if (dwItemData & 0x01) {
            pDrawInfo->clrTextBk = RGB(255, 240, 240); // 高亮行
        }
    }
    
    *pResult = CDRF_DODEFAULT;
}

方案2:利用LVITEM的lParam字段传递ItemData

虚拟列表在处理LVN_GETDISPINFOW消息时,可以给LVITEM的lParam赋值,这个值会在后续的自绘消息中被传递过来,刚好能替代SetItemData的作用。

代码示例:

  1. 处理LVN_GETDISPINFOW消息时设置lParam:
void CMyVirtualList::OnLvnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) {
    NMLVDISPINFO* pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    LVITEM& item = pDispInfo->item;
    
    // 设置显示文本
    if (item.mask & LVIF_TEXT) {
        switch (item.iSubItem) {
            case 0:
                item.pszText = const_cast<LPTSTR>(m_vRowData[item.iItem].strColumn1.GetBuffer());
                item.cchTextMax = m_vRowData[item.iItem].strColumn1.GetLength();
                break;
            // 处理其他列...
        }
    }
    
    // 把原来的ItemData赋值给lParam
    if (item.mask & LVIF_PARAM) {
        item.lParam = m_vRowData[item.iItem].dwItemData;
    }
    
    *pResult = 0;
}
  1. 在自绘中获取lParam:
void CMyVirtualList::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {
    NMCUSTOMDRAW* pDrawInfo = reinterpret_cast<NMCUSTOMDRAW*>(pNMHDR);
    
    if (pDrawInfo->dwDrawStage == CDDS_ITEMPREPAINT) {
        DWORD dwItemData = static_cast<DWORD>(pDrawInfo->lItemlParam);
        // 用dwItemData执行自绘逻辑
    }
    
    *pResult = CDRF_DODEFAULT;
}

方案3:直接存储对象指针(如果原来ItemData是指针)

如果原来你用SetItemData存储的是自定义对象的指针,那更简单——直接在容器里存指针,自绘时通过行索引取出即可:

// 假设MyCustomObject是你原来存储指针的类
std::vector<MyCustomObject*> m_vItems;

// 在自绘中:
MyCustomObject* pObj = m_vItems[pDrawInfo->dwItemSpec];
if (pObj->IsSpecial()) {
    // 执行特殊绘制逻辑
}

关键注意事项

  • 确保虚拟列表已经开启LVS_OWNERDATA风格,否则无法正常工作;
  • 更新数据后,记得调用SetItemCountEx更新控件的总行数;
  • 如果涉及多线程更新数据,一定要加同步锁,避免容器访问冲突;
  • 容器的生命周期要和列表控件保持一致,避免出现悬空指针或无效数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:08:38