简介:面向 Delphi 12.3 开发者的 Ehlib 控件资源包,提供数据库网格、数据感知编辑框、下拉选择等常用组件,适合需要高效处理表格数据展示、编辑、排序与打印场景的中高级桌面应用开发者。包内共 2000 个文件,结构完整,其中 470 个 dfm 窗体文件用于界面定义、469 个 pas 源码文件支撑逻辑实现,245 个 res 资源与 189 个 dproj、170 个 dpk 工程文件帮助完成编译和包管理,另有 CHM 帮助文档、示例图片及 gif 动图辅助上手。压缩包约 32.7MB,体积适中,目录层次清晰,从控件源码、设计时窗体到运行期资源均有覆盖,既可直接安装使用,也可按需修改源码排查问题。已有 60 人学习下载,适合希望深入使用 Ehlib 或基于其二次开发的 Delphi 开发者。
1. Ehlib 111015 是什么:Delphi 12.3 里把 DBGrid 升级成报表级表格的控件包
Delphi 12.3 里做数据密集型桌面程序,原生 DBGrid 能用,但远远不够用。它做不了多表头、做不了行分组、做不了单元格合并,更别想一键导出用户要的 Excel。Ehlib 就是解决“DBGrid 再往上一层”需求的组件包:把表格从单纯的数据展示控件,提升到报表级的数据呈现与交互控件。标题里的“111015”是控件包的构建标记,不是常规版本号,后面会讲怎么读懂它。这个组件包在 Delphi 开发者中覆盖很广,凡做进销存、管理系统、报表工具的项目,几乎都会用到。如果你正被“用户要的表格样式,DBGrid 给不了”这类需求卡住,或者想把老工程的列表展示升级成带过滤、排序、导出的完整方案,这个包值得装。装上不难,真正让人翻车的是版本匹配、路径配置和导出格式三个环节,下面全拆开讲。
2. 安装前的三个选择题:构建标记、包结构与版本匹配
2.1 从构建标记 111015 读懂控件的版本坐标
拿到Delphi 12.3控件之Ehlib 111015.rar,先别急着解压安装。文件名里的 111015 不是标准语义化的版本号,更像一个构建快照编号。第三方控件作者惯用的命名有两种:一种是“主版本号.构建号”,另一种是构建日期数字串,比如“年份月日”。你手里的 111015 更像后者——2023 年 11 月 10 日或 15 日发布的某个构建,也可能是“构建 1110、第 15 次小修”的内部序列。具体是哪一种,解压后看包里 .dpk/.dproj 文件名的数字后缀就能确认。这里想强调一点:Ehlib 是跟着 Delphi 编译器版本走的控件,每个 Delphi 大版本都要编译对应的一套 bpl/dcp。你在 Delphi 12.3 里硬装一个给 Delphi 10.4 编译的 Ehlib 包,结果一定是 IDE 刷新不出组件,或者工程一编译就报包版本不匹配。
所以第一个动作是核对包内有没有对应 Delphi 12 / RAD Studio 12 的工程文件。常见做法是解压后打开Packages目录,看里面的 .dpk 文件名。Ehlib 这类控件包通常按年份或内部代号区分 IDE 版本,你要找的是能对应 12.x 那一档的描述文件。如果文件名含糊,看 .dproj 文件里的<Project Name>和平台配置。还有一个更快的判断方式:看源码目录里 .pas 文件头部注释中的版本历史或条件编译指令,Ehlib 的源码会针对新版 RTL 写{$IF ...}分支,支持新版本的分支越多,说明这个构建越新。把这些判断要点列成表,方便对照:
| 检查项 | 怎么看 | 通过标准 |
|---|---|---|
| 编译工程文件 | Packages 目录下 .dpk / .dproj | 存在对应 Delphi 12 的包描述 |
| 编译单元版本 | Source 目录 .pas 首部注释或条件编译指令 | 有支持新 RTL 的条件分支 |
| dcu 组织方式 | Build 目录内文件分布 | 按版本分子目录,而不是堆在根目录 |
表里最后一行值得多说一句。一个规范的 Ehlib 构建包,Build 目录下会按 Delphi 版本号分子文件夹,比如Build\12和Build\11各放各的 dcu。如果解压后所有 dcu 平铺在一个目录里,多半是某位开发者从自己机器上打包出来的“私人构建”,这类包能装上,但换机器后很难复现同等的编译环境,风险自己扛。
2.2 包内目录结构:认清四个目录,Library Path 才不迷路
解压后你会看到类似这样的目录结构:
Ehlib/ ├── Packages/ # 各 Delphi 版本的 .dpk 与 .dproj,按 IDE 版本归类 ├── Source/ # .pas 源码,运行时单元与设计期单元混排 ├── Build/ # 编译产物 bpl/dcp/dcu,按版本分文件夹 ├── Demos/ # 示例工程,每个功能点对应一个独立 Demo └── Help/ # CHM 帮助文档,属性与方法的唯一权威来源Packages目录是安装的入口。Source目录是必须加进 IDE Library Path 的,因为 Ehlib 的运行时包在编译你的工程时,IDE 需要找到这些 .pas 文件来解析单元引用。Build目录里放的是编译好的 bpl、dcp、dcu,正常情况下你不用手动把 Build 加进 Library Path,IDE 会通过注册包自动找到 bpl。如果你把 Build 和 Source 同时加进 Library Path,反而容易出问题:IDE 搜索路径是优先顺序的,旧 dcu 只要存在就会被优先命中,改源码重新编译后不生效的“玄学”往往就是这么来的。
我一般的配置习惯是只把 Source 目录加入 IDE 的 Library Path。这样每次你编译工程,IDE 都会用你本机这份源码重新编译 Ehlib 相关单元,而不是去 Build 目录里捡现成 dcu。用命令行快速确认包内容也是一个好习惯。在解压目录下执行:
# 列出包内所有 dpk 文件,确认是否存在 Delphi 12 对应版本 dir /s /b Packages\*.dpk # 如果包内同时有源码和已编译 dcu,优先确认 Source 目录文件完整性 dir /b Source\*.pas | find /c ".pas"第一个命令看的是包描述文件,第二个命令统计源码单元数量。如果源码文件数少得可疑,说明这个 rar 可能被精简过,只保留了编译产物,这种包换一台机器就容易失效。另外有个小经验:无论从哪个渠道拿到的包,解压路径都放到纯英文目录,比如D:\Components\Ehlib。中文路径对 IDE 的 dcu 搜索是个隐患,后面避坑章会展开讲。
2.3 Delphi 12.3 下安装 Ehlib 的最小路径:六步走
安装本身不复杂,但每一步都有容易被忽略的细节。按下面顺序操作,能少走一半弯路。
- 先关闭所有 Delphi 实例,解压 rar 到纯英文路径。
- 重新打开 Delphi 12.3,进入
Component > Install Packages,检查右侧已安装包列表里有没有 Ehlib 相关项。如果有,先选中并 Remove,确保干净的安装环境。 - 在
Packages目录里找到对应 Delphi 12 的 .dpk 文件,双击在 IDE 中打开。打开后项目管理器里会显示这个包的所有单元。 - 在项目管理器里右键包节点,先选
Compile。这一步只编译不安装,能快速暴露源码层面不兼容的问题。编译通过后,再右键选Install。Install 会把设计期包注册进 IDE,组件面板里才会出现 Ehlib 页面。 - 打开
Tools > Options > IDE > Library > Library Path,点开编辑框,追加 Source 目录路径。多个路径用分号分隔。 - 新建一个 VCL 工程,拖一个 DBGridEh 到窗体上,确认组件面板里出现 Ehlib 页且控件能正常放置。
第 4 步最容易出问题。Compile 时如果报找不到某个 .pas 或 .dcu,不要急着换版本,先检查 Library Path 里是否已经包含了 Source 目录。很多安装教程把 Library Path 配置放在最后,但你必须在编译 dpk 之前就把它加好,否则 dpk 里的单元引用根本解析不到。Install 时如果提示包已存在,那是第 2 步没清干净,回过去删掉旧注册的包再试。
安装完成后,用一段最小的代码验证数据感知链路是通着的:
// 最小验证:DBGridEh 能否正常绑定数据源并展示数据 procedure TForm1.FormShow(Sender: TObject); begin DBGridEh1.DataSource := DataSource1; DataSource1.DataSet := FDQuery1; FDQuery1.SQL.Text := 'SELECT * FROM customers LIMIT 100'; FDQuery1.Open; end;代码里的链路是DBGridEh -> DataSource -> FDQuery,与原生 TDBGrid 的绑定方式一致。如果控件没安装成功,编译到这里会直接报 Unknown identifier:DBGridEh1,错误位置会指向这一行,排查范围立刻缩小到控件注册环节。
2.4 安装成功的三个可验证信号
装没装成功,不要只看组件面板里有没有图标。真正的验证信号有三个。第一个信号是刚才那段最小代码能编译通过并运行出数据。第二个信号是 IDE 的信息窗口里,编译 dpk 时没有出现红色错误,只有“Build succeeded”或“Install successful”之类提示。第三个信号是你打开工程选项里的Packages页面,能看到新增的 Ehlib 运行时包处于勾选状态。这三个信号全部满足,才说明控件不仅装上了,而且与当前工程的编译链路是通的。很多开发者在组件面板里看到了 Ehlib 页面就认为成功了,结果编译整个工程时找不到 dcu,其实问题出在 Library Path 没配,属于第一信号没通过的情况。
3. TDBGridEh 实战:把原生 DBGrid 升级成报表级表格
3.1 用 TDBGridEh 替换原生 DBGrid:最小改造与事件签名差异
替换控件本身是小事。TDBGridEh 派生自 TCustomDBGrid,数据感知接口与原生完全兼容。原来用FDQuery + DataSource + DBGrid的地方,只需要把 DBGrid 换成 DBGridEh,数据绑定代码一行都不用改。最小改造长这样:
// 原工程改造:只换控件不换数据绑定逻辑 DBGridEh1.DataSource := DataSource1; DataSource1.DataSet := FDQuery1; FDQuery1.SQL.Text := 'SELECT * FROM orders ORDER BY order_no DESC'; FDQuery1.Open;这套链路和原生 DBGrid 一模一样。真正要留意的是事件签名。如果你的老工程里写了OnDrawColumnCell,迁移到 DBGridEh 后,事件参数里的 Column 类型会从 TColumn 变成 TColumnEh:
// DBGridEh 的 OnDrawColumnCell 事件签名 // 注意 Column 参数类型是 TColumnEh,不再是你熟悉的 TColumn procedure TForm1.DBGridEh1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumnEh; State: TGridDrawState); begin // 大部分原有绘制代码可以直接搬过来 // 但访问 Column.Field 之前要先判空,因为 DBGridEh 允许列不绑定字段 if Column.Field = nil then Exit; // 原有绘制逻辑继续 end;TColumnEh 里与显示相关的属性多数与 TColumn 同名,比如 Title、FieldName、Width,这部分代码搬过来没问题。但 TColumnEh 增加了不少原生没有的东西,比如EndEllipsis(内容过长时列内省略号)、Rows(多表头支持)、MergedCells(合并单元格)。当你把老代码搬过来时,如果编译报“属性不存在”,去查 TColumnEh 的属性表,多半是某个原生属性被改名或拆分了。
3.2 多表头与合并单元格:Columns Editor 操作与代码等效方案
多表头是 Ehlib 最常被点名要的功能。业务里常见的需求是“订单信息”下面挂“订单号、客户名称”,“金额信息”下面挂“订单金额、实付金额”。设计期操作路径如下:
- 双击窗体上的 DBGridEh,打开列编辑器。
- 右键选择
Add All Fields,把当前数据集的所有字段生成列。 - 选中某一列,在属性面板里展开它的
Rows属性,把Rows.Count设为 2。 - 给
Rows[0].Caption填上级标题,给Rows[1].Caption填列自己的标题。此时标题栏自动变成两行。 - 对同一个上级标题下的每一列重复第 3、4 步,并保证
Rows[0].Caption取同一个值,表头就会在视觉上合并。
用代码也能达到同样效果,而且比设计期操作更可控,适合在代码里动态生成列的场景:
var Col: TColumnEh; begin Col := DBGridEh1.Columns.Add; Col.FieldName := 'order_no'; Col.Title.Caption := '订单号'; Col.Rows.Count := 2; Col.Rows[0].Caption := '订单信息'; Col.Rows[1].Caption := '订单号'; Col.MergedCells := True; // 相邻行相同值自动合并 end;列合并相关的属性,常用参数整理如下:
| 属性 | 作用 | 常用值 |
|---|---|---|
| Rows.Count | 标题行数 | 1 到 3,超过 3 行表格高度会很夸张 |
| MergedCells | 相邻行相同内容合并 | True / False |
| Title.TitleButton | 标题是否可点击 | True 表示允许点击表头触发排序 |
| EndEllipsis | 单元格内容过长显示省略号 | True |
| WordWrap | 单元格自动换行 | 配合行高使用,别单独开 |
MergedCells有一个边界要注意:它合并的是相邻行同值单元格,不是跨列合并。合并判定按查询结果顺序,所以查询语句的ORDER BY会直接影响合并效果。你要合并的列如果没有先排好序,合并区域会断裂成一块一块,看起来就像控件“失灵”。这是新手使用合并单元格时最常见的“翻车现场”,先排序,再合并,没有例外。
3.3 导出 Excel:SaveToExcel 的格式真相与乱码处理
Ehlib 导出 Excel 有一个长期存在的坑:SaveToExcel方法输出的其实是 HTML 表格,只是把扩展名写成了 .xls。Excel 打开时会弹“格式与扩展名不一致”的警告。代码调用很简单:
procedure TForm1.btnExportClick(Sender: TObject); begin // 第二个参数 True 表示只导出可见列 DBGridEh1.SaveToExcel('D:\report\orders.xls', True); end;但导出之后要验证真实格式。用一个小工具命令就能判断:
# 读取导出文件前 4 字节,判断真实格式 # HTML 文件开头通常是 3C 68(对应字符 <h) # 真正的 OLE 二进制 .xls 开头是 D0 CF 11 E0 $b = [System.IO.File]::ReadAllBytes("D:\report\orders.xls")[0..3] ([System.BitConverter]::ToString($b))如果输出是3C-68-74-6D,说明这文件本质是 HTML,Excel 能打开但会有安全警告。针对这个情况,我做项目的习惯是:内部管理系统可以接受这种伪 xls,因为业务用户只需要双击打开看数据,不会深究格式;但如果有客户提出文件要交给别的系统做二次处理,就必须导成真正的 xlsx。常见做法是先把数据通过 OLE 写入 Excel 再另存为 xlsx,或者导出 CSV 后用代码转换。另一个现实问题是中文乱码。老版本 Ehlib 导出 CSV 时默认按系统 ANSI 编码,到了新版 Excel 里可能显示乱码。规避手段是导出后检查编码,必要时给文件补 UTF-8 BOM,让 Excel 按 UTF-8 识别。这个操作不复杂,但遇上过一次之后,你就会把它写进导出工具函数里。
3.4 本地排序、自动过滤与行分组:三个属性组合出查询界面
Ehlib 比原生 DBGrid 更值钱的一点,是它自带本地排序和过滤。原生 DBGrid 想实现点击表头排序,得自己写OnTitleClick事件再去改 SQL,反复查数据库。Ehlib 可以在数据一次取回后,本地完成排序和过滤:
// 开启本地排序:点击表头即可排序,不再触发数据库查询 DBGridEh1.TitleParams.SortMarker := True; DBGridEh1.OptionsEh := DBGridEh1.OptionsEh + [dghAutoSortMarking, dghMultiSortMarking]; // 开启行头过滤:每列下方出现漏斗按钮,点开直接填过滤条件 DBGridEh1.STFilter.Visible := True; DBGridEh1.STFilter.Local := True; DBGridEh1.STFilter.Autofilter := True;dghAutoSortMarking控制点击表头后标题栏是否显示排序箭头,dghMultiSortMarking允许按住多列做组合排序。STFilter.Visible打开行头过滤区,STFilter.Local := True是关键——它告诉控件在本地内存里过滤,不发 SQL。组合效果是:用户在当前页的过滤框里输入条件,表格立刻收缩到匹配行,查询界面基本不用写一句业务代码。
行分组则是另一个层级的功能。设置列的AllowedGrouping属性为 True 后,这一列就允许被拖到分组面板,表格会按该列的值把行分组显示。分组后每个分组前面会有一条汇总条。这个功能适合做“按客户分组看订单”的树状展示。注意,本地排序、本地过滤和行分组是三个独立机制,可以组合用,但组合使用时数据量会决定性能。两三万行内没问题,超过十万行,本地过滤会有明显延迟,这时候要考虑下一章讲的虚拟网格模式。
4. 配套组件选型:TMemTableEh 与编辑类控件的正确用法
4.1 TMemTableEh:内存表的两类典型场景
Ehlib 不只有网格控件,还包括一套数据集组件,其中 TMemTableEh 是最常用的。它解决两类场景:一是临时汇总数据,比如把两张表的数据拼成一个内存表去做报表映射;二是避免频繁查询数据库,数据一次取回后放内存里反复筛选。TMemTableEh 与 ClientDataSet 的定位不同——它不需要 Provider,没有 Delta 机制,定位就是轻量内存数据存储。只是做展示性报表,用它比 ClientDataSet 省事太多。基本用法如下:
// 方式一:从现有查询结果快速拷贝结构和数据 MemTableEh1.CopyDataSet(FDQuery1, [coStructure, coData]); // 方式二:手工建字段并写入数据 MemTableEh1.FieldDefs.Add('order_no', ftString, 20); MemTableEh1.FieldDefs.Add('amount', ftFloat); MemTableEh1.CreateDataSet; MemTableEh1.AppendRecord(['A-1001', 299.00]);CopyDataSet第二个参数是一个选项集合,[coStructure, coData] 表示结构和数据都拷贝。如果只需要结构而不需要数据,只传 [coStructure] 即可。这是做报表模板时的常用技巧:先用查询生成空结构,再循环逐行填充。
4.2 编辑类控件:TDBNumberEditEh 与 TDBComboBoxEh 的参数设置
编辑控件方面,Ehlib 提供的 TDBNumberEditEh 解决了原生 TDBEdit 对数值格式化无能为力的问题。原生 TDBEdit 显示数据库里的原始值,如果字段是四位小数价格,界面上就是一团数字。TDBNumberEditEh 把显示格式和编辑格式分离,分别用两个属性控制:
DBNumberEditEh1.DisplayFormat := '#,##0.00'; DBNumberEditEh1.EditFormat := '0.00'; DBNumberEditEh1.ValueType := vtFloat; DBNumberEditEh1.EmptyValue := evNull; // 允许空值,不强制填 0DisplayFormat决定控件非编辑状态下的显示样式,EditFormat决定进入编辑状态后输入框里呈现的格式。两者分离的价值在于:用户点击进入编辑时,看到的是一个干净的数字输入框,而不是带千分位和人民币符号的字符串,这能避免编辑时输入光标被格式字符干扰。ValueType控制数值类型校验,设为 vtFloat 后控件会拦截非数字输入。
TDBComboBoxEh 则常被用来做“显示中文、存代码”的下拉列。设计期或代码里按顺序填入显示项和 KeyValues:
DBComboBoxEh1.Items.Add('未支付'); DBComboBoxEh1.Items.Add('已支付'); DBComboBoxEh1.KeyValues.Add('0'); DBComboBoxEh1.KeyValues.Add('1'); // 运行时数据库存 0/1,界面显示“未支付/已支付”Items是用户看到的文本,KeyValues是对应存入数据库的代码值,两个列表按索引一一对应。这样设置后,下拉框在数据感知状态下会自动把数据库里的 0 显示成“未支付”,用户选择“已支付”后写回数据库的是 1。
4.3 虚拟网格模式:让十万行明细不再卡顿
大数据量滚动卡顿是 DBGrid 类控件的通病。Ehlib 的虚拟网格模式是针对性方案:它不绑定真实数据集视图,而是在用户滚动时按需取数据。设置方法:
// 开启虚拟网格模式,适合大量数据的只读展示 DBGridEh1.OptionsEh := DBGridEh1.OptionsEh + [dghVirtualGrid]; // 虚拟模式下的取数回调 procedure TForm1.DBGridEh1VirtualGridFetchData(Sender: TCustomDBGridEh; Data: TDataSet; var Row: Integer); begin // Row 是当前需要显示的记录号,这里从外部数据源按需定位 FExternalData.Seek(Row); Data.RecNo := Row; end;虚拟模式的核心思路是:网格只请求当前视口内的那几行数据,滚动时再补。代价是部分功能会失效,包括本地过滤、自动排序、行分组和单元格合并。它适合明细台账这种只读展示的场景,不适合需要用户在网格里编辑和筛选的场景。选型时先问自己一句:这个表格用户要不要筛选?要的话就别开虚拟模式。
5. 避坑:从安装到运行的五个常见问题
5.1 编译报错找不到 dcu,或编译通过但组件面板空白
现象:在项目管理器里编译 dpk 时提示File not found: EhlibXXX.dcu,或者 dpk 编译成功、Install 也提示成功,但新建窗体后组件面板里找不到 Ehlib 页。
原因:九成是 Library Path 没包含 Source 目录。还有一小部分情况是这个 rar 是从别的机器打包的,里面的 dcu 是为旧编译器生成的,IDE 搜索到旧 dc u 后直接放弃继续查找。
解决:先确认 Library Path 里确实加了解压后的 Source 目录。然后删除 Build 目录下所有旧 dc u,回到源码目录重新编译 dpk。血的教训是:解压路径如果是中文目录名,IDE 的 Unicode 路径处理会直接让 dcu 搜索“失明”,表现就是反复配置 Library Path 都不生效。只要路径里出现中文或空格,立刻改路径。
5.2 Install 时提示 Package already exists
现象:安装到Install一步,IDE 弹窗提示某个包已经存在,安装中断。
原因:机器上装过旧版 Ehlib,旧 bpl 还注册在 IDE 的包列表里,新包无法覆盖安装。
解决:先到Component > Install Packages里手动移除所有 Ehlib 相关记录。如果移除后仍然提示存在,以管理员身份重新打开 IDE 再装一次。卸载旧版之前记得备份工程,因为旧工程的单元引用如果指向旧 dcu,卸载后重编译可能断引用。处理这类问题我习惯先搜注册表里残留的 bpl 路径,删干净再装,比反复点 IDE 界面快。
5.3 高 DPI 屏上绘制错乱、列头文字被截断
现象:4K 屏上运行程序,DBGridEh 列头出现黑线、文字只显示一半,拖动列宽时表格重绘有明显的闪烁错位。
原因:Ehlib 内置绘制代码按旧 DPI 计算尺寸。Delphi 12.3 工程默认开启 PerMonitorV2 后,控件的内部缓存位图没有跟着新 DPI 重建,于是错乱。
解决:两条路,按项目性质二选一。第一条是在工程选项的 Manifest 设置里保持 PerMonitorV2,然后在创建窗体时手动调整控件布局参数,让 Ehlib 重新计算字体和行高。第二条是“兜底方案”:在工程配置里把 DPI Awareness 固定为系统级,同时关闭系统缩放。对内部管理软件来说,第二条路最稳,因为用户不会在不同分辨率屏幕上频繁切换。某做设备台账管理的系统里,我就是用固定 DPI 的方式避免了所有第三方控件的适配问题,一劳永逸。
5.4 导出的 xls 打不开,Excel 提示格式与扩展名不一致
现象:用户双击导出的 .xls 文件,Excel 弹出警告“文件格式与扩展名不匹配”,点击“是”打开后表格能显示但样式乱了。
原因:这几乎是 Ehlib 导出 Excel 的必然现象。SaveToExcel 输出的是 HTML 表格,扩展名写成 .xls。新版 Excel 的安全策略对这类伪装文件拦截得越来越严格,老版本能打开,新版本直接警告。
解决:程序里导出后先检查文件真实格式。读取文件头几字节,如果是 HTML 特征,就按 HTML 文件处理。若客户要求真 .xlsx,不要直接依赖 Ehlib 的导出方法,而是用 OLE 把数据灌进 Excel 再另存,或者导出 CSV 再转换。还有一个细节:导出 CSV 后如果中文乱码,是编码问题,导出后给文件补 UTF-8 BOM 即可,这是最简单有效的修法。
5.5 从 DBGrid 迁移到 DBGridEh 后,事件里强转 TDBGrid 导致崩溃
现象:老工程的 OnDrawColumnCell 事件里原本写的是Sender as TDBGrid,迁移到 DBGridEh 后编译能过,但运行到该事件时直接访问违规,界面卡死。
原因:DBGridEh 触发事件时传入的 Sender 是 TDBGridEh 实例,强转成 TDBGrid 后对象布局不一致,访问内部属性时越界。
解决:事件参数里 Column 类型改为 TColumnEh,事件体里不再依赖 Sender 的类型,而是用 Column 参数取字段和绘制区域。顺带补一个判空:
procedure TForm1.DBGridEh1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumnEh; State: TGridDrawState); begin if Column.Field = nil then Exit; // 未绑定字段的列直接跳过 // 原有绘制逻辑 end;这一个if就能挡掉大部分迁移后的访问违例。DBGridEh 允许不绑定字段的列存在,比如用来放计算值的按钮列,这类列在绘制事件里没有 Field,老代码没做过这个判断就必然踩雷。
6. 进阶技巧:把 Ehlib 调到报表级输出的几个细节
6.1 导出前固定列宽与重复表头,少让用户调整 Excel
导出 Excel 前先把列宽固定好,能让输出文件直接可用。DBGridEh 的列宽在导出时会生效,所以你只需要在导出前把想要的列宽赋值给对应列:
// 导出前逐列固定列宽,避免 Excel 里再次手工调整 DBGridEh1.Columns[0].Width := 90; DBGridEh1.Columns[1].Width := 160; DBGridEh1.Columns[2].Width := 120;多行表头在设计期配好后,导出时会自动带进输出文件,不需要额外代码。这里有一个使用习惯:不要把所有列都导出,用 SaveToExcel 的第二个参数只导可见列,把辅助列、主键列、业务上不想暴露的列在设计期设为不可见,这样导出文件直接就是一份干净报表。
6.2 性能验证:给虚拟网格模式一个触发阈值
虚拟网格模式不是所有表都要开。我一般用一个简单标准:数据量预期超过五万行的只读明细表,直接用虚拟模式;数据量在五万以下且需要用户筛选排序的,用普通模式加本地过滤。你可以做一个对比验证:建一个十万行的测试表,分别用普通模式和虚拟模式打开,拖动滚动条看响应。普通模式如果滚动延迟超过 0.5 秒,换虚拟模式后的改善是立竿见影的。这个阈值不是固定的,和机器性能、行高、列数都有关系,但五万行这个数可以作为中等配置电脑上的一个经验起点。
6.3 升级路径上的最后一个提醒
最后提醒一件事:Ehlib 版本升级时,不要只替换 bpl 和 dcu,要把 Source 目录里的新增单元一并纳入工程版本管理。有一次升级后我漏了某个新版本的 .pas 文件,换机器编译时才暴露,排查了大半天。这类控件升级,源码目录有没有完整跟着走,决定了换环境时能不能干净复现。希望帮到你。
本文还有配套的精品资源,点击获取