
1. 两个名字很像的表格控件到底差在哪第一次在机房维护老系统的时候我看到界面上密密麻麻的表格第一反应是这不就是个显示数据的网格嘛。结果打开代码一看里面同时出现了MSHFlexGrid和MSFlexGrid两个控件名当时就懵了——名字只差一个字母功能看起来也差不多为什么一个项目里要混着用后来翻了不少老代码、也踩过几次坑才搞明白这两个控件虽然长得像亲兄弟但定位完全不同。MSFlexGrid是更早一代的网格控件主要能力就是把数据摆出来给人看排序、合并单元格、格式化显示这些它都能做但它本身不绑定任何数据源你得自己一行一行往里填。而MSHFlexGrid是在它基础上扩展出来的版本多了一个关键能力可以绑定分层记录集也就是能直接展示带层级关系的数据比如部门—员工这种父子结构展开收起都能自动处理。这个区别在机房这类场景里特别重要。机房管理系统经常要展示机柜、设备、端口这种多层结构用MSFlexGrid你得手动算缩进、手动处理展开逻辑代码又长又容易出错换成MSHFlexGrid绑定一个分层记录集层级关系它自己就渲染出来了。但反过来说如果只是展示一张扁平的设备清单用MSHFlexGrid就有点杀鸡用牛刀MSFlexGrid反而更轻快。所以这篇文章我想把这两个控件彻底讲清楚它们各自的能力边界在哪、什么场景该选哪个、实际写代码时有哪些坑、机房类项目里怎么用才稳。不管你是刚接手老系统的维护人员还是准备用它们做点小工具的新手看完应该都能直接上手。2. MSFlexGrid不绑数据源的纯展示型网格2.1 它的核心定位就是手动填格子MSFlexGrid最本质的特点是它不关心数据从哪来。你可以把它想象成一张空白的 Excel 表行列数你自己定每个格子里放什么也完全由你决定。它不主动去连数据库、不主动去读记录集所有内容都得靠代码往里塞。这种设计听起来好像很原始但在很多场景下反而是优点。比如机房巡检时要生成一张临时报表数据是从好几个地方拼凑出来的有来自数据库的、有来自配置文件的、还有实时计算的这时候用一个不绑数据源的网格反而最灵活你想怎么填就怎么填不用去迁就数据源的格式。它的基本用法大概是这样 假设网格名叫 MSFlexGrid1 MSFlexGrid1.Rows 10 设置总行数 MSFlexGrid1.Cols 5 设置总列数 MSFlexGrid1.TextMatrix(0, 0) 机柜编号 MSFlexGrid1.TextMatrix(0, 1) 设备名称 MSFlexGrid1.TextMatrix(1, 0) A-01 MSFlexGrid1.TextMatrix(1, 1) 交换机注意TextMatrix(行, 列)这个属性它是MSFlexGrid最常用的读写入口。行和列都是从 0 开始计数的第 0 行通常拿来当表头。这里有个新手特别容易踩的坑如果你没先把Rows和Cols设够直接往TextMatrix里写数据会报错或者写不进去。我见过有人只设了Rows忘了设Cols结果数据全挤在第一列排查半天才发现是列数没设。2.2 固定行固定列表头不跟着滚动的小技巧机房报表经常有很多行数据用户往下滚的时候表头如果跟着滚走看数据就很痛苦。MSFlexGrid提供了FixedRows和FixedCols两个属性来解决这个问题。MSFlexGrid1.FixedRows 1 第0行固定作为表头 MSFlexGrid1.FixedCols 1 第0列固定作为行标识设了FixedRows 1之后第 0 行就变成冻结状态滚动时始终显示在顶部。FixedCols同理用来冻结左侧的列。这个功能在展示设备清单时特别实用——左边固定显示机柜编号右边滚动看各种参数对照起来很方便。不过这里有个细节要注意固定行和固定列在视觉上默认会显示成灰色背景如果你想让表头好看一点得手动去设CellBackColor和CellForeColor而且设置的时候要跳过固定区域或者专门针对固定区域设。我一般会写个小循环把表头行统一刷个颜色看起来清爽很多。2.3 单元格合并机房拓扑图的好帮手MSFlexGrid有一个很实用的能力是合并单元格通过MergeCells属性配合MergeRow、MergeCol来用。机房场景里比如你要展示一个机柜里有多个设备就可以把机柜编号那一列纵向合并视觉上立刻就能看出归属关系。MSFlexGrid1.MergeCells flexMergeFree 允许自由合并 MSFlexGrid1.MergeCol(0) True 第0列允许合并 MSFlexGrid1.TextMatrix(1, 0) A-01 MSFlexGrid1.TextMatrix(2, 0) A-01 相邻相同内容会自动合并MergeCells有几个取值flexMergeFree是最灵活的相邻单元格内容相同就自动合并。还有flexMergeRestrictRows、flexMergeRestrictCols等限制只能按行或按列合并。实际用的时候我建议先用flexMergeFree试效果不满意再换限制模式。注意合并单元格之后读取数据要特别小心。比如第 0 列合并了你读第 2 行的第 0 列拿到的可能是空字符串因为实际内容只在合并区域的第一个格子里。这个坑我在做设备统计时踩过明明显示着 A-01代码读出来却是空的后来才知道要往上找合并区的起始行。2.4 排序和格式化展示层的最后一块拼图MSFlexGrid自带排序功能Sort属性可以按指定列排序。机房设备清单按 IP 地址、按上架时间排序都是常见需求。MSFlexGrid1.Col 2 MSFlexGrid1.Sort flexSortStringAscending但要注意它的排序是纯字符串排序。什么意思呢就是 10 会排在 9 前面因为字符 1 比 9 小。如果你的 IP 地址是 192.168.1.9 和 192.168.1.10排序结果会不符合直觉。解决办法是要么补零对齐要么自己写排序逻辑把数据先排好再填进去。这个细节很多教程都不提但实际项目里几乎一定会遇到。格式化方面MSFlexGrid支持通过CellFontBold、CellForeColor、CellBackColor等属性对单个单元格设置样式。比如把告警状态的设备标红就是遍历数据时判断状态然后设CellForeColor vbRed。这些属性都是针对当前单元格生效的所以设置前要先设Row和Col定位。3. MSHFlexGrid能绑分层数据的进阶版3.1 分层记录集是它最大的杀手锏MSHFlexGrid和MSFlexGrid最本质的区别就是它支持分层记录集。普通记录集是一张平表而分层记录集可以带子记录集形成树状结构。机房管理里这种结构太常见了机房下面有多个机柜机柜下面有多个设备设备下面有多个端口。用MSFlexGrid做这种展示你得自己维护缩进层级、自己处理展开收起、自己算每一行属于哪个父节点代码量巨大还容易乱。而MSHFlexGrid绑定分层记录集之后层级关系会自动渲染成带缩进的树形表格点击展开收起也是内置的。 假设已经有一个分层记录集 rs Set MSHFlexGrid1.DataSource rs就这一行层级结构就出来了。当然实际用的时候还要配一些属性来调整显示效果但核心逻辑就是这么简单。这也是为什么很多老机房系统里设备树、组织架构这类展示都优先用MSHFlexGrid。3.2 绑定普通记录集时的注意事项虽然MSHFlexGrid主打分层但它也能绑普通记录集。不过这里有个很多人不知道的细节绑定普通记录集时它的行为和MSFlexGrid手动填充差别很大。绑定之后网格的行列结构是跟着记录集走的你不能随便去改Rows和Cols否则会破坏绑定关系。而且记录集里的字段顺序决定了列的顺序想调整列顺序得在 SQL 里调整SELECT的字段顺序或者在绑定后通过Col属性去移动列。还有一个坑是空记录集的处理。如果绑定的记录集一条数据都没有网格可能会显示成一片空白甚至报错。稳妥的做法是绑定前先判断记录集是否为空为空就显示个暂无数据的提示或者干脆不绑定、手动填一行提示文字。3.3 展开收起事件做交互的关键入口MSHFlexGrid在展开或收起某个层级时会触发RowColChange或者专门的展开事件。利用这些事件你可以做很多交互增强。比如展开某个机柜时动态去加载它下面的设备数据而不是一次性把所有数据都查出来。Private Sub MSHFlexGrid1_RowColChange() 判断当前行是否是展开状态 根据状态决定是否加载子数据 End Sub这种按需加载的思路在机房设备特别多的时候很有用。一次性查几千条设备数据再绑定界面会卡很久改成展开时才加载响应就快多了。不过要注意动态加载的数据要正确挂到分层记录集上否则展开后子节点显示不出来。这块逻辑稍微复杂一点建议先在测试环境把层级关系理清楚再上生产。3.4 和 MSFlexGrid 混用时的兼容性问题有些老项目里两个控件同时存在这时候要特别注意属性名和方法名的差异。虽然它们大部分属性是共通的但MSHFlexGrid多出来的一些属性在MSFlexGrid上是不存在的反过来也一样。如果你写了个通用函数想同时操作两种网格得先判断类型。If TypeName(ctrl) MSHFlexGrid Then 走分层逻辑 ElseIf TypeName(ctrl) MSFlexGrid Then 走普通逻辑 End If另外两个控件的默认属性值也可能不同。比如FixedRows的默认值、AllowBigSelection的默认行为等混用的时候如果不显式设置可能会出现同样的代码在两种网格上表现不一样的情况。我的习惯是不管用哪个关键属性都显式设一遍不依赖默认值这样代码更可控。4. 机房场景下的选型逻辑与实战建议4.1 什么情况选 MSFlexGrid什么情况选 MSHFlexGrid选型其实就看一个核心问题你要展示的数据有没有层级关系。场景推荐控件理由扁平设备清单MSFlexGrid轻量手动填充灵活不需要分层开销机柜-设备树MSHFlexGrid分层记录集自动渲染层级省去大量手写逻辑临时拼凑的报表MSFlexGrid数据来源杂不绑数据源反而方便需要展开收起的结构MSHFlexGrid内置展开收起交互成本低数据量小且固定两者皆可看个人习惯MSFlexGrid 代码更直观我个人的经验是如果数据是一张平表能说清楚的就用MSFlexGrid如果数据天然是树状的就用MSHFlexGrid。不要为了用高级功能而用高级功能MSHFlexGrid的分层能力虽然强但配置起来比MSFlexGrid复杂扁平数据用它反而增加维护成本。4.2 性能上的取舍数据量大时怎么办机房设备多的时候几千上万行数据是常事。这时候两个控件都会面临性能问题但表现不太一样。MSFlexGrid手动填充时每写一个TextMatrix都会触发重绘数据量大时界面会疯狂闪烁、卡顿。解决办法是填充前先设Redraw False填完再设回True。MSFlexGrid1.Redraw False ... 大量填充操作 ... MSFlexGrid1.Redraw True这一招能显著提升填充速度我实测过几千行数据从卡顿几秒变成瞬间完成。MSHFlexGrid绑定记录集时性能主要取决于记录集本身的大小和查询效率。如果记录集很大绑定过程也会慢。这时候可以考虑分页查询或者用前面说的按需加载。另外MSHFlexGrid在渲染大量层级节点时展开收起会有一定延迟这是它为了维护层级结构付出的代价。4.3 那些文档里不写的踩坑经验第一个坑列宽自适应。两个控件默认列宽都是固定的数据长了显示不全短了浪费空间。MSFlexGrid可以用ColWidth手动设但更省事的做法是填充完数据后遍历一遍根据内容长度动态调整。不过要注意中文字符宽度和英文不一样简单按字符数算会不准最好用TextWidth方法实际测量。第二个坑滚动条位置。数据刷新后滚动条经常会跳回顶部用户体验很差。可以在刷新前记录Row和Col刷新后恢复。但MSHFlexGrid在分层展开状态下恢复滚动位置比较麻烦因为展开状态变了行号就变了这个要特别小心。第三个坑右键菜单和选中行。机房工具经常需要右键操作某台设备。两个控件都支持MouseDown事件判断右键但要注意右键时当前行不一定会自动选中需要手动设Row MouseRow。这个细节不处理的话用户右键 A 设备操作的却是之前选中的 B 设备很容易出事故。第四个坑数据更新后的刷新。如果底层数据变了MSFlexGrid需要你手动重新填充MSHFlexGrid绑定模式下可以重新绑定或者调用Refresh。但重新绑定会丢失展开状态和滚动位置所以更新单条数据时最好只改对应的单元格而不是整体刷新。4.4 一个机房设备树的完整实现思路假设要做一个机房设备树展示机房 → 机柜 → 设备三层结构用MSHFlexGrid来实现思路大概是这样第一步构造分层记录集。这通常需要用Shape命令或者手动构建层级关系。Shape命令可以在一条 SQL 里查出层级数据但语法比较特殊需要花点时间理解。第二步绑定到MSHFlexGrid设置好各层的显示列和缩进。第三步处理展开事件在用户展开某个机柜时如果设备数据还没加载就去数据库查出来挂到对应节点下。第四步处理选中事件用户点击某台设备时在旁边的详情面板显示这台设备的完整信息。这个思路的核心是分层数据用分层控件详情展示用普通面板各司其职。不要试图在一个网格里把所有信息都塞进去那样只会让界面又乱又难维护。5. 老控件在新环境下的生存之道这两个控件都是有些年头的技术了现在新项目基本不会再用。但机房维护这类场景老系统还在跑就还得跟它们打交道。我的态度是不主动引入但遇到了要能搞定。实际维护中最常见的需求就是在现有网格上加一列或者改个显示格式。这种小改动理解清楚TextMatrix的读写逻辑和行列索引基本就能搞定。稍微复杂点的是数据源变了要调整绑定这时候要特别注意记录集字段和网格列的对应关系。如果确实要新做功能又必须用这两个控件我的建议是优先用MSFlexGrid手动填充。虽然代码量大一点但逻辑清晰、可控性强出了问题好排查。MSHFlexGrid的分层能力虽然诱人但调试起来麻烦得多层级关系一旦出错排查链路很长。最后分享一个实用技巧不管用哪个控件把网格操作封装成独立的函数比如FillGrid、SetCellStyle、GetSelectedRow这些。这样一方面代码复用度高另一方面将来真要迁移到新控件只需要改封装层业务代码不用动。我在几个老系统上都是这么做的后来做界面升级时省了大量返工时间。另外调试网格问题时善用Debug.Print把Rows、Cols、Row、Col这些关键值打出来很多显示不对的问题一看行列数就明白原因了。网格控件的坑十有八九都跟行列索引对不上有关。