MFC List 控件详解:高效材料展示与管理

news/2025/9/25 19:54:34/文章来源:https://www.cnblogs.com/slgkaifa/p/19111887

MFC List 控件详解:高效材料展示与管理

在 MFC 开发中,List 控件(列表控件)是用于展示多行多列数据的强大工具,类似于表格但更加灵活。它由 CListCtrl 类封装,支持多种视图风格和丰富的交互功能,广泛应用于数据管理、文件浏览等场景。本文将全面介绍 List 控件的使用方法,从基础配置到高级功能,帮助开发者快速掌握。

一、List 控件的基本概念

List 控件(CListCtrl)继承自 CWnd 类,主要用于以列表形式展示数据,核心特点包括:

  • 支持四种视图风格:图标视图(Icon)、小图标视图(Small Icon)、列表视图(List)和报表视图(Report)
  • 报表视图(Report)支持多列显示,类似表格结构
  • 可实现数据排序、筛选、选择、编辑等交互操作
  • 支持右键菜单、拖放、虚拟列表等高级功能

最常用的是报表视图(Report),适合展示结构化数据,本文将以此为重点进行讲解。

二、List 控件的创建与初始化

2.1 对话框资源创建(可视化方式)

  1. 打开对话框资源编辑器,从工具箱中拖拽 "List Control" 到对话框

  2. 右键点击控件,选择 "Properties" 配置基本属性:

    • View:设置初始视图风格(Report 为报表视图)
    • Single Selection:是否允许单选(默认允许多选)
    • Grid Lines:是否显示网格线
    • Full Row Select:是否选中整行
    • Edit Labels:是否允许编辑项目文本
  3. 添加成员变量:

    • 右键点击控件,选择 "Add Variable"
    • 变量类型选择 CListCtrl,变量名建议为 m_listCtrl

2.2 动态创建方法

通过 CListCtrl::Create 函数在运行时创建:

cpp

运行

// 在 OnInitDialog 或其他初始化函数中
CRect rect(10, 10, 500, 300);  // 位置和大小
m_listCtrl.Create(
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS | WS_BORDER,
rect,
this,
IDC_MYLISTCTRL  // 自定义ID
);
// 设置扩展风格(网格线、整行选择等)
m_listCtrl.SetExtendedStyle(
m_listCtrl.GetExtendedStyle() |
LVS_EX_GRIDLINES |        // 显示网格线
LVS_EX_FULLROWSELECT |    // 整行选择
LVS_EX_CHECKBOXES         // 显示复选框
);

常用风格说明:

  • 基础风格:WS_CHILD | WS_VISIBLE | WS_BORDER(必须)
  • 视图风格:LVS_REPORT(报表视图,最常用)
  • 交互风格:LVS_EDITLABELS(允许编辑标签)、LVS_SINGLESEL(单选)

扩展风格(通过 SetExtendedStyle 设置):

  • LVS_EX_GRIDLINES:显示网格线
  • LVS_EX_FULLROWSELECT:选中整行
  • LVS_EX_CHECKBOXES:为每行添加复选框
  • LVS_EX_SUBITEMIMAGES:子项支持图标
  • LVS_EX_HEADERDRAGDROP:允许列拖拽调整顺序

三、List 控件的基本操作

3.1 添加列(报表视图必备)

在报表视图中,首先需要添加列标题:

cpp

运行

// 添加列:参数分别为列索引、列名、对齐方式、列宽
m_listCtrl.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 60);
m_listCtrl.InsertColumn(1, _T("姓名"), LVCFMT_LEFT, 100);
m_listCtrl.InsertColumn(2, _T("年龄"), LVCFMT_CENTER, 60);
m_listCtrl.InsertColumn(3, _T("部门"), LVCFMT_LEFT, 120);

调整列宽的方法:

cpp

运行

// 自动调整列宽以适应内容
m_listCtrl.SetColumnWidth(0, LVSCW_AUTOSIZE);        // 适应内容
m_listCtrl.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);  // 适应标题

3.2 添加行数据

使用 InsertItem 添加行,SetItemText 设置子项内容:

cpp

运行

// 添加第一行
int nItem = m_listCtrl.InsertItem(0, _T("001"));  // 第一个参数为行索引,第二个为第一列内容
m_listCtrl.SetItemText(nItem, 1, _T("张三"));     // 设置第1列(0-based)内容
m_listCtrl.SetItemText(nItem, 2, _T("25"));
m_listCtrl.SetItemText(nItem, 3, _T("研发部"));
// 添加第二行
nItem = m_listCtrl.InsertItem(1, _T("002"));
m_listCtrl.SetItemText(nItem, 1, _T("李四"));
m_listCtrl.SetItemText(nItem, 2, _T("30"));
m_listCtrl.SetItemText(nItem, 3, _T("市场部"));
// 批量添加示例
CStringArray strData;
// ... 假设已填充数据 ...
for (int i = 0; i < strData.GetSize(); i += 4)
{
nItem = m_listCtrl.InsertItem(i/4, strData[i]);
m_listCtrl.SetItemText(nItem, 1, strData[i+1]);
m_listCtrl.SetItemText(nItem, 2, strData[i+2]);
m_listCtrl.SetItemText(nItem, 3, strData[i+3]);
}

3.3 获取与修改数据

获取选中行数据:

cpp

运行

// 获取选中行
POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
if (pos == NULL)
AfxMessageBox(_T("没有选中任何项"));
else
{
while (pos)
{
int nItem = m_listCtrl.GetNextSelectedItem(pos);
CString strID = m_listCtrl.GetItemText(nItem, 0);
CString strName = m_listCtrl.GetItemText(nItem, 1);
// ... 处理数据 ...
AfxMessageBox(_T("选中: ") + strID + _T(" - ") + strName);
}
}

修改指定单元格数据:

cpp

运行

// 修改第2行(索引1)第3列(索引2)的数据
m_listCtrl.SetItemText(1, 2, _T("31"));

3.4 删除数据

cpp

运行

// 删除选中行
POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
while (pos)
{
int nItem = m_listCtrl.GetNextSelectedItem(pos);
m_listCtrl.DeleteItem(nItem);
}
// 清空所有数据
m_listCtrl.DeleteAllItems();
// 删除指定列(需要先删除所有行)
m_listCtrl.DeleteAllItems();
m_listCtrl.DeleteColumn(0);  // 删除第1列

四、List 控件的消息处理

List 控件支持多种事件消息,常用的包括:

4.1 选中项变化(LVN_ITEMCHANGED)

当选中项发生变化时触发:

cpp

运行

// 头文件声明
afx_msg void OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult);
// 消息映射
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_MYLISTCTRL, &CMyDialog::OnLvnItemchangedList)
END_MESSAGE_MAP()
// 实现
void CMyDialog::OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR);
// 检查是否是选中状态变化
if (pNMLV->uChanged & LVIF_STATE)
{
// 检查项是否被选中
if (pNMLV->uNewState & LVIS_SELECTED)
{
// 项被选中
CString strMsg;
strMsg.Format(_T("选中了第 %d 行"), pNMLV->iItem);
AfxMessageBox(strMsg);
}
else
{
// 项被取消选中
}
}
*pResult = 0;
}

4.2 双击项(NM_DBLCLK)

处理双击事件:

cpp

运行

// 头文件声明
afx_msg void OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult);
// 消息映射
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
ON_NOTIFY(NM_DBLCLK, IDC_MYLISTCTRL, &CMyDialog::OnNMDblclkList)
END_MESSAGE_MAP()
// 实现
void CMyDialog::OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
// 获取双击的行和列
int nRow = pNMItemActivate->iItem;
int nCol = pNMItemActivate->iSubItem;
if (nRow != -1)  // 确保点击的是有效行
{
CString strData = m_listCtrl.GetItemText(nRow, nCol);
CString strMsg;
strMsg.Format(_T("双击了第 %d 行第 %d 列: %s"), nRow, nCol, strData);
AfxMessageBox(strMsg);
}
*pResult = 0;
}

4.3 右键菜单(NM_RCLICK)

为 List 控件添加右键菜单:

cpp

运行

// 头文件声明
afx_msg void OnNMRclickList(NMHDR *pNMHDR, LRESULT *pResult);
// 消息映射
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
ON_NOTIFY(NM_RCLICK, IDC_MYLISTCTRL, &CMyDialog::OnNMRclickList)
END_MESSAGE_MAP()
// 实现
void CMyDialog::OnNMRclickList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
*pResult = 0;
// 获取右键点击的位置
CPoint pt;
GetCursorPos(&pt);
// 创建右键菜单
CMenu menu;
menu.LoadMenu(IDR_MENU_LIST);  // 加载预先定义的菜单资源
CMenu* pPopup = menu.GetSubMenu(0);
// 显示菜单
if (pPopup)
{
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
}
}

五、高级功能实现

5.1 数据排序

实现点击列标题进行排序:

cpp

运行

// 声明排序比较函数(必须是全局或静态函数)
static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
// lParamSort 用于传递排序信息(列索引和排序方向)
int nCol = (int)(lParamSort & 0xFF);
BOOL bAscending = (lParamSort & 0xFF00) ? TRUE : FALSE;
CListCtrl* pList = (CListCtrl*)((lParamSort >> 16) & 0xFFFF);
CString strItem1 = pList->GetItemText((int)lParam1, nCol);
CString strItem2 = pList->GetItemText((int)lParam2, nCol);
int nResult = strItem1.Compare(strItem2);
// 如果是数字列,可以转换为数值后比较
// int n1 = _ttoi(strItem1);
// int n2 = _ttoi(strItem2);
// int nResult = n1 - n2;
return bAscending ? nResult : -nResult;
}
// 处理列点击事件(HDN_ITEMCLICK)
afx_msg void OnHdnItemclickList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMHEADER pNMHdr = reinterpret_cast(pNMHDR);
static int nSortCol = 0;       // 上次排序的列
static BOOL bAscending = TRUE; // 排序方向
// 如果点击的是同一列,则切换排序方向
if (pNMHdr->iItem == nSortCol)
{
bAscending = !bAscending;
}
else
{
nSortCol = pNMHdr->iItem;
bAscending = TRUE;
}
// 准备排序参数(列索引 | 排序方向 | List控件指针)
LPARAM lParamSort = nSortCol | (bAscending ? 0x100 : 0) | ((DWORD_PTR)this->m_listCtrl.GetSafeHwnd() << 16);
// 为每个项设置lParam(用于排序)
for (int i = 0; i < m_listCtrl.GetItemCount(); i++)
{
m_listCtrl.SetItemData(i, i);
}
// 执行排序
m_listCtrl.SortItems(CompareFunc, lParamSort);
*pResult = 0;
}

5.2 复选框功能

当设置了 LVS_EX_CHECKBOXES 扩展风格后,可以使用复选框功能:

cpp

运行

// 勾选指定行
m_listCtrl.SetCheck(0, TRUE);  // 勾选第1行
// 获取勾选状态
BOOL bChecked = m_listCtrl.GetCheck(0);  // 获取第1行的勾选状态
// 获取所有勾选行
CString strChecked;
for (int i = 0; i < m_listCtrl.GetItemCount(); i++)
{
if (m_listCtrl.GetCheck(i))
{
strChecked += m_listCtrl.GetItemText(i, 1) + _T("\n");
}
}
if (!strChecked.IsEmpty())
{
AfxMessageBox(_T("勾选的项:\n") + strChecked);
}

5.3 虚拟列表(大数据优化)

当需要显示大量数据(如 10 万行以上)时,普通列表会占用大量内存且加载缓慢,此时应使用虚拟列表(Virtual List):

cpp

运行

// 启用虚拟列表风格
m_listCtrl.ModifyStyle(LVS_OWNERDATA, 0);
m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | LVS_EX_VIRTUAL);
// 设置项目总数
const int nTotalItems = 100000;  // 10万条数据
m_listCtrl.SetItemCount(nTotalItems);
// 处理虚拟列表数据请求消息(LVN_GETDISPINFO)
afx_msg void OnLvnGetdispinfoList(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast(pNMHDR);
LV_ITEM *pItem = &(pDispInfo->item);
// pItem->iItem 是请求的数据行索引
// pItem->iSubItem 是请求的列索引
// 填充数据
if (pItem->mask & LVIF_TEXT)  // 请求文本数据
{
switch (pItem->iSubItem)
{
case 0:  // ID列
_stprintf_s(pItem->pszText, pItem->cchTextMax, _T("%03d"), pItem->iItem + 1);
break;
case 1:  // 姓名列
_stprintf_s(pItem->pszText, pItem->cchTextMax, _T("用户%d"), pItem->iItem + 1);
break;
case 2:  // 年龄列
_stprintf_s(pItem->pszText, pItem->cchTextMax, _T("%d"), 20 + (pItem->iItem % 30));
break;
case 3:  // 部门列
CString strDept[] = {_T("研发部"), _T("市场部"), _T("财务部"), _T("人事部")};
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strDept[pItem->iItem % 4]);
break;
}
}
*pResult = 0;
}

虚拟列表的优势是只在需要显示时才加载对应的数据,大大减少内存占用和初始加载时间。

六、常见问题与解决方案

问题 1:列表控件不显示网格线

解决方案:确保已设置扩展风格 LVS_EX_GRIDLINES

cpp

运行

m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | LVS_EX_GRIDLINES);

问题 2:无法选中整行

解决方案:设置扩展风格 LVS_EX_FULLROWSELECT

cpp

运行

m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

问题 3:添加大量数据时界面卡顿

解决方案

  1. 使用虚拟列表(适合 1 万行以上数据)
  2. 添加数据前锁定控件更新:

cpp

运行

m_listCtrl.SetRedraw(FALSE);  // 停止重绘
// ... 批量添加数据 ...
m_listCtrl.SetRedraw(TRUE);   // 恢复重绘
m_listCtrl.Invalidate();      // 强制刷新

问题 4:排序功能不生效

解决方案

  1. 确保已为每个项设置了 lParam(通过 SetItemData
  2. 检查比较函数是否正确实现
  3. 确认 SortItems 函数调用正确

七、总结

List 控件是 MFC 中功能强大的数据展示组件,尤其在报表视图下可以高效展示结构化数据。本文介绍了 List 控件的创建配置、基本操作、消息处理和高级功能,包括数据排序、复选框、虚拟列表等实用技术。

在实际开发中,应根据数据量大小选择合适的使用方式:小数据量可直接使用常规方法;大数据量建议采用虚拟列表;需要频繁交互时应合理处理各种事件消息。

掌握 List 控件的使用,能够帮助开发者构建更加专业、高效的数据管理界面,提升应用程序的用户体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917447.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

历程回顾-(2024-2025)

其实我刚写了一版本了,但是博客园他对于笔记不会随时留档,全没了。 605天的入职时间,经历精彩纷呈,值得总结与交代。 业务周期分成三个阶段,入职五个月+中间一年+结尾三个月。分别是总览->细化->试验,周期…

CF Round 1053(2150 2151) 总结

CF Round 1053(2150 & 2151) 总结 Div2 A 若存在 \(a_i\ge a_i+1\) 那么只出现一次,否则出现 \(n-a_m+1\) 次。 A 我们不能每次从头开始走,考虑怎么利用上一轮的信息。 假设我们要求第 \(k\) 轮的终点,由于第 \…

做投票网站教程应用软件有哪些

效果图 思路&#xff1a; 1. 高亮的色块是独立的一个盒子&#xff0c;需要插入当前激活的内容用来撑开色块盒子的宽度&#xff0c;这样色块的宽度就会和当前激活的内容宽度一致&#xff0c;色块的字体颜色设置透明即可 2. 色块滑动的距离是读当前激活元素的offsetLeft&#x…

帮人盖章网站备案授权书秦皇岛市海港区建设局网站

趣 问 万 物来源&#xff1a;把科学带回家撰文&#xff1a;Mirror如何分离糖和盐&#xff1f;图源&#xff1a;Pixabay小手一抖&#xff0c;不小心把糖(蔗糖)和盐(氯化钠)混在一块儿了该怎么办&#xff1f;趁着光棍节&#xff0c;就让我们吃饱了撑着研究研究把糖和盐拆散的N种方…

20250922_QQ_backdoor

流量分析, 应急响应, WebShell, 哥斯拉, Godzilla, 空白字符隐写, AES, DASCTFTags:流量分析,应急响应,WebShell,哥斯拉,Godzilla,空白字符隐写,AES,DASCTF 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMY…

实用指南:【Java八股文】13-中间件面试篇

实用指南:【Java八股文】13-中间件面试篇pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

AT_agc012_d [AGC012D] Colorful Balls

考虑到可操作的两个球连边,同一个连通块一定能任意交换,方案数是好算的,现在我们需要优化这个建边的过程。 首先,对于同色球,先只考虑最小值和其他球连边,这样一定最优,然后,对于异色球,我们选最小值,非最小…

网站建设套路毕节市交通建设集团网站

可以用&#xff0c;现在很多本地生活商家&#xff0c;都会通过借助批量剪辑工具来提升视频的曝光量&#xff0c;从而带动店铺的客流量。 推荐本地生活商家使用超级编导批量剪辑工具&#xff0c;这是一款0基础小白也可以很快上手的批量剪辑工具&#xff0c;剪辑页面布局以及功能…

销售网站html源码淮南市官网

运用 Transformers 库来完成翻译任务。翻译是典型的序列到序列 (sequence-to-sequence, Seq2Seq) 任务&#xff0c;即对于每一个输入序列都会输出一个对应的序列。翻译在任务形式上与许多其他任务很接近&#xff0c;例如&#xff1a; 文本摘要 (Summarization)&#xff1a;将长…

02、Python从入门到癫狂:函数与资料容器

02、Python从入门到癫狂:函数与资料容器pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

9/25

今天学了算法数结构,学了许多与线性表相关的知识。

社区服务呼叫系统 网站的建设网站效果图设计

提供帧动画组件来实现逐帧播放图片的能力&#xff0c;可以配置需要播放的图片列表&#xff0c;每张图片可以配置时长。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 ImageAni…

关闭Edge浏览器页面的圆角效果

起因 edge浏览器的这个圆角我看着不太习惯 ,特别是在看一些博客文章的时候 ,总感觉不太舒服 ,所以想给他关闭掉关闭或者开启设置在浏览器搜索框输入edge://flags然后再在页面的搜索框中输入Microsoft Edge rounded …

搜索二维矩阵II-leetcode

题目描述 编写一个高效的算法来搜索 *m* x *n* 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:每行的元素从左到右升序排列。 每列的元素从上到下升序排列。示例 1:输入:matrix = [[1,4,7,11,15],[2,5,8…

Rust/C/C++ 混合构建 - Cmake集成Cargo编译动态库

Cmake是一个强大的多语言编译工具,其内置模块提供了强大的扩展能力。 Rust官方的构建工具是Cargo,其提供了依赖下载,上传和编译等多项功能,极大的便利了Rust项目的开发。 目前C++多数用Cmake构建,如果是Rust集成c…

小型公司网站建设知乎济南网站建设网站建设

文章目录 1. Kubernetes的网络类别2. Kubernetes的接口类型3. CNI网络插件 ---- Flannel的介绍及部署3.1 简介3.2 flannel的三种模式3.3 flannel的UDP模式工作原理3.4 flannel的VXLAN模式工作原理3.5 Flannel CNI 网络插件部署3.5.1 上传flannel镜像文件和插件包到node节点3.5.…

织梦网站图片一直转圈品质好物推荐

AOP(Aspect Oriented Programming)&#xff0c;面向切面编程&#xff0c;他是一种编程范式。 作用&#xff1a; 在不改变原始设计的的基础上对其进行功能增强。 几个基本概念&#xff1a; 连接点&#xff1a;所有的方法 切入点&#xff1a;追加功能的方法 通知&#xff1a;追加…

LangChain:LLMs和ChatModels介绍、LangChain 集成大模型的本地部署与 API 调用实践、提示词prompt、输出解析器、链 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

卓伊凡的第一款独立游戏-unity安装运行设置以及熟悉整体unity游戏开发和unity editor【02】-优雅草卓伊凡

卓伊凡的第一款独立游戏-unity安装运行设置以及熟悉整体unity游戏开发和unity editor【02】-优雅草卓伊凡2025-09-25 19:26 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: no…

学习敏捷课程PSM,自考证书分享

PSM课程自考体会心得​ 一直以来,我都对Scrum比较感兴趣,但真正检验自己对Scrum框架的掌握程度,是从准备 PSM(Professional Scrum Master)认证考试 开始的。相比参加培训班,我选择了自考的方式,这段学习过程对我…