您的位置:首页 > 其它

CGridListCtrlEx - 基于CListCtrl的网格控制

2017-06-19 08:43 211 查看
 翻译来源:https://www.codeproject.com/Articles/29064/CGridListCtrlEx-Grid-Control-Based-on-CListCtrl
作者:Rolf
Kristensen
目前找到最好的list控件类,还一直在更新
自定义绘制CListCtrl,具有子项目编辑和格式化

下载演示
- 1.6 MB 

下载源代码
- 180 KB

介绍 

Microsoft 
CListCtrl
支持使用报告样式在网格中显示数据,但是我们必须进行几项更改才能实现以下功能: 

排序

单元导航和键盘搜索

提示

隐藏并显示列

单元格编辑

超链接

自定义行和单元格着色

分组

剪贴板(仅复制)

保持列宽,位置和可见性

OLE拖放(包括项目的重新排序)
本文演示如何使用
CGridListCtrlEx
,它实现了所有上述功能,同时维护Windows
XP / Vista外观。



在GitHub的-
CGridListCtrlEx可如果想的Git / Subversion的接入使用,并且也有Doxygen文档

背景

有很多高级网格控件可以扩展
CListCtrl
,其中之一是增强列表控件(CGfxListCtrl)。这个美妙的控件提供了上述所有功能,但无法处理Windows
XP和Vista。找到一个很好的替代这个控制不是很容易:

MFC
Grid Control - 不会继承,
CListCtrl
所以它不受它的限制,但它不会受益于Microsoft添加到的任何改进
CListCtrl


终极网格 -
像MFC网格控件,它不会继承
CListCtrl
。最初一个人买的,但现在可以自由使用。

CQuickList -
非常接近于一个完美的替代品,但很难添加新的方式来显示数据,这要求
LVS_OWNERDATA
使排序更难。

XListCtrl -
也是一个非常完整
CListCtrl
,但很难添加新的方式来显示数据,并且它无法支持
LVS_OWNERDATA
。现在必须购买许可证才能获得最新版本。

另一个报表列表控制 -
简单易用,但缺少其他方式来编辑除了使用之外的数据,也缺少
CEdit
子导航。

CListCtrlEx -
实现了很多功能,并且有很好的记录。最初需要
LVS_OWNERDRAWFIXED
,现在演变成使用自定义画。自定义绘制和所有者绘制的组合导致代码有点复杂,它也不支持
LVS_OWNERDATA

所述
CGridListCtrlEx
插入件称为柱性状一个抽象层处理该细胞绘制和编辑。如果微软
CListCtrl
再次扩展,那么希望的核心
CGridListCtrlEx
将继续发挥作用。

如何使用CGridListCtrlEx

CGridListCtrlEx
试图留真实的
CListCtrl
,并且不试图取代的东西
CListCtrl
已经提供。这意味着我们可以替换一个
CListCtrl
CGridListCtrlEx
而不需要再做任何事情。

建议我们不
CGridListCtrlEx
直接使用,而是创建一个继承/派生的新类
CGridListCtrlEx
。这将使以后更容易将任何更新迁移到
CGridListCtrlEx
课程中。

编辑单元格/子项目

默认情况下,当插入列时
CGridListCtrlEx
,它们将被配置为只读,无需编辑。通过使用
CGridListCtrlEx::InsertColumnTrait()
,我们可以提供一个
CGridColumnTrait
类,它指定应该使用哪种类型的编辑器。

隐藏   复制代码
CGridColumnTrait* pTrait = new CGridColumnTraitEdit;
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);

当编辑了一个项目时,
LVN_ENDLABELEDIT
会发送一个标准的消息
CListCtrl
。当
CGridListCtrlEx
接收到该消息时,它将自动调用虚拟方法
CGridListCtrlEx::OnEditComplete()
,允许派生类验证输入,并可能更新底层数据模型。

使用组合框编辑单元格/子项

通过使用
CGridListCtrlEx::InsertColumnTrait()
,我们也可以提供一个
CGridColumnTrait
作为一个工作的类
CComboBox


隐藏   复制代码
CGridColumnTraitCombo* pTrait = new CGridColumnTraitCombo;
pTrait->AddItem(0, "Hello");
pTrait->AddItem(1, "Goodbye");
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);

我们可以指定
CComboBox
插入列时的项目(如上所示)。如果我们要动态提供
CComboBox
项目,那么我们可以覆盖
CGridListCtrlEx::OnEditBegin()
。使用
dynamic_cast<>
要么调用列特征的方法
CGridColumnTraitCombo::LoadList()
,或者对返回的工作
CComboBox
-editor直接。  

如果要获取所选项目的
CComboBox
itemdata,则可以覆盖
CGridListCtrlEx::OnEditComplete()
并检查参数值
pLVDI->item.lParam
。需要将itemdata保存在其他位置,因为它不能存储在本地数据模型中
CListCtrl


排序行

默认情况下,
GridListCtrlEx
将为所有列启用排序,其中它将执行简单的文本比较。通过列特征可以通过覆盖来实现自定义排序
CGridColumnTrait::OnSortRows()


使用数字比较配置列特征进行排序:

隐藏   复制代码
CGridColumnTraitEdit* pTrait = new CGridColumnTraitEdit;
pTrait->SetSortFormatNumber(true);	// Numeric column
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);

列特征
CGridColumnTraitDateTime
将自动尝试使用日期比较进行排序。

我们还可以选择覆盖该
CGridListCtrlEx::SortColumn()
方法。那么这只是一个选择正确的排序方法的问题。另请参见CListCtrl和排序行

显示工具提示

默认情况下,
CGridListCtrlEx
将仅将单元格内容显示为工具提示。如果我们想在工具提示中显示不同的东西,那么我们可以覆盖该
CGridListCtrlEx::OnDisplayCellTooltip()
方法。

格式化单元格/子项

如果要更改前景/背景颜色或字体样式(粗体,斜体,下划线),则可以覆盖方法
CGridListCtrlEx::OnDisplayCellColor()
CGridListCtrlEx::OnDisplayCellFont()


隐藏   复制代码
bool MyGridCtrl::OnDisplayCellColor(int nRow, int nCol, COLORREF& textColor, COLORREF& backColor)
{
if (nRow == 3 && nCol == 3)
{
textColor = RGB(0,255,0);
backColor = RGB(0,0,255);
return true;  // I want to override the color of this cell
}
return false;  // Use default color
}

显示单元格/子图像

CGridListCtrlEx
启用扩展样式
LVS_EX_SUBITEMIMAGES
默认,但一个仍然需要附加
CImageList
使用
CListCtrl::SetImageList()


连接图像后,可以在单元格/子项目中绑定索引
CImageList
。这可以通过
CGridListCtrlEx::SetCellImage()
,或者如果使用
I_IMAGECALLBACK
然后通过覆盖返回图像索引
CGridListCtrlEx::OnDisplayCellImage()


CGridListCtrlEx
也使得扩展样式
LVS_EX_GRIDLINES
默认情况下,这可能会导致子项目图像重叠网格边界。这可以通过确保图像仅使用16像素中的15个(第一像素透明)来解决。

当使用子项映像并在Windows XP上运行应用程序或使用经典样式时,当选择行时,它将显示白色背景。这可以通过使用
CGridRowTraitXP


隐藏   复制代码
m_ListCtrl.SetDefaultRowTrait(new CGridRowTraitXP);

复选框支持

CListCtrl
支持开箱即用的标签列的复选框。只适用扩展风格
LVS_EX_CHECKBOXES


隐藏   复制代码
m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_CHECKBOXES);

记住不要使用
InsertHiddenLabelColumn()
,因为它会隐藏标签列及其复选框。可以使用
GetCheck()
SetCheck()
检索/修改复选框值。

如果要对多个列进行复选框,则可以使用
CGridColumnTraitImage
(及其专业化):

隐藏   复制代码
// Appends the unchecked/checked state images to the list control image list
int nStateImageIdx = CGridColumnTraitImage::AppendStateImages(m_ListCtrl, m_ImageList);
m_ListCtrl.SetImageList(&m_ImageList, LVSIL_SMALL);
// Creates an image column, that can switch between the 2 images
CGridColumnTrait* pTrait = new CGridColumnTraitImage(nStateIdx, 2);
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 20, nCol, pTrait);
for(int i=0; i < m_ListCtrl.GetItemCount(); ++i)
m_ListCtrl.SetCellImage(i, nCol, nStateImageIdx);	// Uncheck item

分配
CImageList
使用时,标签列将自动显示图像列
SetImageList()
。不可能禁用此行为,但可以隐藏标签列
InsertHiddenLabelColumn()


CGridColumnTraitImage
使用单元格图像绘制复选框,因此不可能在同一列中同时具有单元格图像和复选框。要获取并设置选中/未选中的状态,可以使用
GetCellImage()
SetCellImage()


CGridColumnTraitImage
支持根据是否启用复选框进行排序。使用
CGridColumnTraitImage::SetSortImageIndex()
启用它。

CGridColumnTraitImage
还支持切换所有选定行的复选框。使用
CGridColumnTraitImage::SetToggleSelection()
启用它。

超链接支持

超链接列可以将单元格内容显示为链接,可以点击链接,并启动外部应用程序,如默认Web浏览器(http)或电子邮件客户端(mailto)。

CGridColumnTraitHyperLink
允许一个提供用于CELLTEXT,它允许一个添加额外的协议的细节没有它正在显示的前缀(和后缀):

隐藏   复制代码
CGridColumnTraitHyperLink* pHyperLinkTrait = new CGridColumnTraitHyperLink;
pHyperLinkTrait->SetShellFilePrefix(_T("http://en.wikipedia.org/wiki/UEFA_Euro_"));
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pHyperLinkTrait);

也可以
CGridColumnTraitHyperLink::SetShellApplication()
指定要启动的自定义应用程序,而不仅仅是基于协议前缀启动的默认应用程序。

超链接也可用于模拟按钮。当点击它会发送一个
LVN_ENDLABELEDIT
通知,哪一个可以视为按钮点击通知在父视图中。

更改行高

CGridListCtrlEx
用途customdraw,所以只有这些可用的解决方案:

分配
CImageList
图像具有该行所需高度的位置。

更改网格控件的字体,行高将跟随。
CGridListCtrlEx::SetCellMargin()
使用这个技巧来增加网格控件的字体,同时保持行字体不变。

更改空标记文本

CGridListCtrlEx
不包含任何项目时,它将显示标记文本以指示列表为空。

使用
CGridListCtrlEx::SetEmptyMarkupText()
更改此标记文本。如果提供空文本,那么它的行为就像一个正常的
CListCtrl


如果使用
CGridListCtrlGroups
,它会反应
LVN_GETEMPTYMARKUP
如果在Windows
Vista上运行。

加载并保存列宽和位置

CViewConfigSectionWinApp
提供存储宽度,位置以及是否显示列的能力。在已经加入所有可用列的
CGridListCtrlEx
,分配的一个实例
CViewConfigSectionWinApp
使用
CGridListCtrlEx::SetupColumnConfig()
,它会通过恢复上次保存的列配置
CWinApp


隐藏   复制代码
m_ListCtrl.SetupColumnConfig(new CViewConfigSectionWinApp("MyList"));

如果在应用程序中使用
CGridListCtrlEx
了几个地方,那么应该确保
CViewConfigSectionWinApp
为每个地方创建一个独特的地方。

OLE拖放

CGridListCtrlEx
都可以作为OLE拖动源和OLE放置目标。这允许在
CGridListCtrlEx
其他窗口和应用程序之间进行拖动操作。

CGridListCtrlEx
在执行内部拖放操作时,支持重新排列行。这是使用特殊的排序操作实现的,因此项目不会被删除/插入。

要实现自己的特殊行为来执行放置操作,可以覆盖
OnDropSelf()
OnDropExternal()
根据您想要处理的情况。

要控制在拖动启动时放置在拖动源中的内容,请覆盖
OnDisplayToDragDrop()


CGridColumnTrait如何工作?

CGridListCtrlEx
试图避免所有关于如何显示和编辑数据的令人讨厌的细节。这些事情是由
CGridColumnTrait
类来处理的,如果我们要修改数据的显示方式,那么“只是”创建一个新
CGridColumnTrait
类就是一个问题。

插入列时,我们可以为列分配一个
CGridColumnTrait
。该
CGridListCtrlEx
会激活相应的
CGridColumnTrait
,当我们需要绘制在列的单元格,或修改此列的单元格。

CGridColumnTrait
包括被称为元数据的一些特殊成员。这些成员可以由您自己的类使用,当它派生时
CGridListCtrlEx
,我们可以轻松地向列添加额外的属性。

当继承时
CGridColumnTrait
,我们必须考虑以下几点:

如果执行自定义绘图,我们还必须处理选择和聚焦着色。

如果执行编辑,我们必须确保编辑器在失去焦点时关闭,并
LVN_ENDLABELEDIT
在编辑完成时发送消息。

CGridRowTrait如何工作?

它基于相同的想法,
CGridColumnTrait
但在行级别而不是列级别运行。这对于必须修改所有列的显示行为的情况非常有用。

使用代码

源代码包括以下类:

CGridListCtrlEx
 -
专业化 
CListCtrl


CGridListCtrlGroups
CGridListCtrlEx
扩展支持分组

CGridColumnTrait
 -
指定列特征的接口

CGridColumnTraitText
 -
实现单元格格式化

CGridColumnTraitImage
 -
通过切换图像来实现细胞编辑(可以模拟复选框)

CGridColumnTraitEdit
 -
实现细胞编辑 
CEdit


CGridColumnTraitCombo
 -
实现细胞编辑 
CComboBox


CGridColumnTraitDateTime
 -
实现细胞编辑 
CDateTimeCtrl


CGridColumnTraitHyperLink 
-
实现细胞行为超链接

CGridRowTrait
 -
指定行特征的界面

CGridRowTraitText
 -
实现行格式化

CGridRowTraitXP
 -
使用经典或XP风格时,执行子图像背景图

CViewConfigSection
 -
持久化列设计的抽象界面

CViewConfigSectionWinApp
 -
实现接口,并可以在多个列设置之间切换。

要做的事

CGridListCtrlEx
尝试执行任何图纸本身望而却步。这意味着以下功能/错误将不会受到很多关注:

支持进度条 - 需要一个
CGridColumnTrait
绘制整个单元格的类。
实现
CGridColumnTrait
绘制整个单元格的类可能可以通过从ListCtrl中窃取/借用一些代码来完成
- 具有Windows Vista样式项目选择的WTL列表控件。

对本项目的贡献非常受欢迎。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: