简介:Delphi 12.3环境下的Ehlib控件完整发布包,面向需要高级表格、数据库网格及灵活报表展示的桌面应用开发者,尤其适合正在升级IDE版本并延续控件使用的团队。压缩包内有2000个文件,以dfm窗体布局、pas单元源码、res资源及dproj、dpk、dpr工程文件为主体,另含ico、png图标与chm帮助文档,便于编译安装和接口查阅。资源总大小约32.7MB,文件类型覆盖设计期包、运行期包和示例工程,并提供了多个可编译示例用于验证控件在12.3下的兼容性,目录结构符合组件包常见组织方式。已有60人学习,适合中高级Delphi开发者参考。可直接研读源码理解EhLib网格控件的内部实现,也能将其安装到Delphi 12.3中使用高级表格、数据排序、分组统计等功能,减少自绘表格的重复开发成本。
1. Ehlib 111015:Delphi 12.3 数据表格场景里一直缺的那层能力
Delphi 12.3 的工程里,Ehlib 111015 是一个常被老开发优先装上的增强型表格控件包。原生 DBGrid 在排序、过滤、统计栏、导出这些需求面前有多被动,用过的人都清楚,而 Ehlib 大多只需要点几个开关。这份资源包里没有一键安装程序,需要自己用 IDE 包管理器编译;但配套的帮助文档、示例工程、位图资源都在,按下面步骤走,十五分钟内能让 DBGridEh 上窗体。它解决的是数据管理类桌面项目的四件套:本地排序、字段过滤、统计栏、批量导出。适合正在用 Delphi 12.x 做 MIS 或进销存类项目的开发者,也适合准备从原生 DBGrid 迁移到成熟组件的团队。
2. Ehlib 111015 下载解压后:文件清单、安装流程与两种 IDE 的边界
拿到压缩包第一件事不是双击安装脚本,而是先把文件分清楚。这类控件包和普通绿色软件完全不同,里面的位图、帮助文件、工程配置各有各的位置,乱动反而会埋雷。我一般会先建一个纯英文目录,比如D:\Libs\EhLib111015,然后整体解压,再按类型过一遍。
2.1 解开压缩包先分类:位图、帮助与 Demo 分别在哪
压缩包里能看到不少.bmp结尾的文件,比如ViewSourceBIN.bmp、NetworkWorkgroupTrans.bmp、tutorial2.bmp、delphixe.bmp,这些不是装饰图,而是控件在 IDE 里显示的图标资源,或者源码浏览器背景素材。编译时它们会被链接进设计期包,所以解压时不要只挑.dpk和.bpl,把整个目录结构保留好,尤其是 Resources、Images 这类子目录,缺失会导致包能编译但工具栏图标空白。
| 文件 | 实际作用 |
|---|---|
| EhLibHelp.chm | 离线帮助文档,安装前建议先读一遍 |
| EHLIB.CNT | 帮助目录索引,配合 CHM 使用 |
| Project1.bpr | C++Builder 示例工程文件 |
| Project1.cpp | C++Builder 示例工程源码入口 |
| Project2.cfg | 示例工程编译配置,包含平台参数 |
| ViewSourceBIN.bmp 等位图 | IDE 工具栏或控件图标资源 |
Project1.bpr和Project2.cfg看起来不像 Delphi 文件,容易被当成杂物,实际上这是包内示例工程的 C++Builder 侧配置。如果你只用 Delphi,这两个文件不影响安装;如果团队里有人用 C++Builder,则要额外留意位宽和 VCL 组件继承关系。
2.2 Delphi 12.3 编译与安装:先挂 Library path,再编译包
常见做法是:先确认解压后的包里有源码目录,再走 IDE 的 Library 配置。不要把.bpl文件直接拷到 Windows 系统目录,那只会让多个版本互相打架。
打开 Delphi 12.3,进入
Tools > Options > Delphi Options > Library,把解压目录下的源码目录加到Library path。这里注意,路径不要带中文,也不要带空格。很多离奇编译错误都出在路径解析上。打开
Component > Install Packages,先看右侧已安装包列表里有没有 EhLib 相关项。如果有旧版本,先卸载,再点击Add按钮,指向解压目录下已编译好的设计期包.bpl。如果压缩包里没有现成的
.bpl,就要用源码编译。在 IDE 里打开源码目录中的.dpk包工程文件,一般会看到运行期包和设计期包两类。先编译运行期包,再编译设计期包,最后右键设计期包选择Install。关闭并重启 IDE,左侧组件面板里出现 EhLib 相关组件页,安装才算结束。
注意:包内没有一键安装程序,所有安装操作都要通过 IDE 的 Package 管理器完成,挂 Library path 只是第一步。编译顺序错是最常见的翻车点,先设计期包后运行期包,几乎必报依赖缺失。
2.3 C++Builder 使用者注意:.bpr 与 .cfg 对应另一条安装线
如果你拿到包后习惯性用 Delphi 打开Project1.bpr,会直接提示无法识别工程。Project1.bpr是 C++Builder 的工程文件,Project2.cfg则是编译器配置,包含目标平台、输出目录等参数。这两个文件的存在说明资源包里附带的 Demo 是跨 IDE 的,至少覆盖了 RAD Studio 下的 C++ 侧。
C++Builder 侧安装时,我一般会先确认当前项目是 32 位还是 64 位,再把.bpl安装到对应 IDE 版本。若直接在 64 位设计期里加载 32 位包,IDE 会静默跳过,组件面板里什么都看不到。另一个容易忽略的是Project2.cfg里的-LU参数,它指定了链接的运行期库,如果和实际编译选项不一致,链接阶段会报“不能解析外部符号”。
3. DBGridEh 参数配置三件套:数据接入、排序过滤、统计栏与下拉列
组件装上以后,真正的效率提升在属性配置。Ehlib 的价值不是多了一个网格控件,而是把大量前端交互能力直接塞进了设计期属性里。我每次接新项目,都会按数据接入、排序过滤、统计栏、列类型四条线逐项过一遍。
3.1 数据接入这项先做对:DataSource 与数据集选型
DBGridEh 本身只是显示层,它依旧通过DataSource读取数据。很多新手把它当成自带数据库连接的控件,这是误解。正确接法是先建数据集,再挂 DataSource,最后把 DBGridEh 的 DataSource 指过去。
FDQuery1.Connection := FDConnection1; FDQuery1.SQL.Text := 'select id, name, amount, create_date from orders order by id'; FDQuery1.Open; DataSource1.DataSet := FDQuery1; DBGridEh1.DataSource := DataSource1;这段代码的逻辑很直接:先给FDQuery1指定数据库连接和查询语句,然后Open打开数据集,最后把DataSource1和DBGridEh1串起来。DBGridEh1本身不做数据访问,所以数据集选什么类型都行,TFDQuery、TClientDataSet、TADOQuery 都能用。
参数说明:FDQuery1用 FireDAC 访问数据库,适合新工程;老项目用 ADO 也完全可以,DBGridEh 对 TDataSet 后代兼容得很好。关键点是DBGridEh1.DataSource不能为空,运行时一旦数据集关闭,网格内容会直接清空。
3.2 OptionsEh 一揽子开关:先理解排序与过滤的本地方案
Ehlib 最强的两个交互是本地排序和本地过滤。所谓本地,是指数据集已经打开的前提下,网格直接在前端完成排序和筛选,不需要重新回数据库执行 SQL。这对单表数据量在十万行以内的桌面场景非常实用。
我一般会这样设置:
DBGridEh1.SortLocal := True; DBGridEh1.FilterLocal := True; DBGridEh1.OptionsEh := DBGridEh1.OptionsEh + [dghAutoSortMarking, dghMultiSortMarking, dghIncSearch];SortLocal := True表示用户点击列头时,网格在内存中对当前数据集排序;FilterLocal := True允许用户在列头下拉框输入筛选条件,网格按当前字段值过滤。OptionsEh是集合属性,里面对应一组开关,dghAutoSortMarking负责在列头显示排序箭头,dghMultiSortMarking支持按住 Ctrl 多列排序,dghIncSearch开启输入即定位,用户敲字母直接跳到匹配行。
这里的排序和数据库索引是两码事。本地排序面向的是已经 Select 出来的结果集,如果数据量特别大,还是应该回数据库做分页和排序,不要在网格里一次性加载百万行再本地排,内存会先撑不住。
3.3 统计栏与页脚合计:不写 SQL 也能出 SUM
业务报表里最常见的底部合计,原生 DBGrid 要自己写页脚绘制,Ehlib 在属性层面直接支持。先把网格的FooterRowCount设为 1,然后对指定列设置Footer.ValueType。
DBGridEh1.FooterRowCount := 1; DBGridEh1.Columns[3].Footer.ValueType := 'SUM'; DBGridEh1.Columns[3].Footer.FieldName := 'AMOUNT'; DBGridEh1.Columns[3].Footer.DisplayFormat := '#,##0.00';FooterRowCount控制底部显示几行统计行;Footer.ValueType可选'SUM'、'COUNT'、'AVG'等内置聚合类型;FieldName指定要对哪个字段做统计;DisplayFormat则格式化成千分位小数。这套合计计算的是当前数据集内可见行,也就是说过滤之后,合计值会自动跟着筛选结果变化,不用重新 SQL 聚合。
提示:如果业务要“过滤前合计、过滤后也要合计”,可以再配合
OnGetFooterValue事件自定义统计值。系统默认合计只会作用在过滤后的行集上,理解这一点能避免不少报表数据对不上的问题。
3.4 下拉、日期、复选框:三类高频列配置一次讲清
网格里常见的下拉选择、日期选择、布尔展示,在 Ehlib 里都能通过列属性配置,不用每列画单元格编辑器。日期列一般设置Column.ButtonStyle为下拉风格,点击列单元格右侧出现小三角,弹出日历选择器。
DBGridEh1.Columns[4].ButtonStyle := cbsDropDown; DBGridEh1.Columns[4].EditButtons[0].Style := ebsEllipsis;ButtonStyle控制列的编辑按钮外观,cbsDropDown代表下拉按钮样式;接着给该列添加一个省略号编辑按钮,点击后可以弹出自定义选择窗体。布尔字段则可以在字段对象的KeyList和DisplayValues上做映射,网格会自动显示为复选框或“是/否”文本。
下拉列表还有一种更轻量的做法,直接在列属性的PickList里手动填几个静态值,适合状态字段,比如“待审核、已审核、已驳回”。这种方式不涉及数据库外键表,写死在设计期,改起来也方便。
4. 实战里的三个封装套路:基类复用、动态生成列、导出不依赖第三方控件
安装配置只是开始,真正让 Ehlib 发挥价值的是封装方式。如果每个窗体都从组件面板拖一个 DBGridEh、配一遍属性,会有大量重复操作。我习惯把通用逻辑收敛到一个基类里,这样后续新增窗体只需要继承。
4.1 统一基类封装:空数据、异常、加载状态一次管住
先创建一个继承自TForm的基窗体,放好TFDQuery、TDataSource、TDBGridEh,然后写一个统一的LoadData方法。这样所有业务窗体都走同一条数据加载路径,异常处理也集中在一处。
type TBaseGridForm = class(TForm) FDQuery1: TFDQuery; DataSource1: TDataSource; DBGridEh1: TDBGridEh; public procedure LoadData(const ASQL: string); virtual; end; implementation procedure TBaseGridForm.LoadData(const ASQL: string); begin FDQuery1.Close; FDQuery1.SQL.Text := ASQL; try FDQuery1.Open; except on E: Exception do begin DBGridEh1.DataSource := nil; MessageDlg('数据加载失败:' + E.Message, mtError, [mbOK], 0); end; end; end;这段代码的逻辑重点在异常处理。数据库连接不稳定时,Open抛出的异常如果没被捕获,窗体上会弹一个默认错误框,界面可能卡在半加载状态。这里先把DBGridEh1.DataSource置空,避免网格显示残留数据,再给用户一个明确提示。
参数说明:ASQL是调用方传入的查询语句;virtual关键字允许子类重写加载逻辑。基类里只负责通用流程,具体业务 SQL 由子窗体传入,减少重复的连接配置代码。
4.2 动态列生成:接口字段不固定时的兜底方案
有些报表接口返回的字段是动态的,设计期没法预先拖列。此时可以在数据集打开后,按照字段集合动态创建列,保证网格不会空白。
procedure TBaseGridForm.BuildColumnsByField; var i: Integer; Col: TColumnEh; begin DBGridEh1.Columns.Clear; for i := 0 to FDQuery1.FieldCount - 1 do begin Col := DBGridEh1.Columns.Add; Col.FieldName := FDQuery1.Fields[i].FieldName; Col.Title.Caption := FDQuery1.Fields[i].DisplayLabel; end; end;这里用DBGridEh1.Columns.Add创建新列,比手动TColumnEh.Create更安全,列会自动挂到网格的列集合上。FieldName指定列对应的数据集字段;Title.Caption取字段的DisplayLabel,这样字段在数据字典里配置过中文名后,网格标题自动显示中文,不需要额外翻译映射。
4.3 Excel 导出:先看帮助文档再选 API,少走弯路
Ehlib 自带导出能力,不用额外引第三方组件。官方帮助文档EhLibHelp.chm里对导出 API 有专门章节,不同小版本方法名会有差异。常见做法是先调用派生列的保存方法,再根据导出文件类型决定是否传格式参数。
procedure TBaseGridForm.ExportToExcel(const AFilePath: string); begin if DBGridEh1.DataSource = nil then raise Exception.Create('没有可导出的数据源'); DBGridEh1.SaveToXLS(AFilePath); end;先判空DataSource,避免用户没加载数据直接点导出,产生一个空表文件。SaveToXLS是较为常见的导出方法名,部分版本里叫ExportGridToFile,不确定时打开 CHM 帮助文档搜索导出关键词,以帮助页面里的签名优先。导出后建议再用ShellExecute打开目标文件,让用户立刻看到结果,体验会好很多。
5. 避坑指南:Delphi 12.3 安装使用 Ehlib 的五个翻车现场
控件包的坑一般不在功能上,而在版本匹配和安装顺序上。这里写的几条都是实际过程中反复遇到的高频问题,每一条都是现象、原因、解决三步走。
5.1 编译期报缺 dclEhLib.dcp:先运行期包后设计期包
现象:打开设计期包工程后按编译,IDE 报找不到dclEhLib.dcp或类似 DCP 文件。
原因:编译顺序反了。设计期包依赖运行期包先编译出的 DCP 文件,如果直接编译设计期包,依赖缺失就会报错。
解决:先打开运行期包工程并编译,确认输出目录生成了.dcp文件后,再编译设计期包。同时检查Tools > Options > Delphi Options > Library里是否把运行期包输出目录加进查找路径。
5.2 拖拽到窗体 IDE 闪退:多数是旧包残留或未重启
现象:安装完成后,把 DBGridEh 拖到窗体上,IDE 直接崩溃或无响应。
原因:机器上装过多个 Ehlib 版本,旧的.bpl还留在已安装包列表里,运行时多个包内同名类冲突,拖拽触发注册表加载时崩溃。
解决:先备份当前工程,打开Component > Install Packages,把旧的 EhLib 相关包全部卸载,关闭 IDE,删除临时目录里的旧编译缓存,重新打开 IDE 再安装新包。安装后必须重启一次 IDE,不要嫌麻烦。
5.3 运行后提示 Class TDBGridEh not found:看 Run-Time Packages 设置
现象:开发环境编译运行正常,把程序拷贝到别的机器后,启动报找不到TDBGridEh类。
原因:工程勾选了Build with runtime packages,生成的 EXE 不包含控件运行期代码,运行时需要对应的.bpl文件,而目标机器没有这些包。
解决:要么在Project > Options > Packages里取消运行期包勾选,让控件静态链接进 EXE;要么把依赖的.bpl一起拷贝到 EXE 同目录。维护工具类程序我会偏向取消运行期包,发布文件简单,不依赖目标机器环境。
5.4 中文变方块与字体错乱:主题和字体分开设置
现象:表头中文显示为方块,或者字体忽大忽小,看起来像换了台电脑后的渲染问题。
原因:DBGridEh 默认字体可能与系统主题不匹配,尤其是开启了深色模式或缩放比例非 100% 的机器上,旧字体无法正确处理中文。
解决:在FormCreate里统一设置DBGridEh1.Font.Name := 'Microsoft YaHei',同时单独设置DBGridEh1.Title.Font。不要只改网格同一个Font后就不管,标题字体和正文字体是两套属性。遇到深色主题还想保留网格白底的,可以把StyleElements里的seFont排除,避免主题强制改字体颜色。
5.5 Demo 的 .bpr 打不开:别在 Delphi 里硬碰 C++Builder 工程
现象:用 Delphi 打开Project1.bpr提示无法识别工程。
原因:.bpr是 C++Builder 专用工程文件,Project2.cfg也是 C++ 编译器配置,Delphi 没有对应加载逻辑。
解决:纯 Delphi 场景忽略这两个文件,直接使用帮助文档和.dpk源码。C++Builder 使用者打开工程前,先确认目标平台和当前 IDE 位宽,32 位工程不要用 64 位配置编译。这个文件和“垃圾文件”无关,是跨语言 Demo 的一部分。
6. 最后一个用得上的技巧:OnGetCellParams 做数据预警着色
状态类报表里的数据预警,一直是界面开发里最琐碎的部分。库存低于阈值要标红,金额为负要变黄,评分过低要加粗,这些如果散落在各个控件事件里,后期维护会非常难受。Ehlib 的OnGetCellParams事件就是专门解决这个问题的,它可以统一根据字段值修改当前单元格的字体和背景色。
procedure TForm1.DBGridEh1GetCellParams(Sender: TObject; Column: TColumnEh; AFont: TFont; var Background: TColor; State: TGridDrawState); begin if Column.FieldName <> 'STOCK' then Exit; if DBGridEh1.DataSource.DataSet.FieldByName('STOCK').AsInteger < 10 then begin Background := $00E8C8C8; AFont.Color := clMaroon; AFont.Style := AFont.Style + [fsBold]; end; end;这段代码先判断当前列是不是STOCK字段,不是就直接退出,避免每画一个单元格都做无谓的取值判断。然后读取当前行该字段的值,小于 10 时把背景色改为浅红,字体改成深红并加粗。State中如果包含gdSelected,说明当前行处于选中状态,这时若直接改背景色,会覆盖系统的选中色,导致看不出哪行被选中。
一个更稳妥的习惯是在事件开头加一句if gdSelected in State then Exit;,让选中行保持原有的高亮样式。这样无预警字段、选中行、正常行三条分支互不干扰。对于想要显示图标的情况,逻辑也是一样的,判断字段后设置Column.ImageIndex即可,底层走的还是同一套逐格绘制机制。
这个事件还有一个好处:不侵入数据模型。无论数据集来自查询还是内存表,显示层只做颜色映射,字段值本身不会被改动。二十几个字段的报表,只要在事件里写几个分支,就能把异常数据全部捞出来,比在每条数据加载后写循环判断要直观得多。
从那以后,我每次把 Ehlib 装进新环境,都强制先跑一遍GetCellParams的变色逻辑,它比任何功能清单都能验证包、依赖和绘制链路是否正常。希望这份从安装到避坑的记录能帮到你。
本文还有配套的精品资源,点击获取