note

  • 虚拟列表可有效加载大量数据
  • 需要处理listctrl的 LVN_GETDISPINFO 消息
  • 使用虚拟列表 不能 使用 insert的方法插入数据,而是告诉listctrl 当前要显示多少行
  • 使用SetItemCount函数告诉listctrl当前显示多少行
  • 使用虚拟列表需要自己维护数据源

使用

  • 添加list ctrl 控件
  • 将其Owner Data 属性 设置为 True

  • 添加listctrl的 LVN_GETDISPINFO 的消息

范例

  • 初始化 listctrl, list_ctrl是自己添加的控件变量
		LONG lStyle;
lStyle = GetWindowLong(list_ctrl.m_hWnd , GWL_STYLE);//获取当前窗口style
lStyle &= ~LVS_TYPEMASK; //清除显示方式位
lStyle |= LVS_REPORT; //设置style
SetWindowLong(list_ctrl.m_hWnd , GWL_STYLE , lStyle);//设置style
DWORD dwStyle = list_ctrl.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl)
dwStyle |= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl)
dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件
dwStyle |= LVS_EX_DOUBLEBUFFER;
list_ctrl.SetExtendedStyle(dwStyle); //设置扩展风格
  • 这里创建了2列
		list_ctrl.InsertColumn(0 , L"ID" , LVCFMT_LEFT , 40);//插入列
list_ctrl.InsertColumn(1 , L"NAME" , LVCFMT_LEFT , 50);
  • 添加LVN_GETDISPINFO消息处理函数
void CMFCApplication1Dlg::OnLvnGetdispinfoList1(NMHDR *pNMHDR , LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR); *pResult = 0;
}
  • 该函数用于显示 行的每一列,函数体实现如下
void CMFCApplication1Dlg::OnLvnGetdispinfoList1(NMHDR *pNMHDR , LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
if (nullptr == pDispInfo)
{
*pResult = 0;
return;
} LV_ITEM* pItem= &(pDispInfo)->item;
if (nullptr == pItem)
{
*pResult = 0;
return;
} /// 自己维护的数据源中没有数据
if (0 >= map_list_item_.size())
{
*pResult = 0;
return;
} /// 当前请求显示的是第几行
int cur_row_index = pItem->iItem; if (LVIF_TEXT & pItem->mask) //字符串缓冲区有效
{
/// 当前请求显示的是第几列
int cur_column_index = pItem->iSubItem; switch (cur_column_index)
{
/// 第一列
case 0:
{ CString str_id;
/// 将id转为字符串
str_id.Format(L"%d", map_list_item_[cur_row_index].id_);
/// 显示第一列
lstrcpy(pItem->pszText, str_id); }
break; /// 第二列
case 1:
{
/// 显示第二列
lstrcpy(pItem->pszText, map_list_item_[cur_row_index].name_);
}
break; default:
break;
} } *pResult = 0;
}

其中,map_list_item_是我自己维护的数据源,其定义如下

	/// <id列,name列>
std::map<int, list_ctrl_item> map_list_item_;

用作测试使用,list_ctrl_item的定义如下:

struct st_list_item_
{
int id_ = 0;
CString name_ = _T(""); void zero_()
{
id_ = 0;
name_ = _T("");
}
st_list_item_()
{
zero_();
}
}; using list_ctrl_item = st_list_item_;
  • OnInitDialog() 函数中对数据源做了添加
		/// 插入数据
auto insert_item = [&](int index)
{
list_ctrl_item item;
item.id_ = index;
item.name_.Format(L"%d", index); map_list_item_.insert(std::make_pair(index, item));
}; insert_item(0);
insert_item(1);
insert_item(2);
insert_item(3);
  • 自己用了定时器, 定时告诉list_ctrl刷新数据
void CMFCApplication1Dlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == ktimer_id)
{
list_ctrl.SetItemCount(map_list_item_.size());
list_ctrl.Invalidate();
} CDialogEx::OnTimer(nIDEvent);
}

效果

完整源码

头文件


// MFCApplication1Dlg.h : 头文件
// #pragma once
#include "afxcmn.h" #include "list_ctrl_ex.h" #include <map> struct st_list_item_
{
int id_ = 0;
CString name_ = _T(""); void zero_()
{
id_ = 0;
name_ = _T("");
}
st_list_item_()
{
zero_();
}
}; using list_ctrl_item = st_list_item_; // CMFCApplication1Dlg 对话框
class CMFCApplication1Dlg : public CDialogEx
{ enum
{
ktimer_id = 1000,
kinterval = 500,
}; // 构造
public:
CMFCApplication1Dlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 UINT_PTR timer_id = 0; void uninit_(); /// <id列,name列>
std::map<int, list_ctrl_item> map_list_item_; // 实现
protected:
HICON m_hIcon; // 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
list_ctrl_ex list_ctrl;
afx_msg void OnLvnGetdispinfoList1(NMHDR *pNMHDR , LRESULT *pResult);
afx_msg void OnLvnOdfinditemList1(NMHDR *pNMHDR , LRESULT *pResult);
afx_msg void OnLvnOdcachehintList1(NMHDR *pNMHDR , LRESULT *pResult);
};

cpp


// MFCApplication1Dlg.cpp : 实现文件
// #include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#include <windowsx.h> #ifdef _DEBUG
#define new DEBUG_NEW
#endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx
{
public:
CAboutDlg(); // 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
DECLARE_MESSAGE_MAP()
}; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
} void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP() // CMFCApplication1Dlg 对话框 CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
} void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX , IDC_LIST1 , list_ctrl);
} /// ----------------------------------------------------------------
/// @brief:
/// ----------------------------------------------------------------
void CMFCApplication1Dlg::uninit_()
{
if (0 != timer_id)
{
KillTimer(timer_id);
timer_id = 0;
}
} BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_NOTIFY(LVN_GETDISPINFO , IDC_LIST1 , &CMFCApplication1Dlg::OnLvnGetdispinfoList1)
ON_NOTIFY(LVN_ODFINDITEM , IDC_LIST1 , &CMFCApplication1Dlg::OnLvnOdfinditemList1)
ON_NOTIFY(LVN_ODCACHEHINT , IDC_LIST1 , &CMFCApplication1Dlg::OnLvnOdcachehintList1)
END_MESSAGE_MAP() // CMFCApplication1Dlg 消息处理程序 BOOL CMFCApplication1Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 {
LONG lStyle;
lStyle = GetWindowLong(list_ctrl.m_hWnd , GWL_STYLE);//获取当前窗口style
lStyle &= ~LVS_TYPEMASK; //清除显示方式位
lStyle |= LVS_REPORT; //设置style
SetWindowLong(list_ctrl.m_hWnd , GWL_STYLE , lStyle);//设置style
DWORD dwStyle = list_ctrl.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl)
dwStyle |= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl)
dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件
dwStyle |= LVS_EX_DOUBLEBUFFER;
list_ctrl.SetExtendedStyle(dwStyle); //设置扩展风格
}
{
list_ctrl.InsertColumn(0 , L"ID" , LVCFMT_LEFT , 40);//插入列
list_ctrl.InsertColumn(1 , L"NAME" , LVCFMT_LEFT , 50);
} {
/// 插入数据
auto insert_item = [&](int index)
{
list_ctrl_item item;
item.id_ = index;
item.name_.Format(L"%d", index); map_list_item_.insert(std::make_pair(index, item));
}; insert_item(0);
insert_item(1);
insert_item(2);
insert_item(3); } {
timer_id = SetTimer(ktimer_id, kinterval, NULL);
} return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
} void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
} // 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。 void CMFCApplication1Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
} /// ----------------------------------------------------------------
/// @brief:
/// ----------------------------------------------------------------
void CMFCApplication1Dlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == ktimer_id)
{
list_ctrl.SetItemCount(map_list_item_.size());
list_ctrl.Invalidate();
} CDialogEx::OnTimer(nIDEvent);
} //当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} void CMFCApplication1Dlg::OnLvnGetdispinfoList1(NMHDR *pNMHDR , LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
if (nullptr == pDispInfo)
{
*pResult = 0;
return;
} LV_ITEM* pItem= &(pDispInfo)->item;
if (nullptr == pItem)
{
*pResult = 0;
return;
} /// 自己维护的数据源中没有数据
if (0 >= map_list_item_.size())
{
*pResult = 0;
return;
} /// 当前请求显示的是第几行
int cur_row_index = pItem->iItem; if (LVIF_TEXT & pItem->mask) //字符串缓冲区有效
{
/// 当前请求显示的是第几列
int cur_column_index = pItem->iSubItem; switch (cur_column_index)
{
/// 第一列
case 0:
{ CString str_id;
/// 将id转为字符串
str_id.Format(L"%d", map_list_item_[cur_row_index].id_);
/// 显示第一列
lstrcpy(pItem->pszText, str_id); }
break; /// 第二列
case 1:
{
/// 显示第二列
lstrcpy(pItem->pszText, map_list_item_[cur_row_index].name_);
}
break; default:
break;
} } *pResult = 0;
} void CMFCApplication1Dlg::OnLvnOdfinditemList1(NMHDR *pNMHDR , LRESULT *pResult)
{
LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
} void CMFCApplication1Dlg::OnLvnOdcachehintList1(NMHDR *pNMHDR , LRESULT *pResult)
{
LPNMLVCACHEHINT pCacheHint = reinterpret_cast<LPNMLVCACHEHINT>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
}

vc mfc listctrl使用虚拟列表 虚拟列表用法的更多相关文章

  1. VC/MFC ListCtrl 控件功能使用汇总(转)

    以下未经说明,listctrl默认view 风格为report 相关类及处理函数 MFC:CListCtrl类 SDK:以 “ListView_”开头的一些宏.如 ListView_InsertCol ...

  2. VC++ 中ListCtrl经验总结

    先注明一下,这里,我们用m_listctrl来表示一个CListCtrl的类对象,然后这里我们的ListCtrl都是report形式,至于其他的如什么大图标,小图标的暂时不讲,毕竟report是大众话 ...

  3. 邮件服务配置(虚拟域&虚拟用户)

    邮件服务配置(虚拟域&虚拟用户) 现在我做的是: Linux + httpd + php + mariadb + postfix + dovecot + phpMyAdmin + postfi ...

  4. VC/MFC如何添加启动界面

    2015-05 转自 香远益清原文VC/MFC如何添加启动界面 1.基于框架类的应用程序添加启动画面的步骤(利用组件库中的Splash Screen组件生成Splash1.cpp 和Splash1.h ...

  5. VC++/MFC(VC6)开发技术精品学习资料下载汇总

    工欲善其事,必先利其器,VC开发MFC Windows程序,Visual C++或Visual Studio是必须的,恩,这里都给你总结好了,拿去吧:VC/MFC开发必备Visual C++.Visu ...

  6. VC ++ MFC activex 控件获取连接的VPN 信息

    vc++  MFC 进行activex  控件的开发步骤就不用多写了,只是简单的说明一下方法,以及具体的代码: 使用的类库是 windows 系统的 rasapi32.dll 记住需要添加的头文件如下 ...

  7. vc/mfc获取rgb图像数据后动态显示及保存图片的方法

    vc/mfc获取rgb图像数据后动态显示及保存图片的方法 该情况可用于视频通信中获取的位图数据回放显示或显示摄像头捕获的本地图像 第一种方法 #include<vfw.h> 加载 vfw3 ...

  8. 《Python CookBook2》 第四章 Python技巧 - 若列表中某元素存在则返回之 && 在无须共享引用的条件下创建列表的列表

    若列表中某元素存在则返回之 任务: 你有一个列表L,还有一个索引号i,若i是有效索引时,返回L[i],若不是,则返回默认值v 解决方案: 列表支持双向索引,所以i可以为负数 >>> ...

  9. Excel列表部分列表隐藏与取消隐藏

    Excel列表部分列表隐藏与取消隐藏 2014-2-19 隐藏:选中需要隐藏的列(选中A.B.C....),右键单击所选部分,选择"隐藏"即可. 取消隐藏:从A选中至所见表格最后的 ...

随机推荐

  1. Codeforces 788E - New task(线段树)

    Codeforces 题目传送门 & 洛谷题目传送门 这是一道 *2900 的 D1E,而且被!我!自!己!搞!出!来!了! 虽然我承认它难度及摆放的位置异常异常虚高,并且就算我到了现场也不可 ...

  2. sigma网格中水平压力梯度误差及其修正

    1.水平梯度误差产生 sigma坐标系下,笛卡尔坐标内水平梯度项对应形式为 \[\begin{equation} \left. \frac{\partial }{\partial x} \right| ...

  3. nginx_access_log的格式设置

    log_format <NAME> <Strin­­­g>; 关键字 格式标签 日志格式 关键字:其中关键字error_log不能改变 格式标签:格式标签是给一套日志格式设置一 ...

  4. MariaDB——简介

    一.MariaDB跟MySQL在绝大多数方面是兼容的,对于开发者来说,几乎感觉不到任何不同.是MySQL的代替品. MariaDB虽然被视为MySQL数据库的替代品,但它在扩展功能.存储引擎以及一些新 ...

  5. void * 指针和const 指针

    1.void * 是不能进行运算的,例如void *p  p++; 这2个值是没有任何规律的. 2 .printf的时候打印void *p 指向的数据,必须强制类型转换,因为编译器不知道取地址多少位. ...

  6. SCRDet——对小物体和旋转物体更具鲁棒性的模型

    引言 明确提出了三个航拍图像领域内面对的挑战: 小物体:航拍图像经常包含很多复杂场景下的小物体. 密集:如交通工具和轮船类,在航拍图像中会很密集.这个DOTA数据集的发明者也提到在交通工具和轮船类的检 ...

  7. 日常Java 2021/9/20

    Java随机数 运用Java的random函数实现猜数字游戏 随机产生一个1-50之间的数字,然后让玩家猜数,猜大猜小都给出提示,猜对后游戏停止 package pingchangceshi; imp ...

  8. nodeJs-querystring 模块

    JavaScript 标准参考教程(alpha) 草稿二:Node.js querystring 模块 GitHub TOP querystring 模块 来自<JavaScript 标准参考教 ...

  9. Linux:sqlldr命令

    第一步:写一个 ctl格式的控制文件 CTL 控制文件的内容 : load data --1. 控制文件标识 infile 'xxx.txt' --2. 要导入的数据文件名 insert into t ...

  10. 基于阿里云 ecs 使用 docker 方式部署 showDoc

    官网文档:https://www.showdoc.cc/help?page_id=65610 (建议先看下这个) 首先说明一下,我 ecs 镜像是 CentOS 7.6 64位 1. 首先在 服务器上 ...