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 1513F - Swapping Problem(分类讨论+乱搞)

    Codeforces 题目传送门 & 洛谷题目传送门 简单题,难度 *2500 的 D2F,就当调节一下一模炸裂了的自闭的心情,稍微写写吧. 首先我看到这题的第一反应是分类讨论+数据结构,即枚 ...

  2. map与unordered_map区别及使用

    需要引入的头文件不同map: #include <map>unordered_map: #include <unordered_map> 内部实现机理不同map: map内部实 ...

  3. Apollo配置中心中的Namespace

    前言:Apollo(阿波罗)是携程研发的开源配置管理中心,能够集中管理应用不同环境.不同集群的配置.同一环境统一集群的配置下有namespace概念,具体信息官方文档如下: namespace是配置项 ...

  4. R语言与医学统计图形-【30】流行病学数据可视化

    sjPlot包适用于社会科学.流行病学中调查数据可视化,且能和SPSS数据无缝对接(流行病学问卷调查录入Epidata软件后,都会转成SPSS格式或EXCEL格式保存). 辅助包sjmisc进行数据转 ...

  5. Linux之vi和vim编辑器

    目录 1. vi和vim简介 2. vi 和 vim 的三种常见模式 2.1 正常模式 2.2 插入模式 2.3 命令行模式 3. 三种模式间的切换 4. 常用快捷键案例 5. 常用命令 1. vi和 ...

  6. shell 基本系统维护指令

    笔记 [1]man.passwd.su.echo命令的用法 (1)获取联机帮助 1)使用man命令可以找到特定的联机帮助页,并提供简短的命令说明.一般语法格式为: man commandname 2) ...

  7. SQL-用到的数据库语句总结

    0.SELECT * FROM  CHARACTER_SETS LIMIT 0,10   #从CHARACTER_SETS表中,从第1行开始,提取10行[包含第1行] 1.SELECT * FROM  ...

  8. 33、搜索旋转排序数组 | 算法(leetode,附思维导图 + 全部解法)300题

    零 标题:算法(leetode,附思维导图 + 全部解法)300题之(33)搜索旋转排序数组 一 题目描述! 题目描述 二 解法总览(思维导图) 三 全部解法 1 方案1 1)代码: // 方案1 & ...

  9. CPU如何同时运行多个进程?

    1 # -*- coding: utf-8 -*- 2 import re 3 mem = [x for x in re.split('[\r|\n]', ''' 4 store a 1 5 add ...

  10. hashtable深度探索

    1.什么是哈希表(hashtable)?为什么要发明哈希表? 首先回答第二个问题,在之前的数据结构中我们学习了数组,链表,二叉树等数据结构,记录在结构中的相对位置是随机的,和记录的关键字之前不存在确定 ...