ListView 的CUSTOMDRAW和LVS_OWNERDATA

ListView自定义绘制与数据管理

把 ListView 的CUSTOMDRAW和LVS_OWNERDATA结合起来使用将极大的增强ListView的功能,特别是当需要用列表的方式显示数以千计或更多的记录时。
LVS_OWNERDATA的目的在于ListView本身不存储数据,它只存储数据的行列信息,具体的行列数据,通过响应LVN_GETDISPINFO通知来实时生成,以下是一个例子
void CSampleListCtrl::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
{
 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
 
 if (pDispInfo->item.mask & LVIF_TEXT)
 {
  if (pDispInfo->item.iSubItem == 0)
   wnsprintf(pDispInfo->item.pszText, pDispInfo->item.cchTextMax,
    _T("%04d"), pDispInfo->item.iItem);
  else
   wnsprintf(pDispInfo->item.pszText, pDispInfo->item.cchTextMax,
    _T("[%04d, %04d]"), pDispInfo->item.iItem, pDispInfo->item.iSubItem);
 }
 *pResult = 0;
}
pDispInfo是ListView向程序员请求显示的参数,pDispInfo->item.pszText由程序员设置显示的内容,
pDispInfo->item.iItem,item.iSubItem 分别是行和列的位置信息。

如果ListView响应了NM_CUSTOMDRAW通知,那么它就可以控制自己的绘制行为,根据MSDN的描述,NM_CUSTOMDRAW响应的实现必须通过以下3步。
void CSampleListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
 LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
 *pResult = 0;
 
 if (pLVCD == NULL)
  return;
 // the step expain to see see ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/shellcc/platform/commctls/custdraw/custdraw.htm#CustomDraw_Prepaint
 // first step, we want custom draw
 if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
 {
  *pResult = CDRF_NOTIFYITEMDRAW;
  return;
 }
 // second setp, we want draw each sub item
 if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
 {
  *pResult = CDRF_NOTIFYSUBITEMDRAW;
  return;
 }
 // third step, we do our custom draw
 if (pLVCD->nmcd.dwDrawStage &  (CDDS_SUBITEM | CDDS_PREPAINT))
 {
  DoCustomDraw(pLVCD);
  *pResult = CDRF_NEWFONT;
 }
}
当ListView向程序员提问是否需要进行CUSTOMDRAW时,返回需要的回答,直到第三步。pLVCD->nmcd.dwDrawStage表示ListView的询问,而*pResult则包含程序员的回答。另外,在第三步,(pLVCD->nmcd.dwDrawStage &  (CDDS_SUBITEM | CDDS_PREPAINT)时,按照MSDN的描述应该是==,但实际上应该是 &。

DoCustomDraw 是我们自己的函数,它具体的控制如何绘制,在例子中,它用交替的背景进行绘制,并且每隔3列绘制成红色,将选中的列背景绘制为淡蓝色
void CSampleListCtrl::DoCustomDraw(LPNMLVCUSTOMDRAW pLVCD)
{
 
 if (pLVCD->nmcd.dwItemSpec % 2 == 0)
  pLVCD->clrTextBk = RGB(0xFF, 0x00, 0x00);
 else
  pLVCD->clrTextBk = RGB(0xEF, 0xF3, 0xEB);
 if (pLVCD->iSubItem == m_nSelectColumn)
  pLVCD->clrTextBk = RGB(0xA6, 0xCA, 0xF0);

 if (pLVCD->iSubItem % 3 == 0)
  pLVCD->clrText = RGB(0xFF, 0x00, 0x00);
 else
  pLVCD->clrText = RGB(0x00, 0x00, 0x00);
}
截图

<think>嗯,用户这次询问的是Windows列表控件中两个扩展样式LVS_EX_FULLROWSELECTLVS_EX_GRIDLINES的具体用法示例。这明显是个Windows编程问题,用户可能是位正在开发带列表视图界面的程序员。之前系统提示过要参考站内引用,但这次提问是全新的技术问题,没有历史对话可延续。用户特别强调要"尽可能参考用户上一次的问题",但查记录发现这其实是对话开端,说明用户可能误以为我们有过类似讨论。不过没关系,正好可以完整解答。从专业角度看,这两个扩展样式都是ListView控件常用的增强功能:网格线样式能让报表视图看起来像表格,整行选择则提升用户体验。用户要示例代码,说明ta需要立即可用的解决方案而非概念解释。考虑到Windows编程的复杂性,应该给出C++C#两种主流语言的实现,毕竟开发者使用的语言可能不同。注意到用户没有指定具体编程语言,所以示例需要涵盖基础API调用.NET封装两种方式。特别要强调SetExtendedStyle的使用技巧——很多新手会忽略必须用位或操作合并样式这个细节。另外在C#示例中加入说明避免手动处理WM_NOTIFY消息这点很重要,因为.NET框架已做了封装。代码中每个设置步骤都加了注释,这样用户即使不熟悉Win32API也能理解。额外补充"常用场景""注意事项"是因为:实际开发中常遇到网格线不生效的问题(通常是漏设LVS_REPORT样式),还有整行选择在高对比度模式下的兼容性问题。这些坑应该提前告知。最后生成的相关问题都围绕ListView样式扩展:为什么有时设置失效(常见困惑)、如何动态修改(进阶需求)、替代整行选择的方法(覆盖边缘场景)以及网格样式优化(视觉定制需求)。这些问题能帮助用户深入探索该控件的其他功能。</think> 好的,关于 Windows ListView 控件扩展样式 `LVS_EX_FULLROWSELECT` `LVS_EX_GRIDLINES` 的使用方法,以下是详细的解释代码示例: **核心概念:** 1. **`LVS_EX_FULLROWSELECT`:** * 作用:启用后,当用户点击 ListView 项(行)的**任意位置**(包括项目文本、子项文本或行中空白区域),都会选中**整个行**。这与默认行为(仅点击第一列选中整行)不同。 * 效果:提供更直观、更类似于现代表格的行选择体验。用户不需要精确点击第一列。 2. **`LVS_EX_GRIDLINES`:** * 作用:在 ListView 的**报表视图**(`LVS_REPORT`)模式下,在**项(行)子项(列)之间绘制水平垂直的网格线**。 * 效果:使数据看起来更清晰、更有结构感,类似 Excel 表格的外观。 * **重要前提**:此样式**仅在** ListView 控件设置为报表视图 (`LVS_REPORT`) 时生效。其他视图模式(图标、列表、平铺)不会显示网格线。 **设置方法:** 在 Windows 编程中,ListView 控件的扩展样式通过 `ListView_SetExtendedListViewStyle` 宏或发送 `LVM_SETEXTENDEDLISTVIEWSTYLE` 消息来设置。设置扩展样式时,**通常会保留现有的扩展样式**,这通过使用 `|`(按位或)运算符将它们与新样式结合在一起来实现。 **代码示例:** 以下是使用 C++ (Win32 API) C# (.NET WinForms) 设置这两种扩展样式的示例: **1. Win32 API (C++) 示例:** ```cpp #include <windows.h> #include <commctrl.h> // 包含 ListView 控件相关常量的头文件 // ... 假设 hwndListView 是你的 ListView 控件的窗口句柄 ... // 获取当前的扩展样式 (可选,但建议保留) DWORD dwExStyle = ListView_GetExtendedListViewStyle(hwndListView); // 添加 FULLROWSELECT GRIDLINES 样式,并保留可能已存在的其他样式 dwExStyle |= (LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); // 设置新的扩展样式组合 ListView_SetExtendedListViewStyle(hwndListView, dwExStyle); // 或者使用 SendMessage 实现相同的功能: // SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (LPARAM)(dwExStyle | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES)); ``` **2. C# (.NET WinForms) 示例:** 在 WinForms 中,可以直接通过 ListView 控件的属性设置。 ```csharp // 假设 listView1 是你的 ListView 控件实例 listView1.View = View.Details; // 必须设置为 Details (报表视图) GRIDLINES 才有效果 listView1.FullRowSelect = true; // 对应 LVS_EX_FULLROWSELECT listView1.GridLines = true; // 对应 LVS_EX_GRIDLINES ``` * `View = View.Details`:这是启用网格线 (`GridLines`) 的**必要条件**。 * `FullRowSelect = true`:启用整行选择。 * `GridLines = true`:启用网格线(因为 View 已经是 Details)。 **常见场景:** 这两种样式**经常一起使用**,特别是在需要显示结构化数据表格(如数据库查询结果、文件列表带详细信息)的应用程序中: * `LVS_EX_GRIDLINES` 提供清晰的视觉分隔,方便用户阅读不同行列的数据。 * `LVS_EX_FULLROWSELECT` 提供一致的行选择体验,用户点击行内任意位置都能选中整行,避免误操作或选中不完整。 **注意事项:** 1. **`LVS_EX_GRIDLINES` 仅限报表视图:** 确保 ListView 的视图模式设置为 `LVS_REPORT` (`View.Details`) ,否则 `LVS_EX_GRIDLINES` 不会有任何视觉效果。 2. **样式组合:** 使用 `|`(按位或)将新样式与现有扩展样式合并,避免覆盖掉其他已设置的有用样式(如 `LVS_EX_DOUBLEBUFFER` 用于减少闪烁)。 3. **初始化时机:** 通常在创建 ListView 控件后、添加项之前设置这些扩展样式。也可以在程序运行时根据需要动态更改(例如通过菜单或设置对话框)。 4. **高对比度模式:** 在系统使用高对比度主题时,ListView 可能不再绘制自带的网格线或行选择高亮,而是遵循系统高对比度绘制规则。如果需要在所有主题下保证视觉效果,可能需要自定义绘制(Owner Draw)。 5. **系统主题支持:** `LVS_EX_GRIDLINES` 绘制的网格线颜色通常由当前系统主题决定。在启用了视觉样式的应用程序中,线条颜色可能会更匹配系统主题。 **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值