对于GIS软件来说,属性数据的显示是一个比较重要的问题,在基于VC++的GIS底层开发中,一般用ClistCtrl或者DBGrid来显示属性数据。然而这些控件显示效率比较低或者效率高的话比较难操作。通过最近的探索,找到了一个比较好的网格控件来显示属性数据。
CGridCtrl
这个控件是一个源代码公开的控件,你可以自己修改源代码。
控件的特点:
●使用鼠标可以进行单元格的选择,还可以辅助ctrl和shift的组合键进行选
择。也可以取消选择。
● 行和列可以按照大小进行重排,还可以取消对行、列或两者的排序。
● 双击区分点,行或者列可以按照大小自动排序
● 可以对任何列或行固定
● 单元格可以有不同文本和背景颜色的个性化设置
● 单元格可以有字体的个性化设置
● 单元格可以标注"只读"或者其他的状态设置及检测
● OLE的拖放动作
● Ctrl-C, Ctrl-X和Ctrl-V执行拷贝、剪切、粘贴操作,Ctrl-A全选
● 当单元格成为焦点,并且在单元格的编辑区域按下字符键,就意味着在
那个单元格进行编辑了
● 支持微软的智能鼠标
● 可以在单元格中加入图片
● 对大型数据可以使用"虚拟"模式
● 充分的打印支持,支持文档/浏览环境(包括打印预览)或是基于会话的应用(不支持打印预览)
● 可选的"列表模式",包括对行的全选或单选,还有单击列标题提示进行插入的操作。
● 众多的虚函数可以很容易对控件进行功能扩充
● 支持UNICODE
● 支持WinCE
● 单元格的标题提示太小不能显示数据
● 可以隐藏行和列
● 在VC4.2、5.0、6.0和CE工具箱2.0、3.0下编译通过示例中示范了grid控件中大部分特征文档
如果想在你的工程中使用这个Grid控件的话,你还得在你的工程中添加一些文件:
gridctrl.cpp, gridctrl.h Grid控件资源文件和头文件
gridcellbase.cpp, gridcellbase.h 单元格的基础类
gridcell.cpp, gridcell.h 单元格的默认执行文件
CellRange.h CcellID和CcellRange类的定义
MemDC.h Keith Rule's的直接存储类
InPlaceEdit.cpp, InPlaceEdit.h 定位编辑窗口的源文件和头文件
GridDropTarget.cpp, GridDropTarget.h Grid容器的drag和drop对象 只有在gridctrl.h中没有定义 GRIDCONTROL_NO_DRAGDROP的时候才有必要使用。
Titletip.cpp, Titletip.h 从Zafir Anjum那里的到的单元格标题提示. 只有在gridctrl.h中没有定义 GRIDCONTROL_NO_TITLETIPS 的时候才有必要使用结构
这个Grid是基于一种框架(CgridCtrl工程),这种框架组织和控制那些容纳数据、执行某些操作如画图、句柄方法如按钮的点击事件的单元格的动作。 Grid工程本身的句柄事件如点击是在单元格之前响应,如果它认为有必要的话,它还会发送某种鼠标信息。它还包含一个拖曳对象(CGridDropTarget)和一个标题提示对象(CTitleTip),前者处理拖曳操作,后者在单元格物理空间在最大限度内不足以显示其内容时可以显示出其内容。Grid单元格可以是任何类型,其长度与源自CgridBaseCell的类的长度一样。包含这个包的是一个CgridCell类,它能处理基本的数据存储和编辑操作。扩充的两个类CgridCellCombo和CGridURLCell示范了如何创建自己的单元格类。
第一步:加载控件。
在对话框中加入一个自定义控件,将类设置为MFCGridCtrl
第二步:初始化控件
int nfieldCnt = m_pLayer->GetLayerDefn()->GetFieldCount(); //列数
m_ctrlAttrTable.SetVirtualMode(TRUE);
m_ctrlAttrTable.SetEditable(FALSE);
m_ctrlAttrTable.AutoSizeColumns(GVS_DATA);
m_ctrlAttrTable.SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黄色背景
m_ctrlAttrTable.SetRowCount(m_pLayer->GetFeatureCount()+1); //初始为10行
m_ctrlAttrTable.SetColumnCount(nfieldCnt+2); //初始化为字段数+2列
m_ctrlAttrTable.SetFixedRowCount(1); //表头为一行
//m_ctrlAttrTable.SetFixedColumnCount(1); //表头为一列
m_ctrlAttrTable.SetListMode();
m_ctrlAttrTable.SetRowResize(1);
m_ctrlAttrTable.SetColumnResize(1);
从上面的代码可以看出,此控件支持虚拟模式,我们就是要用到这个特性显示大量的属性数据的。此函数m_ctrlAttrTable.SetVirtualMode(TRUE);
int nfieldCnt = m_pLayer->GetLayerDefn()->GetFieldCount(); //列数
m_ctrlAttrTable.SetVirtualMode(TRUE);
m_ctrlAttrTable.SetEditable(FALSE);
m_ctrlAttrTable.AutoSizeColumns(GVS_DATA);
m_ctrlAttrTable.SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黄色背景
m_ctrlAttrTable.SetRowCount(m_pLayer->GetFeatureCount()+1); //初始为10行
m_ctrlAttrTable.SetColumnCount(nfieldCnt+2); //初始化为字段数+2列
m_ctrlAttrTable.SetFixedRowCount(1); //表头为一行
//m_ctrlAttrTable.SetFixedColumnCount(1); //表头为一列
m_ctrlAttrTable.SetListMode();
m_ctrlAttrTable.SetRowResize(1);
m_ctrlAttrTable.SetColumnResize(1);
第三步:重载父窗口的OnNotify消息响应函数
BOOL CAttrTableDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
if (wParam == (WPARAM)m_ctrlAttrTable.GetDlgCtrlID())
{
*pResult = 1;
GV_DISPINFO *pDispInfo = (GV_DISPINFO*)lParam;
if (GVN_GETDISPINFO == pDispInfo->hdr.code)
{
SetGridItem(pDispInfo);
return TRUE;
}
else if (GVN_ODCACHEHINT == pDispInfo->hdr.code)
{
GV_CACHEHINT *pCacheHint = (GV_CACHEHINT*)pDispInfo;
TRACE(_T("Cache hint received for cell range %d,%d - %d,%d\n"),
pCacheHint->range.GetMinRow(),
pCacheHint->range.GetMinCol(),
pCacheHint->range.GetMaxRow(),
pCacheHint->range.GetMaxCol());
}
}
return CDialog::OnNotify(wParam, lParam, pResult);
}
上面的SetGridItem(pDispInfo);这个函数就是自己定义的如何显示数据的函数了,其中pDispInfo是指向结构体
typedef struct tagGV_DISPINFO {
NMHDR hdr;
GV_ITEM item;
} GV_DISPINFO;
的指针,它包括了单元格的一些基本信息。
第四步:显示数据
int CAttrTableDlg::SetGridItem(GV_DISPINFO *pDispInfo)
{
//设置表格显示属性
int row = pDispInfo->item.row;
int col = pDispInfo->item.col;
if (row < 1) //设置0行表头显示
{
pDispInfo->item.nFormat = DT_CENTER|DT_WORDBREAK;
if (col >= 2) //本身的字段名字
{
pDispInfo->item.strText.Format(CString(m_pLayer->GetLayerDefn()->
GetFieldDefn(col-2)->GetNameRef()));
}
else if (col == 0)
{
pDispInfo->item.strText = _T("FID");
}
else if (col == 1)
{
pDispInfo->item.strText = _T("Shape *");
}
}
else
{
pDispInfo->item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
if (col >= 2) //字段本身的值
{
pDispInfo->item.strText.Format(CString(m_pLayer->
GetFeature(row-1)->GetFieldAsString(col-2)));
}
else if (col == 0)
{
pDispInfo->item.strText.Format(_T("%d"),m_pLayer->GetFeature(row-1)->GetFID());
}
else if (col == 1)
{
pDispInfo->item.strText =
CString(m_pLayer->GetFeature(row-1)->GetGeometryRef()->getGeometryName());
}
}
return 0;
}
最后显示的效果如下所示
总共7603条记录很快就显示出来了。我想这个的内部原理就是用到了高速缓存,实际上控件本身没有存储数据。希望对GIS开发或者数据库开发者有用。有什么意见大家可以一起交流。谢谢