简介:这是一套面向 Delphi 开发者的 TMS VCL UI Pack 13.5.9.0 控件资源包,专为 Delphi 13.1 环境优化,适合需要快速搭建专业 Windows/Linux 桌面应用界面的中高级程序员。组件库涵盖按钮、面板、树形列表、工具栏、数据网格、图表等常用控件,并支持主题、皮肤和丰富的自定义属性,开发者可借其实现风格统一的现代化 UI,大幅减少界面层重复编码。包内共有 2000 个文件,以 pas 源文件、dfm 表单、dproj/dpr 工程文件为主,还包含大量 res/ico/png 图标资源、pdf/rtf/htm 说明文档以及演示项目,既可用于直接集成,也方便对照源码学习控件定制技巧;压缩包整体约 104.63 MB。目前已有 39 人学习/下载。这套组件包还带有向导与编辑器,可辅助完成复杂布局和设计时配置,对提升 Delphi 项目界面开发效率很有帮助,也适合希望深入理解 VCL 控件机制的开发者作为参考。
1. 拿到 TMS VCL UI Pack 13.5.9.0 的 7z 包后,先别急着双击解压
手上有 Delphi 13.1 环境,又同时拿到 TMS VCL UI Pack 13.5.9.0.7z 这个压缩包的人,多半不是来尝鲜的:要么手上的 VCL 项目正缺能排序、能合并单元格的表格,要么在评估要不要在团队里正式引入一套成熟的 VCL 控件库。TMS VCL UI Pack 是 VCL 生态里覆盖很广的商用控件包,表格、编辑器、工具栏、面板、计划表一应俱全;而 .7z 后缀意味着第一步不是双击压缩包,而是先正确解压,再按 IDE 版本把设计期包注册进去。这篇笔记按我实际装过、用过的顺序讲:从解压、注册进 Delphi 13.1、挑几个高频控件下手,到常见报错排查,最后是团队里复用的一套基础配置。适合刚接触 Delphi 的开发者,也适合回去翻旧项目的维护者。
2. 解压与目录规划:7z 格式、路径规则、IDE 位宽先把环境钉死
2.1 为什么压缩包是 7z 而不是 zip:格式与工具
TMS 的发行包用 7z 压缩,最直接的原因是压缩率比 zip 高,一个塞了几十个 Demo、全套源码和三套平台包的目录能省出不少体积。但代价是 Windows 资源管理器自带的压缩支持对这个格式无效,你双击它只会看到"无法打开文件"。这不是压缩包损坏,是工具不对。
常见做法是装 7-Zip 或 WinRAR,然后用右键菜单解压。我习惯用 7-Zip 的命令行,方便在批处理里反复验证同一个路径规则,也方便写进团队文档。解压命令长这样:
7z x D:\Downloads\TMS_VCL_UI_Pack_13.5.9.0.7z -oD:\Dev\Components\TMSPack -y参数说明:x表示解压并保留原目录结构;-o后面直接跟目标路径,中间不要留空格,这是 7-Zip 命令行最容易踩的细节;-y是全部确认,避免解压中停下来问你"是否覆盖"。如果压缩包带了密码,作者会在发布说明里写,解压时命令行会提示输入,普通渠道拿到的包通常不需要密码。
解压完成后先看一眼输出目录,如果里面还有一层同名文件夹,说明我的-o路径给深了一层。这种嵌套最容易在 IDE 配 Library path 时把路径搞乱,后面File not found: AdvGrid.dcu的坑有一半是从这里开始的。
2.2 解压到哪个目录:路径规则和我的习惯
路径规划这件事看着不起眼,实际影响后面所有环节。Delphi 的 Library path 会把目录里的.dcu编进索引,路径一旦带中文、带空格、带特殊符号,轻则在 IDE 里显示乱码,重则编译时"File not found"或"Internal error"。我踩过一次把控件装进D:\常用工具\TMS 控件包的翻车现场,后来统一成一条规则:只允许英文字母、数字、下划线,路径里不出现空格。
我自己常用的一级目录是D:\Dev\Components\TMSPack,后面带版本号子目录区分构建。原因很实际:Delphi 的大版本之间.bpl不互通,同一台机器上可能同时装着 10.4、11、12 的项目,控件更新会互相覆盖。用版本号分目录,切 IDE 版本时不用重装,只改 Library path。
路径规划还有一个隐藏好处:幻兽帕鲁式的"装坏了大不了重来"。控件包安装过程本质是往 IDE 注册几个.bpl,目录放独立了,卸载就是移除引用、删掉整个文件夹,干净利落。
2.3 解压后先核对版本:包目录结构与 IDE 位宽
解压完成后先别急着进 IDE,花两分钟核对结构。常见的 TMS 包目录一般会分成Packages、Source、Demo这几类,命名在不同构建里略有差异,但用途基本一致。我见过太多次"装完发现没有注册表残留、但 IDE 就是识别不了"的求助,查到最后都是把Runtime包当Design包装了。所以先记住这张文件类型表:
| 文件类型 | 扩展名 | 在安装中的用途 |
|---|---|---|
| 设计期包 | .bpl | 注册进 IDE,拖控件到窗体就靠它 |
| 运行时包 | .bpl | 程序运行时动态加载的库,这里存的是实现 |
| 包声明文件 | .dcp | 告诉编译器这个包里有哪些单元 |
| 编译产物 | .dcu | 编译时直接引用的二进制单元 |
| 源码 | .pas | 查实现、调试断点、看属性定义 |
| 演示工程 | .dproj/.dpr | 验证安装是否成功的第一手材料 |
另一个必须核对的是位宽。Delphi 13.1 的 IDE 本身是 32 位进程,但它可以编译出 Win32 和 Win64 两种目标;设计期包必须装成 IDE 进程能加载的那一份,也就是 Win32 变体。包里如果同时给了Win32和Win64两个目录,设计期用Win32,发布时按你目标平台选。把 Win64 的设计期包强行加载进 32 位 IDE,基本就是第 5 章里Access Violation的直接原因。
3. 把包注册进 Delphi 13.1:设计期 .bpl 安装与 library path 两条主线
3.1 先分清两种包:设计期 .bpl 与运行时 .bpl
TMS 的包安装核心是搞懂两种.bpl的区别。设计期包(Design Time Package)只在开发环境里发挥作用,负责在 IDE 的组件面板上注册控件、生成 DFM 属性编辑器;运行时包(Runtime Package)是交付时跟着 exe 走的那部分,如果工程勾选了 "Use runtime packages",目标机器上就得有对应的.bpl。
这里的血泪经验是:许多人拿到压缩包后直奔.bpl就 Add 进 IDE,结果列表里出现的是运行时包,组件面板上什么也没多出来。区分方法很简单:文件名里通常带着Design、D后缀,或在包描述里写着 "design time only"。直接把.bpl全装一遍不是不行,但会让 IDE 加载一堆用不上的包,启动变慢、偶发冲突,我从不这么干。
3.2 手工安装设计期包的具体步骤
在 Delphi 13.1 里,打开 IDE 主菜单Component > Install Packages,点击右侧Add按钮,定位到解压目录下Packages里面对应当前 IDE 版本号的子目录,选择那个设计期.bpl文件,确认回到上一级界面后,列表里会出现对应描述。
我一般会顺手把下方的Runtime packages里涉及 TMS 的项也确认一下,但第一次安装时不改动它,保持默认。然后回到窗体上,在组件面板里找一个叫 TMS 或 TMS VCL UI Pack 的分页,拖一个TAdvStringGrid到新建窗体上。能拖动、能改属性、FormCreate里能写代码引用,说明设计期包已经生效。
这里有个容易被忽略的操作:Add 完.bpl后,如果 IDE 提示 "The following packages were found but could not be loaded",先别急着找别的原因,大概率是你把当前平台或者 IDE 更新号选错了。常见做法是回到包目录,确认选的那个构建编号与 IDE 的Help > About里显示的版本更新号一致,然后换一个变体重试。
3.3 用 msbuild 验证一个最小工程能否吃到 TMS 单元
设计期包装上只是第一步,真正跑通业务代码还需要编译期能找到单元。验证最快的方式不是新建一个空工程,而是直接打开包目录里的某个 Demo,按当前 IDE 版本编译一次。但在我维护的团队里,更常用的是命令行验证,因为可以写进自动构建,也方便在换机器时快速自检。
我一般会这样跑一个最小项目的构建,确认 TMS 单元能被编译器找到、链接器能解析:
msbuild TMSDemo.dproj /t:Build /p:Configuration=Release /p:Platform=Win32参数说明:/t:Build指定 MSBuild 执行构建目标;/p:Configuration=Release和/p:Platform=Win32分别覆盖发布配置和 32 位目标平台。如果命令提示找不到msbuild,说明你不在 RAD Studio 的命令行环境里,需要从开始菜单打开RAD Studio Command Prompt,它已经帮你把编译器路径写进了环境变量。
看到Build succeeded之后,我还会顺手检查输出目录里生成的 exe 旁边有没有带上 TMS 相关的.bpl。如果没带,而工程又开着 runtime packages,那就要回头看第 5 章的运行时缺失问题。这一步能提前发现一半的交付事故。
4. 选对上手的控件:用 TAdvStringGrid 把进销存列表做厚
4.1 为什么先拿 TAdvStringGrid 试手,以及它和原生 TStringGrid 的差距
TMS VCL UI Pack 里有一百多个控件,新手最容易挑花眼。我的建议是从TAdvStringGrid开始,原因是它覆盖了桌面开发里最高频的痛点:复杂表格。原生TStringGrid在简单列表场景够用,但一旦涉及表头多行、单元格合并、按列排序、类型化编辑、合计行变色,写起来就很痛苦,代码里全是OnDrawCell和OnMouseUp的拼凑。
TAdvStringGrid继承了原生网格的绝大部分行为,所以在迁移现有代码时阻力小;而它在列的合并、对齐、排序设置上提供了更靠上的封装。以下对比只挑实际项目里被问得最多的几项:
| 能力 | 原生 TStringGrid | TAdvStringGrid |
|---|---|---|
| 固定行、固定列 | 支持 | 支持,且在属性面板里更直观 |
| 列宽拖拽、行高调整 | 支持 | 支持 |
| 按列排序 | 需要自己实现 | 属性面板里可开启 |
| 单元格合并 | 需要自绘 | 按行列设置即可 |
| 单元格类型(下拉、数字、日期等) | 几乎只能自绘 | 按列设置类型编辑器 |
| 整体视觉 | 默认偏旧 | 与 VCL 主题配合更好 |
不是说原生控件一无是处,而是当表头有三层、首列是序号、末行还得有合计的时候,原生方案的代码量会很快失控。这也是很多人装了 TMS 之后第一件事就是替换旧表格的原因。
4.2 搭一张带合计行的进销存表:最小代码
下面这段代码是我在项目里反复用到的骨架,新建一个窗体,放一个TAdvStringGrid,在OnCreate里做初始化和数据填充。它刻意只用了继承自 VCL 基础网格的属性,保证在不同构建的 TMS 版本里都能编译过。
uses AdvGrid; procedure TfrmMain.FormCreate(Sender: TObject); begin AdvStringGrid1.ColCount := 5; AdvStringGrid1.RowCount := 10; AdvStringGrid1.FixedCols := 1; AdvStringGrid1.FixedRows := 2; AdvStringGrid1.Cells[1, 0] := '商品名称'; AdvStringGrid1.Cells[2, 0] := '入库数量'; AdvStringGrid1.Cells[3, 0] := '出库数量'; AdvStringGrid1.Cells[4, 0] := '结余'; AdvStringGrid1.Cells[1, 9] := '合计'; AdvStringGrid1.Options := AdvStringGrid1.Options + [goColSizing, goRowSizing, goEditing, goAlwaysShowEditor]; end; procedure TfrmMain.AdvStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; ARect: TRect; AState: TGridDrawState); begin if ARow = 9 then begin AdvStringGrid1.Canvas.Brush.Color := $00FFE0C0; AdvStringGrid1.Canvas.Font.Style := [fsBold]; AdvStringGrid1.Canvas.FillRect(ARect); AdvStringGrid1.Canvas.TextOut(ARect.Left + 2, ARect.Top + 2, AdvStringGrid1.Cells[ACol, ARow]); end; end;逻辑说明:构造函数里先把行列数钉死,FixedRows := 2表示前两行固定不滚动,这里用来放双层表头;FixedCols := 1固定第一列,通常是序号列。最后四行操作汇总行加粗、换底色,OnDrawCell里判断到第 9 行就用自己的画笔重绘。这段代码不依赖 TMS 特有的新接口,换成TStringGrid也能编译,方便你在接入 TMS 之前先对比感受差异。
4.3 三个顺手就该调的参数:行列固定、编辑模式、列宽拖拽
第一个参数是FixedRows和FixedCols。不是所有表头都要两行,但如果你的列表有分组表头,FixedRows := 2能省掉大量自绘代码。第二个是Options。goEditing一开,用户就能双击单元格改内容,这时配合goAlwaysShowEditor会把编辑态常驻,适合做数据录入界面;如果只是展示,这两个选项都不要加。
第三个是列宽。goColSizing打开后用户能拖列宽,但拖完的宽度不会自动记住,下次启动又回到默认值。我一般会在FormClose里把列宽写进注册表或 INI,启动时再读回来。另外要提醒:goEditing开启后,如果没有给单元格设置合适的输入类型,用户填进去的任意字符串都可能破坏后续统计运算。常见做法是给数据列单独设置数字类型,或者在OnGetCellText里做格式化兜底,别把数据校验全押在数据库层。
5. 安装与编译避坑:5 个我见过最多人卡住的报错现场
5.1 双击 7z 提示"无法打开":解压工具与目标目录
现象:从网盘或官方渠道拿到TMS_VCL_UI_Pack_13.5.9.0.7z,双击后 Windows 弹窗说无法打开,甚至有人怀疑文件下载损坏。
原因:.7z用的是 LZMA 压缩算法,Windows 自带解压只支持 zip 和自解压 exe,对这个格式无能为力,文件本身大概率没坏。
解决:装 7-Zip,右键选择"解压到指定目录",或按第 2 章的命令行方式解压。解压时目标路径不要包含空格和中文,这一步直接影响后面的 Library path 配置。如果命令行解压时提示Unsupported Method,才是真压缩包损坏,重新下载并核对校验值。
5.2 装完设计期包,IDE 报 Access Violation
现象:在Component > Install Packages里 Add 了.bpl,点确定后 IDE 直接报Access Violation,或者重启后 IDE 打不开,只能禁用包再进。
原因:九成是把运行平台搞错了。设计期.bpl必须匹配 IDE 的进程架构,把 Win64 变体注册进 32 位 IDE 进程,加载即崩;另一小半原因是 IDE 更新号与包构建号不匹配,新包装进旧更新号的 IDE。
解决:卸载冲突包,回到包目录重新选择与当前 IDE 版本对应的子目录。区分办法是看文件名或目录名里的版本提示,一般Packages下会按 IDE 更新号分目录。装好后先只注册一个包,验证能拖控件了再注册其它扩展包,降低排查面。
5.3 File not found: AdvGrid.dcu:Library path 没接上
现象:代码里写了uses AdvGrid,按 F9 编译时报File not found: 'AdvGrid.dcu',但设计期包明明已经出现在 Install Packages 列表里。
原因:设计期包注册解决的是 IDE 里拖控件的问题,编译期找.dcu走的是另一条路——库路径。.dcu文件没被 IDE 索引时,代码再正确也编不过。
解决:打开Tools > Options > Language > Delphi > Library,把Library path追加 TMS 的Source目录和对应的Lib目录,分 Win32 和 Win64 各加一遍。注意别把Source和Lib混成一个路径,两个目录的作用不一样,前者放.pas后者放预先编译好的.dcu。改完路径后重启 IDE,再编译一次,这个报错基本消失。
5.4 开发机跑得好,换台机器缺 bpl
现象:开发机上编译运行一切正常,把Release的 exe 拷到客户电脑或同事机器上,双击要么弹"缺少 TMSXXX.bpl",要么直接闪退没有提示。
原因:工程开启了Build with runtime packages,exe 没有把 TMS 的实现代码链进自身,运行时才去找对应的.bpl。目标机器上没装这套运行库,自然起不来。
解决:打开Project > Options > Runtime Packages,取消Link with runtime packages或里面的勾选,重新编译。这样 TMS 代码会静态链进 exe,交付物变成一个单文件加必要资源。副作用是 exe 体积变大,但换来的是部署省心。现在我在团队里的默认约定就是:正式交付一律静态连编,开发调试再考虑运行时包。
5.5 高 DPI 发虚与杀软误报:两个容易被忽略的收尾动作
现象:高分辨率屏幕上,TMS 控件字体发虚、面板边缘对不齐;另一台机器上杀毒软件直接把解压出来的.bpl隔离。
原因:前者是进制在 DPI 感知上不一致,Windows 对没有声明 DPI 感知的程序做了位图拉伸,字体自然糊;后者是杀软对不常见签名的安装包常见误报,尤其当压缩包里带大量源码和 DLL 时更容易触发。
解决:在项目管理器里检查 DPI Awareness 是否为PerMonitorV2(Delphi 13.1 下这是默认推荐值),让每个窗体按实际显示器缩放;杀软误报则把解压目录加入白名单,并从官方渠道重新解压一次,用哈希工具核对与发布说明一致后再使用。不要为了方便临时退出杀软,那不是解决问题的态度。
6. 进阶:把 TMS 骨架化,并把每次安装装没装漏变成可验证的事
6.1 把 TMS 骨架化:一个初始化函数覆盖常用控件
当项目里十几个窗体都在用TAdvStringGrid时,逐个窗体设置字体和颜色会越改越乱。我习惯在公共单元里放一个统一入口,程序启动时遍历一次窗体上的控件,把所有 TMS 相关控件的基础样式一次性对齐:
procedure ApplyTMSStyle(AForm: TForm); var I: Integer; begin for I := 0 to AForm.ComponentCount - 1 do begin if AForm.Components[I] is TAdvStringGrid then begin TAdvStringGrid(AForm.Components[I]).Font.Name := 'Microsoft YaHei UI'; TAdvStringGrid(AForm.Components[I]).Font.Size := 9; end; end; end;逻辑说明:这个函数只处理当前窗体的直属控件,实际项目中建议改成递归遍历TWinControl的后代,并把字体名抽成全局变量,这样换主题时只改一处。统一的字体配置是团队代码审查里最不容易吵起来的公约数。
6.2 交付前的三连验证
我最后养成了一个习惯:交付前不做"我觉得没问题",而是跑一套固定自检。第一,新建一个空窗体只放一个 TMS 控件,编译并运行,确认 IDE 的包加载正常;第二,用第 3 章的msbuild命令行把工程全量构建一遍,排除 IDE 缓存造成的假成功;第三,到输出目录看 exe 旁边是否还残留 TMS 相关的.bpl,有残留就说明 runtime packages 还开着,按第 5.4 条改掉。
这套流程源自一次交付现场的教训:客户机器上界面都起来了,一操作表格就报缺失 DLL,当时查了半宿才发现是运行时包没有带走。从那以后,我的默认动作就是静态连编,并在交付单上写清构建平台和 IDE 更新号。版本升级也一样,换 IDE 大版本时重新走一遍解压、注册、验证三连,比直接复制旧配置可靠得多。希望这些经验帮你在 TMS VCL UI Pack 上少走几趟弯路,真正把这套控件变成项目里的生产力,而不是堆在组件面板上的摆设。
本文还有配套的精品资源,点击获取