热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CGridCtrl控件(原文来自vc知识库,转载请标明出处)

一、引言在用vc开发关于数据库的项目时,通常我们只好用微软的DBGRID作为数据库表格控件,其实微软的DBGRID并不好用,想找一份好的

一、引言

在用vc开发关于数据库的项目时,通常我们只好用微软的DBGRID作为数据库表格控件,其实微软的DBGRID并不好用,想找一份好的帮助文档都找不到,并且界面并不友好,比起C++Builder中的DBGRID来说是逊色不少,但是DBGRID在开发数据库的项目中又是常用的控件,所以就一直想找一个好用的DBGRID,可是网上又没有找到。上次在无意中看到了CGridCtrl(一个很漂亮的表格控件,如果你还没有用过,可以到http://www.codetools.com/下载,上面还有详细的使用说明)支持虚模式,在这种模式下,即使你向这个表格插入一百万条数据,它并不会真的生成一百万行,而是随着你的滚动条的滚动,计算出在屏幕上要显示的行和列,然后会向你提供一个接口,通过这个接口,你可以在这儿设置你要显示的数据。这给了我一些启示,我决定用它来做一个DBGRID。下面的例子是它的一个应用。

 

二、原理

DBGRID和一般的GRID的不同之处在于,一般的GRID并不适合显示大的数据量,如果你的一个查询结果有上万条记录的话,如果你都要插入到GRID中,这将是一个很慢的过程,并且你在GRID中移动滚动条的话,它的记录的滚动也是很慢的,而DBGRID并不会真正把这些记录的数据全部插入到控件中,当DBGRID的滚动条滚动时,它会根据DBGRID的显示面积的大小和查询得到的总的记录数计算出当前应该显示哪那些行,然后会把那几行的记录数据插入到表格中,这样速度当然是很快的,而且没有数据量多少的限制。

幸运的是,CGridCtrl类已经为我们提供了这种机制,它是采用虚模式的方式,要使用这种方式,按照以下的步骤就可以了:

步骤一 初始化


1.void SetVirtualMode(TRUE) 设为虚模式
2.BOOL SetRowCount(int nRows) 设置总的行数。
3.BOOL SetFixedRowCount(int nFixedRows = 1)设置固定的行数据
4.BOOL SetColumnCount(int nCols)  设置列数
5.BOOL SetFixedColumnCount(int nFixedCols = 1)设置固定的列数

步骤二 响应消息 显示数据

我们假设CGridCtrl是放在对话框上,而且它关联的变量是m_Grid,利用ClassWizard添加对话框的OnNotify响应函数。这个响应函数的写法是固定的,类似下面的代码:


01.BOOL CMyOdbcDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
02.{
03.if (wParam == (WPARAM)m_Grid.GetDlgCtrlID())
04.{
05.*pResult = 1;
06.GV_DISPINFO *pDispInfo = (GV_DISPINFO*)lParam;
07.if (GVN_GETDISPINFO  == pDispInfo->hdr.code)
08.{
09./*这是我们自己加的函数,在这个函数里我们设置当前要显示的数据*/
10.SetGridItem(pDispInfo);
11.return TRUE;
12.}
13.}
14.return CDialog::OnNotify(wParam, lParam, pResult);
15.}

在上面的代码中,SetGridItem(pDispInfo)是我们自己加的函数,在这个函数里我们设置当前要显示的数据,pDispInfo是一个GV_DISPINFO的结构体对象,在这个结构中包含了每个单元格的信息,如行号,列号,有没有位图,背景色,前景色等,CGRIDCTRL会在当前要显示那个单元格时,会把这个单元格的行号,列号传递给我们,我们只要在里面设置要显示的数据就可以了。如下面是一个显示数据的例子。


1.int CMyOdbcDlg::SetGridItem(GV_DISPINFO *pDispInfo)
2.{
3.pDispInfo->item.strText.Format("row%d,col%d",pDispInfo->item.row, pDispInfo->item.col);
4.return 0;
5.}

通过上面的介绍,我们应该已经会使用CGridCtrl虚模式,下面说明一下用CGridCtrl虚模式做DBGRID的原理,大家都知道,MFC的CRecordset类支持多种游标机制,如双向游标的,如果我们是用ClassWizard来生成一个查询的CRecordset的派生类的话,那么可以调用函数CRecordset::SetAbsolutePosition(),用这种方式方式来做DBGRID真是太简单了,因为在上面的int CMyOdbcDlg::SetGridItem(GV_DISPINFO *pDispInfo)函数中,我们已经知道要显示的是哪一行,哪一列的数据,所以只要通过CRecordset::SetAbsolutePosition(pDispInfo->item.row)函数,把游标定位到那一行,然后获取每个字段的数据就可以了。

但是使用上面的方法有一个不好的地方在于,我们必须用ClassWizard为每个查询从CRecordset派生出新类,这样做很不方便,在VC知识库第六期上面有一篇介绍“单独使用CRecordset”文章,可是上面的CRecordset打开方式只能使用CRecordset::forwardOnly,游标只能向前滚动,我们不能使用CRecordset::SetAbsolutePosition()函数,如果要想使用方便的话,我们必须想别的办法,来提供当前要显示的那个单元格的数据。

我们知道,oracle数据库并不支持双向游标,那么为什么我们用ClassWizard为查询从CRecordset派生的类能够使游标双向移动呢?我的猜想是这样的,CRecordset类其实把每次获取的数据都保存了下来,并且保存了每行记录所在的位置,这样,当我们调用CRecordset::SetAbsolutePosition()函数时,它就可以得到我们想要的数据了。不管这种猜想对不对,按照上面的思路已经实现一个很好用的DBGRID。我把封装成了类COdbcDBGRIDFILE,它实现的原理是首先得到所查询的行数,列数,然后设置CGridCtrl的属性,如把它设为虚模式,设定CGridCtrl的行数和列数等。然后使用内存映射文件来保存每条记录数据,同时用一个结构体来记录下这条记录所在的位置。

这里有一个小技巧,如果查询的结果有几十万条的话,如果我们一开始就把所有的这些记录都保存在内存映射文件中的话,那么时间要很长,所以根本不能满足应用,所以我们在开始时,只会获取记录集的一部分用于显示,当用户在CGRIDCTRL上拖动滚动条向下滚动时,它会随着用户的滚动不断的获取数据,这样显示上面就一点问题也没有。

三、使用实例

好了,给大家说了这么多,下面就看看如何使用这个封装好的类COdbcDBGRIDFILE 这个类对外的接口只有4个函数可以被调用,下面简单的说明一下这4个函数


1.COdbcDBGridFILE(CGridCtrl *pGrid = NULL,
2.CDatabase *pDatabase = NULL,
3.CString strSql = "",
4.CString strFilePath = ""); //构造函数,必须把所要的变量传递进去。
5.int InitGrid();//初始化函数
6.int SetGridText(GV_DISPINFO *pDispInfo);//被外部函数调用接口,用来返回每个单元格应该显示的
7.int Release();//释放资源

按照下面的步骤做一遍,你就能够知道它是不是很实用了。

步骤一

新建一个基于对话框的工程,命名为demo2,打开stdafx.h文件,加入#include,从例子中把OdbcDBGRIDFILE.h, OdbcDBGRIDFILE.cpp复制到这个工程的目录下,并且加入到工程中,方法是菜单project->add to project->files,选择这二个文件就可以了。不要忘了,你要把CGRIDCTRL类的文件都包含进来。

步骤二

在对话框上,按照上面的样例放上一个CURSTOMER CTROL(就是一个人头的那个控件),在属性的CLASS上输入MFCGridCtrl,ID为IDC_GRIDODBC,然后在放上其它编辑框和按钮控件,为四个编辑框控件通过CLASSWIZARD关联变量


1.CString m_strPass;//口令
2.CString m_strSource;//数据源名
3.CString m_strSql;//查询sql
4.CString m_strUser;//用户名

在类CDemo2Dlg中加入下面的几个成员变量


1.CGridCtrl m_Grid;
2.COdbcDBGRIDFILE *m_pMapFile;
3.CImageList m_ImageList;
4.CDatabase m_db;

当然你要在CDemo2Dlg的声明文件中加入


1.#include "gridctrl.h"
2.#include "OdbcDBGRIDFILE.h"

在CDemo2Dlg::DoDataExchange(CDataExchange* pDX)函数中加入DDX_Control(pDX, IDC_GRIDODBC, m_Grid);

在CDemo2Dlg的构造函数中加入


1.m_pMapFile = NULL;

步骤三

用ClassWizard生成CDemo2Dlg的CDemo2Dlg::OnNotify消息响应函数,在这个函数中,输入如下的代码


01.BOOL CDemo2Dlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
02.{
03.if (wParam == (WPARAM)m_Grid.GetDlgCtrlID())
04.{
05.*pResult = 1;
06.GV_DISPINFO *pDispInfo = (GV_DISPINFO*)lParam;
07.if (GVN_GETDISPINFO  == pDispInfo->hdr.code)
08.{
09.SetGridItem(pDispInfo);
10.return TRUE;
11.}
12.}
13.return CDialog::OnNotify(wParam, lParam, pResult);
14.}

在上面的代码中,SetGridItem(pDispInfo)是我们自己加的函数,在这个函数里我们设置当前要显示的数据

步骤四

为CDemo2Dlg加入查询按钮的响应函数OnBtnquery()

下面是这个函数的代码


01.void CDemo2Dlg::OnBtnquery()
02.{
03.this->Release();/*这个函数用于释放资源的*/
04.CString strConn;
05.UpdateData(TRUE);
06.strConn.Format("ODBC;DSN=%s;UID=%s;PWD=%s",m_strSource,m_strUser, m_strPass);
07.BOOL bResult = m_db.Open(strConn);
08.if(bResult == FALSE)
09.{
10.return;
11.}/*上面的代码用于连接数据库*/
12.m_pMapFile = new COdbcDBGRIDFILE(&m_Grid, &m_db, m_strSql,"c:\\csl.txt");
13.m_pMapFile->InitGrid();/*初始化函数,用于创建内存映射文件等*/
14.}

在上面的代码中,调用了COdbcDBGRIDFILE的构造函数,它的原型是COdbcDBGRIDFILE::COdbcDBGRIDFILE(CGridCtrl *pGrid , CDatabase *pDatabase, CString strSql, CString strFilePath),其中的参数的含义如下

      CGridCtrl *pGrid-------是一个指向CGridCtrl的指针

      CDatabase *pDatabase------是一个指向Cdatabase的指针,你必须把一个已经连接好的Cdatabase的对象传递进去。

      CString strSql------是一个查询语句,如select * from emp;

      CString strFilePath-----是一个用于生成内存映射文件的文件的路径

步骤五

为CDemo2Dlg加入成员函数SetGridItem(pDispInfo),这个函数是CDemo2Dlg::OnNotify()中调用的,我们用它来设置显示记录数据。


01.void CDemo2Dlg::SetGridItem(GV_DISPINFO *pDispInfo)
02.{
03./*
04.在这儿你可自己进行设置,如第一行为查询的字段中文名,
05.第一行为行的序号等,如果你不进行设置,那么默认会取每
06.个查询得到的字段名,每一行的序号
07.*/
08. 
09.m_pMapFile->SetGridText(pDispInfo);
10. 
11./*
12.上面的函数只是用来得到了本来,你还可以用它来进行另
13.外一些属性,如当超过一定的值是显示DBGRID的不同的前
14.景色和前景色等
15.*/
16.}

步骤六

添加成员函数Release(),下面是它的代码,用于释放资源


01.void CDemo2Dlg::Release()
02.{
03.if(this->m_pMapFile)
04.{
05.m_pMapFile->Release();
06.m_db.Close();
07.delete m_pMapFile; m_pMapFile = NULL;
08.}
09.}

在上面的代码中 m_pMapFile->Release()是COdbcDBGRIDFILE的成员函数,用于释放所有的资源。

步骤七

添加对话框的消息响应函数OnClose(),释放所有资源


1.void CDemo2Dlg::OnClose()
2.{
3.this->Release();
4.CDialog::OnClose();
5.}

完成好上面的工作,你就可以编译运行了,在上面的编辑框上输入数据源名、用户名、口令和查询的sql语句,你就可以试试效果了,你可以找一张大一点的表,最好有几十万条记录来测试一下,这个DBGRID怎么样。

四、总结

使用这个DBGRID的好处在于我们在查询时,不需要进行先绑定的操作,而且它比微软的DBGRID控件方便多了,希望它能为我们用VC开发数据库项目提高一点效率。如果你看过上一篇文章,用类CMYODBC和CODBCSet来代替MFC的CDatabase和CRecordset的话,那么运行的效率会有很大的提高,我也写了一个相似的类CMyODBCDBGRIDFile,并且用这个类做了一个样例,就是文章篇头提供的配套代码二。


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
author-avatar
招聘HR
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有