
简介面向 MFC 开发者的 DataGrid 控件用法详解文档重点解决在 VC 对话框程序中网格化展示与操作数据的问题适合需要快速上手 ADO 数据绑定、列控制与格式化的读者。文档以可运行工程 TestGrid 为基础系统讲解将查询结果通过 SetRefDataSource 绑定到控件、利用 CColumns/CColumn 调整列宽、使用 SetNumberFormat 将小数显示为百分数、设置行高实现多行显示以及通过 HeadClick 消息进行排序等关键用法并配有 VC6.0 下的调试说明使读者能够边读边练、直接对照实践。资源为单份 doc 文档压缩包大小 226KB内容集中、轻量精炼适合作为 MFC 界面开发中网格控件的案头参考目前已有 849 人学习下载是入门与进阶 DataGrid 控件时不可多得的实用资料。1. MFC DataGrid控件老项目里最省事的网格方案先搞懂再动手接触过VC6.0时代MFC项目的工程师大概率都跟DataGrid控件打过照面。这玩意是微软自家提供的ActiveX网格控件用来显示数据库查询结果非常顺手当年很多桌面管理系统里的表格界面都是它撑起来的。它的优势在于和Windows系统兼容性极好你不需要自己实现排序、列宽调整、单元格格式化这些功能只要把数据源接上界面就出来了。网上虽然有不少第三方网格控件功能看着更花哨但用微软自家的东西有个实际好处——它随系统升级你的代码几乎不用大改就能继续跑。这篇笔记我把实际拆过的工程VC6.0 Windows XP工程名TestGrid里的用法整理出来覆盖三块核心操作显示查询结果、控制列宽、格式化单元格内容。适合正在维护老MFC项目、或者被迫用VC6.0干活的朋友新手按步骤走也能跑通。2. 准备工作插入控件与类封装别在这步偷懒2.1 找到并插入Microsoft DataGrid控件先建立一个基于对话框的MFC工程然后在对话框编辑器里右键选择插入ActiveX控件。在弹出的列表里找到Microsoft DataGrid Control, version 6.0 (OLEDB)确定插入。注意这里要选OLEDB版本不是ODBC那个老版本因为OLEDB的连接方式更灵活配合ADO用起来最顺手。插入后对话框上会出现一个空的网格默认带一个示例列。这时候先别急着写代码右键这个控件选择建立类。VC6.0会弹出一个确认类名的对话框这里需要手动选择要封装的类。一般情况下至少选三个CDataGrid控件主类、CColumns列集合类、CColumn单列类。提示这几个类是对ActiveX控件COM接口的封装本质上是调用了控件的COM查询接口。所以你看不到控件内部源码只能通过方法名推断功能。这也是为什么很多新手一开始觉得它像个黑匣子——其实方法名起得很直白见名知意。2.2 认识三个核心类的关系CDataGrid是控件本身负责整体显示和数据源挂接。CColumns是列集合通过CDataGrid.GetColumns()拿到。CColumn是单个列对象通过CColumns.GetItem()拿到。这个关系一定要理清楚。打个比方CDataGrid是一张表CColumns是所有列的目录CColumn是目录里某一列的详细档案。你要改列宽、改格式、改标题最终操作的都是CColumn对象。还有个容易忽略的点GetItem()的参数是VARIANT类型传索引时要小心类型转换。常见做法是构造一个VARIANT变量赋值vt.iVal 列索引vt.vt VT_I4。直接传整数在某些编译器版本会出类型不匹配的警告。// 正确获取列对象的方式 CColumns cols m_ctrlDG.GetColumns(); VARIANT vt; vt.vt VT_I4; vt.iVal 0; // 第0列 CColumn col cols.GetItem(vt);这段代码先拿到列集合再通过VARIANT类型的索引取出具体列。参数说明vt是VARIANT结构体必须显式设置vt类型为VT_I44字节整数不然COM调用可能失败。3. 显示查询结果ADO连接数据库把Recordset绑定给控件3.1 建立数据库连接和执行查询原文用的是ACCESS数据库文件note.mdb连接字符串用Jet OLEDB 4.0 Provider。这个Provider在32位系统上很成熟但64位系统上经常翻车——后面避坑章节会详细说。CADODatabase m_DBCn; CADORecordset m_Rs; CDataGrid m_ctrlDG; CString strConnection; strConnection.Format(_T(ProviderMicrosoft.Jet.OLEDB.4.0;) _T(Data Sourcenote.mdb)); m_DBCn.Open((LPCTSTR)strConnection); m_Rs.SetDatabase(m_DBCn); m_Rs.Open(_T(select * from test;)); m_ctrlDG.SetRefDataSource((LPUNKNOWN)m_Rs.GetRecordset());注意这段代码里的ADO封装类CADODatabase、CADORecordset不是MFC自带的需要你自己引入一个ADO封装类或者直接用原生ADO方式。逻辑说明前三行声明对象然后连接字符串指定数据库文件路径打开连接把记录集关联到数据库执行查询最后把记录集的IUnknown接口传给DataGrid控件做数据源。这里有个关键点SetRefDataSource的参数是LPUNKNOWN类型也就是COM接口指针。如果你用的是ADO的RecordsetPtr记得用GetInterfacePtr()获取原始接口指针而不是直接传智能指针。3.2 数据刷新时机与释放问题我第一次做的时候踩过一个坑查询执行完毕后立即SetRefDataSource网格确实显示了数据但关闭记录集后网格又变空了。原因在于DataGrid控件内部持有的是记录集的COM引用记录集一旦释放数据源就失效了。正确的做法是数据源连接和记录集的生命周期要覆盖DataGrid的使用周期。一般情况下m_Rs作为成员变量在对话框销毁时才关闭。如果需要在同一控件上切换不同查询结果可以用m_ctrlDG.Refresh()来刷新显示。// 刷新数据源的通用写法 m_Rs.Close(); m_Rs.Open(_T(select * from table2;)); m_ctrlDG.Refresh();数据源切换时先关闭旧的记录集再开新的然后调Refresh让控件重新取数据。参数说明Close后记录集处于关闭状态必须重新Open才能再次作为数据源。4. 控制显示与格式化列宽、标题、百分数格式实操4.1 设置列宽和列标题之前已经讲过怎么用CColumns和CColumn获取列对象拿到列之后的控制手段才是重点。CColumns cols m_ctrlDG.GetColumns(); VARIANT vt; vt.vt VT_I4; vt.iVal 0; CColumn col cols.GetItem(vt); col.SetWidth(100); // 设置列宽为100像素 col.SetCaption(_T(学号)); // 设置列标题 col.SetVisible(FALSE); // 隐藏该列列的常用属性就这几个宽度、标题、可见性。SetWidth的单位是像素twips不这里是像素老版本VC里可能会有单位换算的问题——有次我设了个50结果列窄得看不见后来发现是单位不对。实际上MSDN的说明里宽度单位是OLE_XFORM_PIXELS但有些旧文档说是缇twip实践下来用像素最靠谱。4.2 数字格式化成百分数这个是DataGrid比较实用的功能之一。如果你的数据源里存的是0.85这样的小数又想显示成85.0%不用自己在SQL里做字符串拼接直接在列上设置格式就行。col.SetNumberFormat(_T(0.0%));这个格式串的含义和Excel里是一样的0是数字占位.0表示保留一位小数%表示乘以100并加百分号。同理如果你要显示成两位小数的普通数字用_(0.00)要加千分位分隔符用_(#,##0.00)。提示SetNumberFormat对整列生效如果你想在同一列里混用不同格式做不到。数据格式显示的统一性在设计表格时就要考虑好。这里有个细节值得注意格式串里的百分号在CString里没有任何特殊含义直接传字符串就行。但如果是通过资源文件加载字符串别忘了百分号在格式化输出时的转义问题。4.3 行高与多行显示原文提到了多行显示对应到代码就是设置行高。m_ctrlDG.SetRowHeight(30);行高的单位为像素默认值大约是20左右。设成30后每行高度增加适合显示内容比较长的单元格也支持自动换行。不过要注意行高只影响数据显示区域不改变表头高度。4.4 列排序功能用HeadClick消息实现DataGrid控件有个HeadClick事件点击列标题时触发。利用这个事件可以做点击列名排序。// 在头文件中声明消息处理函数 afx_msg void OnHeadClick(short ColIndex); // 消息映射 ON_EVENT(CDataGrid, 0, 27, OnHeadClick, VTS_I2)消息映射里的事件ID27是HeadClick在DataGrid控件的dispatch ID。不同版本控件可能不一样如果消息没响应可以用OLE/COM Object Viewer查一下控件的类型库。排序逻辑可以用ADO记录集的Sort属性实现void CMyDlg::OnHeadClick(short ColIndex) { CString strSort; // 根据列索引构造排序字段名 CColumns cols m_ctrlDG.GetColumns(); VARIANT vt; vt.vt VT_I4; vt.iVal ColIndex; CColumn col cols.GetItem(vt); CString strField col.GetCaption(); m_Rs.Sort _T(学号 ASC); m_ctrlDG.Refresh(); }注意ADO的Sort属性只在客户端游标模式下有效。如果记录集用的是服务器端游标Sort属性会失效具体表现就是点标题没反应。5. 避坑指南DataGrid常见问题与排查记录5.1 现象64位系统上无法注册控件或者运行时崩溃原因Microsoft DataGrid 6.0是32位ActiveX控件在64位系统上需要注册到SysWOW64目录而且运行时需要通过32位进程加载。如果你的程序编译成64位直接使用这个控件会失败。解决一种办法是强制编译成32位程序VC6.0本身只能产出32位程序所以这个问题主要在把旧代码迁移到VS2010以上版本时出现。另一种办法是用regsvr32注册32位版本的控件CD C:\Windows\SysWOW64然后regsvr32 msdatgrd.ocx。5.2 现象SetRefDataSource后网格显示空白数据没出来原因记录集没有处于打开状态或者记录集的游标类型不支持绑定。DataGrid要求数据源是客户端游标adUseClient如果使用adUseServer控件拿不到数据。解决打开记录集时显式指定客户端游标——在ADO封装类里调用Open之前设置CursorLocation参数或者在Open语句里加上adCmdText和adUseClient。5.3 现象列宽设置不起作用或者宽度值不对原因SetWidth接收的实际是缇twip值不是像素值。1像素等于15缇按96DPI计算所以如果直接传像素值列宽会缩小15倍。解决把像素值乘以15再传给SetWidth。想省事的话用屏幕DC的GetDeviceCaps(LOGPIXELSX)把DPI取出来然后换算。旧代码里如果看到SetWidth(1500)这种值基本就是这个原因。5.4 现象点击列标题排序没反应原因事件映射ID不对或者记录集游标类型不对。HeadClick的dispatch ID在不同版本控件里可能有差异用错ID事件不会触发。解决先用OLE/COM Object Viewer打开msdatgrd.ocx的类型库找到HeadClick的dispatch ID确认是你要用的版本。然后检查记录集的CursorLocation确保是adUseClient。5.5 现象程序退出时崩溃报错在DataGrid释放阶段原因DataGrid控件和记录集的释放顺序不对。如果先释放记录集再销毁控件控件内部还持有已失效的COM引用释放时就会崩溃。解决在对话框的OnDestroy里先销毁控件或者调用m_ctrlDG.SetRefDataSource(NULL)断开数据源然后再关闭记录集。释放顺序是先断开数据源关闭记录集再销毁控件。6. 进阶玩法DataGrid控件的边界与替代方案把原工程跑通之后有几个点值得继续深入。DataGrid 6.0最明显的限制是它依赖OLEDB无法直接绑定自定义数据结构比如数组或C容器。如果不想走数据库可以考虑的替代方案是MSFlexGrid但MSFlexGrid的格式化能力又差一些。对于还在维护老项目的场景我的习惯做法是把DataGrid封装成一个自绘控件类内部统一处理数据源绑定、列格式设置、排序逻辑。这样即使以后换控件界面层的改动也能控制在一个类里面。另一个比较实用的技巧是动态生成列。默认情况下DataGrid会根据数据源的字段自动生成列。如果只想显示部分字段可以在绑定之前先清除默认列然后手动添加需要的列m_ctrlDG.GetColumns().Clear(); // 重新添加列 CColumn col1 m_ctrlDG.GetColumns().Add(0); col1.SetCaption(_T(ID)); col1.SetDataField(_T(id));列集合的Add方法需要传入列索引返回新的CColumn对象。然后用SetDataField指定这个列绑定到记录集的字段名。这个方法适合做动态报表用户选哪些字段就显示哪些列。关于单元格合并、背景色设置、字体控制这些高级功能DataGrid 6.0支持得不好。真有这些需求建议跳槽到更现代的控件——但如果是老系统维护能用DataGrid把数据展示好、格式调对、排序做出来功能上已经够业务用了。从那以后我每次接DataGrid相关的活儿都会强制先跑一遍注册控件、连数据库、绑数据源、调列宽格式、点排序把基础五步走完再谈别的。这套流程走下来能过滤掉大部分低级问题。希望帮到你。本文还有配套的精品资源点击获取