简介:EhLib 10.0.031 是面向 Delphi 11 开发者的第三方组件库资源,适合需要快速构建图形处理、数据库应用与报表系统的中高级开发者。该版本已在 Delphi 11.1.5 环境下验证可正常安装使用,核心价值在于提供图像查看与格式转换、多数据库连接与查询构建、报表设计预览打印导出、图表可视化以及工具栏菜单等界面增强组件,并附带完整源码便于二次开发与功能扩展。压缩包共 890 个文件,约 24.63MB,以 302 个 dfm 窗体文件、299 个 pas 源码单元、70 个 res 资源文件、55 个 dpr 工程文件及 30 个 dcu 编译单元为主,另含 dpk 安装包、doc/docx 手册与 mdb 示例数据库,安装程序位于 install 文件夹。目前已有 886 人学习下载,配合帮助文档与示例代码,可帮助读者快速上手组件集成、理解内部实现并应用于实际项目。
1. EhLib 10.0.031 在 Delphi 11 里到底能省多少事
上周有个做设备管理系统的朋友甩给我一个压缩包,说 Delphi 11 老项目里的表格控件一到大数据量就卡成幻灯片,问我有没有靠谱的替代方案。我打开一看,是 EhLib 10.0.031 For Delphi 11 的完整源码包。这东西在 Delphi 圈子里算是老牌选手了,专门解决 VCL 下表格、网格、数据感知控件的显示与交互问题,尤其是那种几万行数据还要带排序、过滤、合计、分组的老大难场景。它适合谁?手上维护着 Delphi 7 到 11 存量项目、又不想把界面推倒重来的从业者。这篇笔记就把我拆包、编译、跑通、踩坑的全过程摊开讲,重点说清楚 EhLib Delphi 这套东西在 Delphi 11 下的实际表现和参数怎么调。
2. 拆包与安装:从源码目录到 IDE 里能拖控件
拿到压缩包先别急着双击安装程序,EhLib 的包结构有它自己的脾气。我一般先解压到一个不带中文、不带空格的短路径下,比如D:\Dev\EhLib1003,这一步看着多余,但后面编译包的时候路径里的空格会让命令行参数解析翻车,血泪经验。
2.1 目录结构与该装哪些包
解压后大致能看到这么几个关键目录,我按用途列一下:
| 目录/文件 | 用途 | 是否必须 |
|---|---|---|
Source | 核心控件源码,含网格、表格、数据感知单元 | 必须 |
Packages | 各版本 Delphi 的包工程文件 | 必须 |
Demos | 示例工程,验证安装是否成功 | 建议保留 |
Help | 帮助文档 | 可选 |
Tools | 辅助工具与资源编辑器 | 按需 |
Delphi 11 对应的是包工程里带Alexandria或11字样的那一组,别拿 Delphi 10.4 的包硬装,接口对不上会报一堆找不到单元的错。常见做法是先装运行期包再装设计期包,顺序反了 IDE 里控件面板出不来。
2.2 编译安装的具体步骤
我习惯用命令行加 IDE 双保险的方式,先确认环境变量里rsvars.bat能正常调用。下面这段是我实际用的编译脚本,放在解压根目录执行:
@echo off REM 载入 Delphi 11 编译环境,路径按本机实际安装位置改 call "C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\rsvars.bat" REM 先编译运行期包,输出到默认 BPL 目录 msbuild "Packages\EhLib110.dproj" /t:Build /p:Config=Release /p:Platform=Win32 REM 再编译设计期包,设计期包依赖运行期包 msbuild "Packages\dclEhLib110.dproj" /t:Build /p:Config=Release /p:Platform=Win32 REM 编译完成后手动在 IDE 里 Install 设计期包 pause这段脚本的逻辑很直白:rsvars.bat负责把msbuild、dcc32这些工具链塞进当前会话的 PATH,不调用它直接敲 msbuild 大概率报找不到编译器。/p:Config=Release是为了避开 Debug 配置下某些断言导致的编译中断,/p:Platform=Win32则是因为不少存量项目还是 32 位,先编 32 位验证通过再考虑 64 位。设计期包dclEhLib110.dproj编译完不会自动注册,得打开 IDE,进Component > Install Packages,把生成的 BPL 加进去,控件面板才会出现 EhLib 那一页。
提示:如果编译报「Unit not found」,九成是
Source目录没加进搜索路径。在 IDE 的Tools > Options > Language > Delphi > Library里把D:\Dev\EhLib1003\Source加进去,重启 IDE 再试。
2.3 验证安装是否真的生效
装完别急着写业务代码,先拿Demos里任意一个网格示例编译跑一下。能正常显示带排序箭头的表头、能拖动列宽、能右键弹出过滤菜单,说明运行期和设计期都到位了。我见过有人只装了运行期包,编译能过但设计器里拖不出控件,白折腾半天。验证通过后再把示例工程关掉,别留在 IDE 里,否则下次打开项目组会连带加载一堆无关的 demo。
3. 核心控件的参数调优:让大数据量表格不再卡
装好只是起点,真正决定体验的是网格控件的几个关键属性。EhLib 的表格体系里,TDBGridEh是最常用的数据感知网格,TDBVertGridEh适合纵向表单式展示,TDBSumList负责合计行。这一章挑最影响性能的几个参数讲透。
3.1 虚拟模式与数据抓取策略
默认情况下网格会把数据集里的记录尽量往内存里拉,几万行还能扛,上到几十万行就开始吃内存、滚动发涩。EhLib 提供了虚拟模式相关的开关,核心思路是「只渲染可视区域」。我一般会这么配:
// 关闭一次性全量抓取,改为按需取数 DBGridEh1.DataSource := DataSource1; DBGridEh1.Options := DBGridEh1.Options + [dgTitles, dgIndicator, dgColumnResize]; DBGridEh1.Options := DBGridEh1.Options - [dgEditing]; // 只读场景先关编辑,减少状态维护 // 关键:开启虚拟滚动相关行为 DBGridEh1.VirtualMode := True; DBGridEh1.CacheSize := 200; // 缓存行数,按内存和滚动流畅度权衡VirtualMode打开后,网格不再为每一行维护完整的显示对象,而是复用可视区域内的行,滚动时动态绑定数据。CacheSize是缓存窗口大小,设太小滚动时会频繁回源取数,设太大又回到吃内存的老路,200 到 500 之间是比较稳的区间,具体看单行字段多少。这里有个容易忽略的点:虚拟模式下自定义单元格绘制要走OnGetCellParams之类的回调,别在OnDrawColumnCell里做重活,否则滚动照样卡。
3.2 排序、过滤与合计的联动配置
业务方最爱提的需求就是「点表头排序、按列过滤、底部看合计」,这三件事在 EhLib 里各有开关,配不好会互相打架。排序靠OptionsEh里的dghAutoSortMarking,过滤靠STFilter相关属性,合计则要挂TDBSumList。我通常这样组织:
// 允许点击表头排序并显示排序标记 DBGridEh1.OptionsEh := DBGridEh1.OptionsEh + [dghAutoSortMarking, dghMultiSortMarking]; // 开启列头下拉过滤,Local 表示在本地数据集上过滤 DBGridEh1.STFilter.Visible := True; DBGridEh1.STFilter.Location := stflInTitleFilterBox; // 合计行绑定到独立的合计列表组件 DBSumList1.DataSource := DataSource1; DBSumList1.Active := True;dghMultiSortMarking允许多列组合排序,按住 Shift 点表头即可,适合报表类界面。STFilter.Location设成stflInTitleFilterBox是把过滤框塞进列头里,界面更紧凑,但列宽窄的时候会挤,列多的话考虑换成弹出式。合计这块要注意:TDBSumList是对当前数据集做聚合,如果数据集本身是分页取数的,合计只会算当前页,想要全量合计得在服务端或另开一个统计查询,这个坑我在项目里踩过,页面上显示的总数对不上,查了半天才发现是分页的锅。
3.3 外观与高 DPI 适配
Delphi 11 对高 DPI 的支持比老版本好,但第三方控件不一定跟得上。EhLib 的网格在 125% 或 150% 缩放下容易出现行高错位、表头文字被裁。我的处理办法是在窗体创建时按当前 DPI 动态调行高和字体:
procedure TForm1.FormCreate(Sender: TObject); var ScaleFactor: Double; begin ScaleFactor := Screen.PixelsPerInch / 96.0; DBGridEh1.RowHeight := Round(22 * ScaleFactor); DBGridEh1.TitleHeight := Round(26 * ScaleFactor); DBGridEh1.Font.Size := Round(9 * ScaleFactor); end;Screen.PixelsPerInch拿到当前屏幕的 DPI,除以 96 得到缩放比例,行高、表头高、字号都按比例放大。这只是权宜之计,更彻底的做法是在项目选项里开启 Per-Monitor V2 感知,再配合控件自身的 DPI 属性,但存量项目改这个动静大,先用运行时缩放顶住。字号别用固定像素,用Font.Size让 VCL 自己换算,能少踩一半的坑。
4. 避坑与排查:那些装完才发现的坑
这一章是我和身边几个维护 Delphi 老项目的同行攒出来的踩坑记录,每条都按现象、原因、解决三段写,照着排查能省不少时间。
4.1 编译通过但运行时找不到 BPL
现象:程序在自己机器上跑得好好的,拷到客户机上双击就弹「找不到 xxx.bpl」。原因:设计期包被误当运行期依赖打进了发布清单,或者运行期 BPL 没随 exe 一起拷。解决:发布时只带运行期 BPL,设计期包永远不进客户机;更稳妥的做法是把运行期包编成静态链接进 exe,项目选项里关掉「Build with runtime packages」,虽然 exe 变大,但部署省心。
4.2 网格滚动时闪烁严重
现象:数据量一大,拖动滚动条整片区域闪白。原因:默认双缓冲没开,加上自定义绘制里做了耗时操作。解决:给网格所在窗体开DoubleBuffered := True,同时把OnDrawColumnCell里能提前算好的东西挪到数据加载阶段,绘制回调里只做赋值。还不行就检查是不是在滚动事件里触发了数据集Refresh,那等于每滚一下重查一次。
4.3 过滤后合计行数字不变
现象:列头过滤生效了,但底部合计还是全量数字。原因:TDBSumList绑定的是原始数据集,过滤走的是网格层的显示过滤,两者不同步。解决:要么把过滤下沉到数据集层(用Filter属性),让合计跟着数据集走;要么在过滤事件里手动重算合计并刷新显示。前者改动小,后者灵活,看业务能不能接受数据集层过滤的性能开销。
4.4 64 位编译报指针截断警告
现象:切到 Win64 平台编译,一堆「Pointer truncation」警告,运行偶发崩溃。原因:源码里有些老式类型转换按 32 位写的,指针当 Integer 用。解决:优先用官方较新的包版本,老版本源码里这类问题得逐个改,把Integer换成NativeInt、Cardinal换成NativeUInt。改之前先备份,改完跑一遍示例工程回归,别只编译通过就完事。
4.5 设计器里控件图标丢失
现象:控件面板里 EhLib 那一页有名字但没图标,拖出来是空白。原因:资源 DLL 或图标资源没注册,常见于只拷了 BPL 没拷配套资源文件。解决:确认安装时资源包也一并注册,IDE 里Install Packages列表里该有的条目一个不能少;实在不行重装一遍设计期包,别手动往目录里塞文件。
5. 进阶用法:把网格导出与打印做成可复用单元
装好、调优、避坑都走完,最后落到一个实际高频需求上:把网格内容导出成 Excel 或直接打印。EhLib 自带导出和打印的支撑单元,但直接调容易和业务代码缠在一起,我一般封装成一个独立单元,界面层只传网格引用和文件名。
unit GridExportHelper; interface uses DBGridEh, SysUtils; // 导出当前网格可视列到 CSV,简单可靠,不依赖 Office function ExportGridToCSV(Grid: TDBGridEh; const FileName: string): Boolean; implementation function ExportGridToCSV(Grid: TDBGridEh; const FileName: string): Boolean; var Lines: TStringList; Row, Col: Integer; Line: string; begin Result := False; Lines := TStringList.Create; try // 先写表头,跳过隐藏列 Line := ''; for Col := 0 to Grid.Columns.Count - 1 do if Grid.Columns[Col].Visible then Line := Line + Grid.Columns[Col].Title.Caption + ','; Lines.Add(Line); // 逐行取显示值,而不是原始字段值,保证和界面一致 Grid.DataSource.DataSet.DisableControls; try Grid.DataSource.DataSet.First; while not Grid.DataSource.DataSet.Eof do begin Line := ''; for Col := 0 to Grid.Columns.Count - 1 do if Grid.Columns[Col].Visible then Line := Line + Grid.Columns[Col].DisplayText + ','; Lines.Add(Line); Grid.DataSource.DataSet.Next; end; finally Grid.DataSource.DataSet.EnableControls; end; Lines.SaveToFile(FileName); Result := True; finally Lines.Free; end; end; end.这段代码的关键点在两个地方。一是用DisplayText而不是字段原始值,这样界面上看到的格式化结果(比如日期、金额千分位)能原样导出,业务方核对时不会对不上。二是遍历期间DisableControls,避免每移动一条记录网格都重绘一次,几万行导出能快好几倍。CSV 用逗号分隔,字段里本身带逗号的情况这里没处理,真实项目里得加引号转义,我留了个口子没写全,算是给需要的人提个醒。
打印这块,EhLib 的打印预览组件能直接挂网格,但分页和列宽自适应要手动调。我的习惯是先设好PrintOptions里的标题行重复和缩放模式,再在预览里目测一遍列有没有被截断,确认没问题再交给业务方。导出和打印都建议做成独立单元,别塞进窗体代码,后面换控件或加格式(比如导出 xlsx)时改动面小。
从那以后我每次接 Delphi 老项目,装第三方控件包都强制走一遍「命令行编译 + 示例验证 + 发布清单核对」这三步,宁可多花半小时,也不想到客户现场再翻车。希望帮到你。
本文还有配套的精品资源,点击获取