☰
EhLib 11.0.021在Delphi 12 Athens中的安装与DBGridEh实战
2026/9/30 14:48:47 网站建设 项目流程

简介:面向 Delphi 12.3(Athens)开发者的 EhLib VCL 11.0.021 控件库安装包,用于增强数据库应用程序开发体验,尤其适合需要复杂数据表格、分组排序过滤和报表输出的 RAD 项目。包内含 1686 个文件,以 dfm 窗体、pas 源码、dcu 编译单元、res 资源、dpr 工程文件为主体,辅以 doc/README、示例数据库及编译配置文件,rar 压缩包仅 34.95MB,结构完整可直接在 IDE 中引用。目前已有 323 人学习下载。开发者拿到后可直接安装或加载源码包,在 Delphi 12.3 中调用高级 DBGrid、报表生成器及数据感知控件,组件支持 Unicode 与 FireDAC,便于构建稳定高效的数据库客户端。对希望扩展 VCL 原生控件、快速搭建数据管理界面的 Delphi 工程师而言,这是一份省去底层封装的实用工具集。

1. 为什么在 Delphi 12 Athens 里,我第一件事就是装 EhLib

如果你手里同时维护着两套 Delphi 项目,一套是早年用 TDBGrid 写的进销存,另一套是准备在 Delphi 12.3 Athens 上重构的新版管理系统,你大概能体会那种“离开 EhLib 就不知道怎么把表格做得像样”的处境。这个Delphi 12.3控件之EhLib VCL 11.0.021 for Delphi 12 Athens.rar标题背后,其实是一套 VCL 控件包里几乎所有 Delphi 开发者都会第一时间补上的组件——EhLib。做桌面数据库应用的人管它叫“数据网格天花板”,一点不过分。它能让你在 VCL 的 TDBGrid 基础上,直接获得多表头、统计行、自动筛选、单元格合并、树形展示这些生产级功能,而这些功能如果从零自己写,没个把月根本拿不下来。

当然,搜索这个包的人,多半是刚把 IDE 升级到 Delphi 12 Athens,或者新配了一台开发机。常见的问题往往不是“EhLib 好不好用”,而是“这个版本到底装不装得上、装完怎么配置、和旧项目里的 EhLib 5.x 代码差多少”。这篇文章不打算给你念说明书,而是按我自己在 Delphi 12 Athens 上从解压到跑通一个带分组统计的网格的完整路径来讲,包括哪些参数值得调、哪些坑容易让人翻车。如果你正打算在新 IDE 里重新搭一套后端管理系统,或者被迫升级老项目,这篇应该能省你一到两天的折腾时间。

2. 拿到 rar 之后先别急着装:版本匹配与包结构说明

解压之前要先想清楚一件事:你手上的EhLib VCL 11.0.021 for Delphi 12 Athens是不是完整版。EhLib 的发布包里通常包含两类安装文件,一类是二进制版(带 .bpl/.dcu),另一类是带源码的完整版。从文件名里的 11.0.021 来看,这个版本号对应的组件源码和编译包默认是匹配 Delphi 12 Athens 的,但网上下到的资源经常有人把二进制文件抽掉,只剩源码。如果你打算直接通过 IDE 的 “Open Package” 来安装,而且你选用的是英文版 Delphi 12.3,那打开EhLib 11.0目录下的*.dpk时通常能直接编译通过;如果你用的是打了中文资源包或者第三方皮肤插件的 IDE,那先得把 IDE 的第三方皮肤暂时禁用,否则编出来的包极易报资源冲突。

2.1 解压后的目录结构与每个目录是干什么的

解压后项目根目录下常见的几个子目录基本决定了你看这篇文章的下一步动作,我一般会按下面这套顺序来认目录:

  • Lib或Lib\Delphi12:放的是编译好的 .dcu 文件,如果只有 .dcu 没有 .pas,那连编译都省了,直接配库路径就能用;
  • Source:控件源码,尤其是DBGridEh系列组件在设计期的属性编辑器代码。想改行为、做二次开发的人会盯住这里;
  • Resources:多语言资源文件,比如俄文、德文、中文的菜单资源;
  • Docs:一个叫EhLibManual的 HTML 帮助文档目录,按组件分类讲用法;
  • Demos:官方示例,重点关注DBGridEh.Demo这个子目录,大部分高级用法都是从示例改出来的;
  • Packages:按 Delphi 版本分好子目录的运行时与设计时包工程。

认清目录的作用会直接影响你的安装方式。如果发现资源里只有Source和Packages,说明这是源码版,你在编译设计时包之后,IDE 组件面板上才会出现EhLib那一组控件。如果只有Lib目录,那基本是别人编译好的二进制版,只需要在 IDE 的 Library Path 里指向它就能在已有项目里引用,但组件面板上不会有新控件。

2.2 为什么说 “11.0.021” 这个版本号要认真比对

从 EhLib 官方版本演进规律来看,11.0.x 系列是专为新版 Delphi 适配而推出的维护分支,通常 11.0.021 修复了大量在 Win11 高分屏下出现的绘制闪烁和字体缩放问题。这里的关键点在于“适配 Delphi 12 Athens”不是一句空话:Delphi 12 的 VCL 中对 TStyleManager、HighDPI、以及 Win11 圆角窗口支持都做了改动,旧版 EhLib 的绘制代码会直接导致设计期假死或运行期界面发虚。

在动手装之前,建议先在 IDE 里点Help -> About确认你的 Delphi 版本号。标题里写了 Delphi 12.3,也就是说你至少得是 12.0 以上,如果还是 11.3 或者 10.4,那这个包编译时会报E2009之类的类型不兼容错误。

提示:最稳妥的办法是先在虚拟机上装一个裸的 Delphi 12 Athens,把 EhLib 放进去编译一次。这样能排除是 IDE 环境脏导致的安装失败。

2.3 先确认你的 IDE 是 32 位还是 64 位安装

Delphi 12 的 IDE 分 32 位和 64 位两种安装形态,但 IDE 位数跟你项目编译目标是两回事。EhLib 的设计时包必须和 IDE 位数一致,也就是说你装在 64 位 IDE 上就要编译 64 位的设计时包,否则 IDE 加载不了。网上下的这个包如果默认只有Win32的预编译 bpl,那你在 64 位 IDE 上打开包重新编译一次是最省事的。

我见过有人在 64 位 IDE 上手动把库路径指向 32 位的 bpl,结果一开窗体就说Can't load package ... 找不到指定的模块。这不是起源代码问题,只是位数不匹配。正确做法是打开 Packages 里的EhLibDesignTime64.dpk重新编译,IDE 才会生成 64 位可用的设计时包。

注意:如果你的项目里有第三方皮肤控件(比如 AlphaControls、SkinEngine),在编译 EhLib 设计时包之前,先把皮肤组件的设计时包暂停加载。两者会在 IDE 的资源句柄管理上打架,导致窗体设计器一拖拽控件就崩溃。我自己的习惯是,装完 EhLib 之后再把皮肤包加回来,顺序颠倒很容易出现莫名其妙的Access Violation。

2.4 二进制版和源码版,选哪种装法

选型的逻辑很简单:如果只是想在项目里直接用,且包里有对应 Delphi 12 的 .dcu,那就选二进制版,效率最高;如果是要修改控件源码,或者要在调试时单步跟踪到 EhLib 内部,那就必须用源码版。

源码版安装时有一个值得留神的地方:Tools -> Options -> Library里的路径顺序会影响编译结果。Delphi 编译时按 Library Path 从前到后找 .dcu,一旦前面存在旧版本 EhLib 的 .dcu,新代码里调用了新属性就会报F2063之类的错误,而你根本想不通是哪出的问题。所以装新版之前,把旧版本的路径整个从 Library Path 里删除再重启 IDE,这一步别偷懒。

3. 用 Package 把 EhLib 装进 Delphi 12 Athens:完整编译与安装步骤

上一章解决了“装之前要认清什么”的问题,这一章直接讲安装动作本身。安装的核心动作是编译并安装两个包:运行时包(Runtime Package)和设计时包(DesignTime Package)。运行时包是被你的程序引用、最终随 exe 发布的组件逻辑,设计时包则是让 IDE 在设计期把控件显示在组件面板上并生成代码的桥梁。

3.1 在“包管理器”里打开运行时包并编译

启动 Delphi 12 Athens 之后,通过菜单File -> Open找到Packages\Delphi12\EhLibRuntime.dpk并打开。打开之后,项目管理器里会出现这个包。此时不要直接Install,先点右键Compile。编译的作用是把组件源码先转成 .dcu 和 .bpl,如果源码与 Delphi 12 的 RTL 存在不兼容,这一步就会报错。

编译通过后再右键Install,此时会弹出确认对话框,告诉你“Package ... 已安装”。但注意,运行时包安装与否并不决定组件面板上是否出现控件,它只是让你项目在编译链接时能找到组件实现。

// 这一步没有业务代码,只在 IDE 包管理器里操作 // 编译输出位置默认在 Delphi 的 Projects\Bpl 目录 // 如果这里报了 E2209 或 E2009,通常是源码里用了不兼容的语法 // 处理方式:确认 Delphi 12 的 Update 版本已打全,再重新 Build

编译时的参数值得单独看一眼。在Project -> Options -> Delphi Compiler -> Compiling里,确认Runtime packages是勾选状态。如果你要做的项目最终希望 exe 尽量独立、方便拷贝到别的机器上跑,那可以把运行时包改成“静态链接”(即不使用 bpl,而是把组件代码直接编进 exe)。但这里我不建议这样做:静态链接会让每个使用 EhLib 的 exe 都膨胀 5-10MB,而且如果你同时维护多个程序,改一个控件 Bug 就得全部重编。动态链接 bpl 虽然分发时要带上 EhLib 的 bpl 文件,但调试和升级方便得多。

3.2 安装设计时包:这一步才是组件面板出现 EhLib 页的关键

运行时包编译通过后,接着打开Packages\Delphi12\EhLibDesignTime.dpk。同样先 Compile。这个包负责把 DBGridEh 等组件注册到 IDE 的组件面板。编译成功后右键Install,稍等片刻,IDE 左侧组件面板最右边会出现一个新的选项卡,名称就叫EhLib。

这里常遇到的一个情况是:编译设计时包时报E2081或F2613,原因是设计时包依赖了运行时包的 .dcu,但 IDE 的 Library Path 没先指向运行时包的输出目录。解决方式是把上一节编译运行时包产生的目录加到Tools -> Options -> Library -> Library Path中,然后再编译设计时包。

提示:如果你之前安装过 EhLib 的旧版本,而且是用 Install 方式装到同一个 IDE,那么安装完 11.0.021 后,先到组件面板上找一个旧版本残留的组件(比如 MemTableEh),拖一个到窗体上,看设计器能不能正常打开。如果报“Property 'xxx' not found”,说明旧组件的设计时包还在 IDE 中,两个版本的资源冲突了。这时要去Components -> Install Packages里把旧的 EhLib 设计时包取消勾选,重启 IDE 再来一次。

3.3 把运行库路径固定下来:三个路径缺一不可

安装完成后,为了后续每个项目都能正常引用 EhLib,必须把以下路径配置到 Delphi 的全局 Library Path 中。路径按你实际解压目录为准,我假设解压在D:\Libs\EhLib:

  • D:\Libs\EhLib\Lib\Delphi12:放 .dcu 文件,这是编译项目时最简单的一步;
  • D:\Libs\EhLib\Source:如果后续要调试代码,这个路径必须配;
  • D:\Libs\EhLib\Packages\Delphi12:编译期如果你用了“构建配置”或需要重新生成 bpl,这个路径能救命。
// 配置完路径后,写一个最小测试代码验证引用是否生效 // 新建一个 VCL 工程,在窗体上放一个 TDBGridEh(从 EhLib 组件页拖动) // 然后放一个 TDataSource、TADOQuery(或 TFDQuery) // 把 TDBGridEh.DataSource 指向 DataSource,再把 DataSource.DataSet 指向查询组件 // 代码不需要写一行,运行起来能显示空表即表示引用链路打通

这里要先理解 TDBGridEh 的层级关系:它本身不连接数据库,靠DataSource属性把网格和数据集绑定起来。关键点是,在 Delphi 12 Athens 中,EhLib 的TDBGridEh.DataSource在对象检查器里默认是空值,你绑定数据集后表格才有行和列的数据来源。这一步没做对,后面所有操作都没意义。

3.4 验证版本:字段列表、编译消息和 IDE 启动速度

装完之后,我习惯跑一个极小的验证工程,目的不是测功能,而是验证这套包是否正确:

// 在 FormCreate 里读取 EhLib 版本号,用于确认加载的是 11.0.021 而非旧版 procedure TForm1.FormCreate(Sender: TObject); begin Caption := 'EhLib Version: ' + DBGridEh1.Version; // 返回类似 11.0.021 的字符串 end;

这个代码如果在编译期报Version属性不存在,说明你 IDE 的库路径里有个旧版 EhLib 的 .dcu 被优先引用了。此时打开Project -> Options -> Delphi Compiler -> Search path,看项目级搜索路径里是否还残留着旧版目录。我自己的血泪经验是,全局 Library Path 和项目 Search Path 是两个完全独立的查找序列,项目级路径优先于全局路径,所以旧项目升级时必须在项目级路径里把老 EhLib 目录删干净,否则哪怕全局路径配的是新版本,编译时依然用的是旧的 DCU。

4. 在 Delphi 12 Athens 中用 TDBGridEh 做增删改查:核心属性与数据驱动方式

安装只是开始,真正让 EhLib 值回票价的是它在表格交互上的表现。这一章直接写一个带查询、编辑、删除、批量导入的完整数据网格,代码覆盖核心场景。

4.1 配置数据集与网格联动:告别手工拼 SQL 的编辑体验

在 Delphi 12 Athens 中,用 FDQuery 连接数据库是常见姿势。要用 EhLib 实现网格内直接编辑保存,关键是把网格的Options属性里dgEditing打开,并把DataSource正确指向数据集。下面的代码展示了最小可编辑网格的配置:

procedure TMainForm.SetupGrid; begin // 1. 先配置数据集 FDQuery1.Connection := FDConnection1; FDQuery1.SQL.Text := 'SELECT * FROM orders WHERE order_date > :pDate ORDER BY order_date DESC'; FDQuery1.ParamByName('pDate').AsDate := Date - 30; FDQuery1.Open; // 2. 再绑定网格 DBGridEh1.DataSource := DataSource1; DataSource1.DataSet := FDQuery1; // 3. EhLib 网格关键属性 DBGridEh1.ReadOnly := False; // 允许编辑 DBGridEh1.Options := DBGridEh1.Options + [dgEditing, dgAlwaysShowSelection]; DBGridEh1.ColumnDefValues.AutoFitColWidth := True; // 列宽自适应内容 end;

这里的ColumnDefValues.AutoFitColWidth是个很好用的属性,它能让列宽根据内容长度自动调整。但要注意,这个属性只在网格初始化时生效一次。如果数据集在运行期通过Open反复刷新,列宽不会重新自适应。想每次刷新都自动调宽,需要放在AfterOpen事件里重新设置DBGridEh1.OptimizeAllColsWidth。

4.2 用内置筛选器和统计行替代手工查询界面

EhLib 的表格自带一个类似 Excel 的自动筛选功能,它的实现方式是在列头下方生成一个下拉箭头,点开后按列过滤。这比你自己在窗体顶部堆一排 TEdit+Button 要高效得多。

procedure TMainForm.SetupFilter; begin DBGridEh1.STFilter.Visible := True; // 显示筛选行(列头下的那一行小格子) DBGridEh1.STFilter.Local := True; // 本地过滤,不重新查询数据库 DBGridEh1.STFilter.Mode := frmIn; // 匹配方式:包含,而不是精确相等 // 如果希望在筛选时同步刷新合计行: DBGridEh1.SumList.Active := True; DBGridEh1.SumList.SumColumnList.Add('quantity', smSum); // 对 quantity 列做求和 end;

值得说道说道的是STFilter.Local:当它为 True 时,筛选只在当前已加载的数据集范围内进行。如果数据量大且只有几万行,这个没问题;但如果数据集有几十万行,本地筛选也能撑住,因为它内部用索引查找。如果为 False,EhLib 会把过滤条件拼到 SQL 里重新查询数据集,这时候要求DataSet是支持参数的,而且 EhLib 会调用DataSet.Filtered := True配合OnFilterRecord事件。对于新手,我建议先用 Local 模式,等逻辑稳定了再考虑下推 SQL。

统计行的配置是另一大卖点。基础用法就是在DBGridEh1.SumList.SumColumnList里按列加统计类型。实际使用中我习惯对“金额”列用smSum,“单价”列用smAvg,“备注”列用smCount。这里要留意的是,统计行读取的是原始数据类型,如果你在这列上套了显示格式(DisplayFormat),统计结果也会按格式显示,不会额外保留小数精度。

4.3 在主从表架构中的网格联动:下拉框与关联字段显示

主从表是进销存类系统的常态。EhLib 处理这类场景有两个常用技巧:一是用KeyList与PickList组合给外键列做下拉选择;二是直接用TDBGridEh的多层数据展示能力。

procedure TMainForm.SetupLookup; begin // 给“客户ID”列加下拉选择,但显示客户名称 with DBGridEh1.Columns.Add do begin FieldName := 'customer_id'; Title.Caption := '客户'; // KeyList 存的是实际写入数据库的 ID 值 KeyList := TStringList.Create; KeyList.Add('1'); KeyList.Add('2'); KeyList.Add('3'); // PickList 存的是下拉列表里看到的内容 PickList.Add('北京分公司'); PickList.Add('上海分公司'); PickList.Add('广州分公司'); // 保存值时自动回写到 FDQuery1.FieldByName('customer_id') EditLimit := -1; // 禁止手输,只允许下拉选择 end; end;

这种做法适合客户表数据量小且基本不变的场景,比如只有几十个固定选项。如果客户表有上千条记录,再往 KeyList 里塞就太蠢了,那就要用TDBLookupComboboxEh配合查询数据集来做:

// 放一个 TDBLookupComboboxEh 在 DBGridEh 的列编辑器中 DBLookupComboboxEh1.KeyField := 'id'; // 实际存储的外键字段 DBLookupComboboxEh1.ListSource := CustomerDS; // 客户表数据源 DBLookupComboboxEh1.ListField := 'company_name'; // 下拉框里显示的名称 DBLookupComboboxEh1.DataSource := DataSource1; DBLookupComboboxEh1.DataField := 'customer_id';

这里踩坑最多的一个点:ListField可以一次写多个字段,用分号分隔,比如'company_name;contact_person',但此时下拉框每次刷新查询可能把列顺序打乱。如果发现关联数据显示错位,先检查ListField里字段顺序与DropDownList列设置是否一致。

4.4 数据保存时的并发冲突处理与事务控制

EhLib 网格内置编辑能力,修改后可以用Post提交,但并发控制仍要自己做。Delphi 12 Athens 下配合 FDQuery 保存,一个稳妥写法是放到事务里:

procedure TMainForm.ButtonSaveClick(Sender: TObject); begin if not FDConnection1.InTransaction then FDConnection1.StartTransaction; try // 检查当前是否有编辑未提交的行 if FDQuery1.State in [dsEdit, dsInsert] then FDQuery1.Post; FDConnection1.Commit; except FDConnection1.Rollback; raise; end; end;

如果数据库里某一行已经被别人改过,而你基于旧数据做修改并Post,这时 FDQuery 会抛一个“记录已被另一个用户修改”的异常,取决于驱动版本可能提示Records changed by another user。EhLib 配合这个写法的处理逻辑是,在BeforePost事件里记录原始值,在 post 失败时比较字段值,如果冲突字段正好是你要改的字段,那就强行覆盖。

5. 避坑笔记:EhLib 在 Delphi 12 Athens 里最常翻车的五个场景

这一章是我在升级到 Delphi 12 之后总结的最容易踩的坑,每条都按“现象 -> 原因 -> 解决”结构来写,照方抓药即可。

5.1 现象:编译通过但运行时弹出 “Cannot focus a disabled or invisible window”

这个错误让人头大,因为它不是在设计期出现的,而且崩溃点在TDBGridEh的单元格进入编辑状态时。常见场景是窗体刚打开,程序自动把焦点定位到某个单元格,但这个单元格所在的列或行在某个Visible或ReadOnly属性配合下处于不可编辑也不可聚焦状态。

原因出在网格的Options与列的ReadOnly配置互相矛盾。例如全局dgEditing是打开的,但某一列的ReadOnly := True,当用户用 Tab 键把焦点切到这一列时,VCL 底层试图让单元格进入编辑态,而列又禁止编辑,于是焦点管理就炸了。

解决方式比较直接:把列级ReadOnly改为DBGridEh1.Columns[i].EditButtons相关配置,或者确保在设计期把该列的ReadOnly设为 True 时,同时把网格的Options里dgEditorShowMode改为emShow。如果你用的是代码动态建列,那就在建列时统一判断:

if MyColumn.ReadOnly then DBGridEh1.Options := DBGridEh1.Options - [dgEditing];

5.2 现象:多表头打印导出到 Excel 后表头错位或变空白

EhLib 的多表头(MultiTitle)在屏幕上显示没问题,但用DBGridEh_ExcelExport导出时,Excel 文件里合并单元格的表头会错位。尤其在跨列合并后紧跟一个不合并的列,导出结果那列表头与数据列对不上。

原因在于导出组件TDBGridEhExportAsText或TDBGridEhExportToExcel的多表头处理逻辑和你手动设置的Title.Height有关。EhLib 按标题的实际行数去写 Excel 的合并单元格,如果你自己改了Title.Height但又没设置MultiTitle := True,那导出的只是平面表头,多表头信息直接丢失。

解决方法是先确认设计期DBGridEh1.Title.MultiTitle := True,然后调用导出前强制刷新一次标题尺寸:

DBGridEh1.Title.Height := 35; // 两行表头建议至少 35 像素 DBGridEh1.Title.MultiTitle := True; DBGridEh1.OptimizeAllColsWidth; DBGridEhExportToExcel...

5.3 现象:滚动时表格内容闪烁或出现残影

这是升级到 Delphi 12 Athens 后比较常见的问题,特别是系统开启了 Windows 的“动画效果”或用了较新的显卡驱动时。EhLib 在滚动时如果启用了VertScrollBar的平滑滚动,某些机器上会出现控件底部残留上一帧图像。

原因在于DBGridEh1.OptionsEh中有一个dcehShowHourglass和滚动缓冲机制,在高 DPI 125% 或 150% 缩放下,GDI 位图缓存没有完全失效,导致残影。这不是 EhLib Bug,更多是绘制缓存跟不上滚动频率。

我的习惯是关闭那个高代价的平滑滚动,把网格还原成“普通滚动”,对绝大多数管理软件来说手感不会有太大差别,但稳定很多:

DBGridEh1.OptionsEh := DBGridEh1.OptionsEh - [dcehSmoothVertScroll]; DBGridEh1.OptionsEh := DBGridEh1.OptionsEh - [dcehHorSmoothScroll];

5.4 现象:窗体设计器打开时 IDE 崩溃,错误指向 “EAccessViolation in DCL_EHLIB”

这个报错基本就是设计时包与 IDE 的组件类型信息不同步。尤其当你之前在 IDE 里安装过旧版 EhLib,然后没卸载直接覆盖安装新版本时,设计时包里的RegisterComponents注册表项残留了旧的组件类名。

解决方法是彻底清理:在Components -> Install Packages里,把 EhLib 设计时包前面对勾取消掉,重启 IDE,然后删除 bpl 目录下残留的dclEhLib*.bpl,重新编译安装。如果还报错,那就是 IDE 自身缓存问题,删除%APPDATA%\Embarcadero\BDS\22.0\KnownIDEpackages里与 EhLib 相关的键值,但做之前记得备份。

5.5 现象:运行时发现中文字体发虚,列宽显示特别宽或特别窄

EhLib 在 Delphi 12 Athens 下默认字体与旧 Delphi 不同,如果你在设计机(Win11 高DPI)上开发,然后程序部署到 Win10 旧机器或远程桌面里,网格字体渲染变化会导致列宽错位。

原因是 EhLib 的ColumnDefValues.AutoFitColWidth依据的是当前字体测量,而远程桌面下字体替换后测量结果完全不同。解决办法是给网格指定一个跨平台稳定的字体,并关闭 EhLib 的“设计期字体映射”:

DBGridEh1.Font.Name := 'Microsoft YaHei UI'; DBGridEh1.Font.Size := 9; DBGridEh1.ColumnDefValues.AutoFitColWidth := False;

同时,把DBGridEh1.ParentFont设为 False,否则网格会跟随窗体字体变化,那样你在 Windows 显示设置里一调缩放比例,整个表格字号跟着变,列宽很容易错乱。

6. 让 DBGridEh 跑得更顺手:三个进阶技巧与一个验证习惯

聊完避坑,这一章写点真正能提升开发效率的进阶用法,包括把常用设置固化成默认模板、用代码批量生成列,以及用关键性能指标确认你的网格在当前配置下没有“白费力气”。

6.1 把列配置做成模板,几行代码复用整套网格样式

在项目中经常出现多个窗体使用相同风格的网格:同样的字体、同样的行高、同样的筛选行可见性。如果把每个窗体都拖一遍控件再手动设一遍属性,既慢又不一致。我的做法是在公共单元里写一个过程,统一配置。

procedure ConfigureGridEh(Grid: TDBGridEh); begin Grid.Font.Name := 'Microsoft YaHei UI'; Grid.Font.Size := 9; Grid.RowHeight := 24; Grid.Title.Font.Name := 'Microsoft YaHei UI'; Grid.Title.Font.Size := 9; Grid.Title.Height := 26; Grid.STFilter.Visible := True; Grid.STFilter.Local := True; Grid.OptionsEh := Grid.OptionsEh + [dcehShowTitleHint]; // 行高在高分屏下建议固定,不要用 AutoHeight end;

这样每个窗体只要在FormCreate里调用ConfigureGridEh(DBGridEh1),就能保证风格一致。关键点是RowHeight要设为偶数,在 125% 缩放下如果行高是 23,字体渲染经常被截掉像素,改成 24 就干净了。

6.2 动态创建列并绑定字段:让数据字典驱动网格生成

老项目里你可能有几十个表要展示,手工拖列不现实。可以用数据字典(字段名数组)驱动列生成:

procedure AddGridColumns(Grid: TDBGridEh; const AFields: array of string); var i: Integer; col: TColumnEh; begin Grid.Columns.Clear; for i := Low(AFields) to High(AFields) do begin col := Grid.Columns.Add; col.FieldName := AFields[i]; col.Title.Caption := AFields[i]; col.Title.TitleButton := True; // 允许点击表头排序 if AFields[i] = 'amount' then col.DisplayFormat := '#,##0.00'; end; end;

配合FDQuery的字段列表,这套代码能在一秒内生成几十列的网格。但要注意:FieldName对性能影响较大。每列在刷新时都要通过 FieldName 去数据集里查找字段对象,如果你有 50 列以上且数据集每次刷新上万行,建议改为把列关联到字段对象的引用上,即col.Field := FDQuery1.FieldByName(AFields[i]),这样运行时连查找都省了。

6.3 判断网格是否真正把数据加载完:状态区与进度回调

大数据量下,怎么验证网格在可接受时间内完成加载?我的判断标准是“从 SQL 打开到界面可交互,耗时不超过 0.5 秒”。如果超过这个时间,先看是不是 SQL 慢,再看是不是 EhLib 在计算统计行时拖慢了。统计行本身在每次数据集滚动时都要扫描当前可见行,数据越多越耗时。

// 在 BeforeOpen 和 AfterOpen 里记录耗时 FDQuery1.BeforeOpen := FDQuery1BeforeOpen; FDQuery1.AfterOpen := FDQuery1AfterOpen; procedure TMainForm.FDQuery1BeforeOpen(DataSet: TDataSet); begin FStartTick := GetTickCount; end; procedure TMainForm.FDQuery1AfterOpen(DataSet: TDataSet); begin Caption := 'Load Time: ' + IntToStr(GetTickCount - FStartTick) + ' ms'; end;

这个习惯能帮你快速定位是“数据库慢”还是“网格渲染慢”。如果 SQL 打开耗时很短,而窗口迟迟不响应,那就去查TDBGridEh的SumList.Active是否在数据量极大的情况下仍在实时计算。这种情况下可以关闭统计行,或者在 OnScroll 事件里把统计行的计算改成手动刷新。

至于单元格固定列(FrozenCols)的用法,在列数多的时候确实管用,但建议冻结列数不要超过 2 列,而且冻结列与普通列的边框绘制在 125% 缩放下偶有 1 像素错位。如果项目要求视觉对齐,冻结列尽量放在最左侧且不要加粗,否则错位更明显。

这套方案的最终价值,不只是省下自己造轮子的功夫,而是让业务代码和表格交互剥离——你的窗体只需要关注数据逻辑,剩下的显示、筛选、统计全交给 EhLib 处理。当然,别指望装完控件后什么都不调就能无缝对接老项目,特别是从 5.x 迁移过来的工程,属性名变化和事件参数变化都值得花一个下午认真过一遍。希望这篇能帮你少走几步弯路,折腾得顺利一点。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询