MFC CListCtrl 虚拟列表使用详解
一、简介
CListCtrl 是MFC 库的一个列表控件,用于显示数据的。可以通过 InsertItem 快速向列表添加一行数据,并且用 SetItemText 设置其后面列的数据项。
但是本篇文章重点不是介绍 CListCtrl 控件的基本使用方法,而是介绍 CListCtrl 的扩展功能“虚拟列表”。
为什么使用虚拟列表?
使用 InsertItem 方法可以显示少量数据,但是数据量大或者是频繁刷新列表数据时,会出现缓慢甚至卡顿的现象。虚拟列表可以很好的解决该问题。
通过什么方式实现虚拟列表?
1、处理 LVN_GETDISPINFO 消息。
2、使用 SetItemCountEx 方法告诉列表要显示多少条数据。
3、使用 RedrawItems 方法刷新指定区间行的数据。
二、实验环境
开发语言:C++
OS 平台:windows 7
SDK:VS 2015
三、正文
3、1 新建一个基于对话框的MFC 工程。
3、2 在对话框中添加一个 ClistCtrl 控件,并将 view 的属性改为 Report 类型,Owner Data 的属性改为 true 。
3、3 为 CListCtrl 控件绑定变量 m_list1,并向列表添加数据列(OnInitDialog)。
DWORD dwStyle = m_list1.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_CHECKBOXES|LVS_EX_INFOTIP|LVS_EX_DOUBLEBUFFER;
m_list1.SetExtendedStyle(dwStyle);
m_list1.InsertColumn(0, "代码",LVCFMT_LEFT, 80);
m_list1.InsertColumn(1, "名称",LVCFMT_LEFT, 80);
m_list1.InsertColumn(2, "日期",LVCFMT_LEFT, 80);
3、4 到了这步后,不能直接向列表 InsertItem 数据。是需要定义自己的数据结构和向量。
// TestListVTableDlg.h
// 数据结构
struct WTSTickStruct{
char arrCode[20];
char arrName[20];
char arrDate[20];
};
// 自定义一个向量数据类型
typedef std::vector<WTSTickStruct> LST_Tick;
在 CTestListVTableDlg 中定义一个向量
LST_Tick m_LstTickData;
3、5 初始化向量数据,并通知列表刷新数据(OnInitDialog)
WTSTickStruct fd1={0};
WTSTickStruct fd2={0};
WTSTickStruct fd3={0};
strcpy(fd1.arrCode, "10000");
strcpy(fd1.arrName, "test1");
strcpy(fd1.arrDate, "2021-01-10");
strcpy(fd2.arrCode, "10001");
strcpy(fd2.arrName, "test2");
strcpy(fd2.arrDate, "2021-01-11");
strcpy(fd3.arrCode, "10002");
strcpy(fd3.arrName, "test3");
strcpy(fd3.arrDate, "2021-01-12");
m_LstTickData.push_back(fd1);
m_LstTickData.push_back(fd2);
m_LstTickData.push_back(fd3);
int iCount = m_LstTickData.size();
m_list1.SetItemCountEx(iCount);//告诉列表要显示多少数据
m_list1.RedrawItems(0,iCount);// 重绘第0~iCount行的数据
3、5 为 CListCtrl 控件添加 LVN_GETDISPINFO 消息,并在该消息函数中填充列表的数据。
void CTestListVTableDlg::OnLvnGetdispinfoList1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
LV_ITEM* pItem = &(pDispInfo)->item;
if(pItem==NULL) return;
CString strCell="";
int iItemIndx=pItem->iItem;//设置虚拟类表绘制的起始位置
if (pItem->mask & LVIF_TEXT)
{
switch(pItem->iSubItem)//iSubitem指列
{
case 0:
{
lstrcpy(pItem->pszText,m_LstTickData[iItemIndx].arrCode);
}
break;
case 1:
{
strCell.Format("%s",m_LstTickData[iItemIndx].arrName);
lstrcpy(pItem->pszText,strCell);
}
break;
case 2:
{
strCell.Format("%s",m_LstTickData[iItemIndx].arrDate);
lstrcpy(pItem->pszText,strCell);
}
break;
}
}
*pResult = 0;
}
3、6 到这里已经完成了虚拟列表的操作,可以编译运行了。
四、技能扩展
4、1 为虚拟列表添加排序功能,处理 LVN_COLUMNCLICK 消息。
lwei2: 好吧,还是从资源里加载好!
装饰自我pp: 加载的是磁盘里的图片,要指定路径
lwei2: 楼主,你自定义的控件应该不是从资源里加载的图片吧?
装饰自我pp: 感谢指正,写文档时疏忽了。是要把 Owner Data 属性设置为 true 才可以,博客内容已修正。
比奇堡鹏子哥: 虚拟列表必须要求ListCtrl是LVS_OWNERDATA样式吧?