CListCtrl 和分组行






4.92/5 (34投票s)
启用 MFC 列表控件中类别的示例
引言
Microsoft 的 CListCtrl
支持在网格中显示数据,但也支持数据分组。本文将演示如何激活 CListCtrl
的分组功能。
该演示应用程序允许您体验如何使用分组。只需右键单击列标题,即可根据该标题对数据进行分组。

背景
Microsoft 通过发布 Windows XP 扩展了 CListCtrl
的分组支持。这项新功能并没有得到太多的推广,并且在功能上也受到限制(无法折叠组)。后来,Windows Vista 发布时,该实现得到了进一步扩展,增加了更多功能(折叠、页脚、副标题等)。
已经有一些 .NET 文章描述了如何在 Windows XP 中使用分组
还有 MSDN - Windows Vista 控制增强,其中描述了 Windows Vista 中的一些新内容。
我第一次真正看到一个有用的应用程序使用这种分组功能是在 TortoiseSVN 1.5 中。
如何在 CListCtrl 中激活分组
在激活分组之前,必须满足一些条件
- 操作系统必须支持 Common Controls ver. 6(Windows XP/Vista 及更高版本)
- 应用程序必须通过其清单启用 Common Controls ver. 6
- 应用程序必须使用至少设置为
0x0501
的_WIN32_WINNT
进行编译
当满足上述要求时,我们可以像这样创建一个组
LRESULT CListCtrl_Category_Groups::CreateSingleGroup(int nIndex,
int nGroupId, const CString& strHeader)
{
EnableGroupView( TRUE );
LVGROUP lg = {0};
lg.cbSize = sizeof(lg);
lg.iGroupId = nGroupId;
lg.state = LVGS_NORMAL;
lg.mask = LVGF_GROUPID | LVGF_HEADER | LVGF_STATE | LVGF_ALIGN;
lg.uAlign = LVGA_HEADER_LEFT;
// Header-title must be unicode (Convert if necessary)
lg.pszHeader = strHeader.GetBuffer();
lg.cchHeader = strHeader.GetLength();
nGroupId = InsertGroup(nIndex, &lg );
if (nGroupId==-1)
return nGroupId;
// Insert all current items into this group
for(int nRow = 0; nRow < GetItemCount(); ++nRow)
{
LVITEM lvItem = {0};
lvItem.mask = LVIF_GROUPID;
lvItem.iItem = nRow;
lvItem.iSubItem = 0;
lvItem.iGroupId = nGroupID;
SetItem( &lvItem );
}
}
上面的示例代码创建一个具有以下属性的新组
- 该组将被插入到
CListCtrl
内部组列表中nIndex
的位置。 - 该组将获得外部标识符
nGroupId
。 - 该组标题将变为文本字符串
strHeader
。
Windows XP 中的限制
在 Windows XP 上使用组时,我们会发现以下内容缺失
- 无法询问
CListCtrl
内部组列表中有多少个组。建议我们自己跟踪创建的组。 - 无法遍历
CListCtrl
内部组列表中的组。建议我们自己跟踪创建的组。 - 无法询问
CListCtrl
鼠标光标当前是否位于组上方。相反,我们必须进行大量猜测。 - 无法更改组的状态,使其变为折叠状态。
这些限制已通过 Windows Vista 解决,同时还添加了以下功能
- 可以将任务链接附加到组
- 可以提供位于标题文本正下方的副标题文本
- 可以为组提供页脚文本
使用源代码
提供的源代码演示了如何激活不同的分组功能
InsertGroupHeader()
- 创建一个新组。CListCtrl::InsertGroup()
的包装器。SetRowGroupId()
- 将行添加到现有组。带有LVIF_GROUPID
的CListCtrl::SetItem()
的包装器。GroupByColumn(int nCol)
- 为指定列中每个唯一的单元格文本创建一个新组。SortColumn(int nCol)
- 对从指定列生成的组进行排序。CListCtrl::SortGroups()
的包装器。GroupHitTest(const CPoint& point)
- 尝试查找给定鼠标点下方的组。CheckEntireGroup()
- 启用CListCtrl
扩展样式LVS_EX_CHECKBOXES
后,此方法将更改整个组的复选框状态。
它还演示了如何使用一些新的 Windows Vista 功能
CollapseAllGroups()
- 循环访问所有组并使它们折叠。带有LVGS_STATE
的CListCtrl::SetGroupInfo()
的包装器。SetGroupTask()
- 更改组的任务链接。当用户单击任务链接时,它将生成一个LVN_LINKCLICK
消息。SetGroupSubtitle()
- 更改组的副标题。SetGroupFooter()
- 更改组的页脚。
历史
- 2008-09-16 - 文章的首次发布
- 2009-09-28 - 从
CGridListCtrlEx
反向移植错误修复,并且在启动时,它现在按第 3rd 列对行进行分组 - 2011-07-27 - 修复了在 Vista\Win7 上使用并使用 _WIN32_WINNT >= 0x0600 编译时
GroupHitTest()
中的错误