简介本资源为TMS FNC UI Pack v7.1.1.0完整源码包面向使用Delphi与C Builder进行跨平台界面开发的程序员覆盖XE7至13 Florence版本。控件包提供现代化、触摸友好的UI组件支持Windows、Mac、Linux、iOS与Android多平台可帮助开发者快速构建响应迅速、风格可定制的应用界面。压缩包共约2000个文件大小31.09MB以pas源码、dproj与dpr工程文件、dfm与fmx窗体、res与ico资源、dcr组件包及html文档为主另含csv、svg、png等辅助素材结构完整便于按模块查阅。已有56人学习下载。完整源代码意味着开发者可自由修改与扩展组件配合文档能深入理解实现机制减少调试成本适合希望深度定制界面或长期维护项目的Delphi开发者参考使用。1. TMS FNC UI Pack 到底解决什么问题Delphi 13.1 跨框架控件库的选型逻辑如果你正在用 Delphi 13 Florence 做项目大概率会遇到一个尴尬局面VCL 项目里攒了一堆好用的控件想迁到 FMX 做跨平台发现全部要重写或者反过来FMX 上写好的界面逻辑想复用到 VCL 桌面端同样得推倒重来。TMS FNC UI Pack 就是冲着这个痛点来的——它是一套基于 FNCFramework Neutral Components架构的 UI 控件库同一套控件源码可以同时跑在 VCL、FMX、LCL 甚至 Web 端Delphi 和 CBuilder 的 XE7 到 13 Florence 全版本通吃。这次拿到的 v7.1.1.0 是 Full Source 版本意味着你手里有完整的 Pascal 源码不是编译好的 DCU 黑匣子遇到玄学问题可以直接跟进去看。适合谁适合那些维护着多套 UI 代码、想统一控件层、又不想被某个单一框架锁死的团队。2. FNC 架构拆解与源码包结构为什么 Full Source 比 DCU 值钱2.1 FNC 的分层设计Framework Neutral 到底中立在哪要理解 TMS FNC UI Pack 的价值得先搞清楚 FNC 架构的分层逻辑。传统 Delphi 控件要么绑 VCL要么绑 FMX控件的绘制、消息处理、输入响应全部直接调用框架 API。FNC 的做法是在中间加一层抽象最底层是 FNC 核心运行时负责定义统一的绘制接口、字体度量、鼠标键盘事件模型中间层是各框架的适配器VCL 适配器把 Windows 消息翻译成 FNC 事件FMX 适配器把 FireMonkey 的 Canvas 操作映射到 FNC 绘制指令最上层才是具体控件比如 TTMSFNCGrid、TTMSFNCTreeView、TTMSFNCRichEditor 这些。这个分层带来的直接好处是你在 VCL 项目里给 TTMSFNCGrid 设置的列宽、单元格样式、排序逻辑换到 FMX 项目里几乎原样能用因为控件本身的代码是同一份差异被适配器吃掉了。代价是性能上多了一层间接调用极端场景下比如十万行级别的表格滚动FNC 版本可能比原生 VCL 的 TStringGrid 慢一些。但实际业务里这种量级的表格本来就该上虚拟模式FNC 的虚拟模式支持是完整的所以这个代价基本可以接受。Full Source 版本的核心价值在于可调试性。DCU 版本遇到问题你只能猜比如 TTMSFNCTreeView 在某个节点展开时闪退你只能反复试参数、查文档、等官方回复。Full Source 版本你可以直接在 IDE 里 Step Into 到控件内部看它是在哪个绘制阶段、哪次节点遍历时出的问题。我一般会把 FNC 源码目录加到项目的搜索路径里编译时直接编源码而不是链接 DCU这样断点能落到具体行号。代价是首次编译时间会变长但调试效率的提升完全值得。2.2 源码包目录结构与关键文件定位拿到这个 rar 包之后先别急着往 IDE 里装。解压出来看目录结构典型的 TMS FNC UI Pack Full Source 包大概长这样TMS FNC UI Pack v7.1.1.0/ ├── Source/ │ ├── Core/ # FNC 核心运行时框架无关的基础类 │ ├── VCL/ # VCL 适配器层 │ ├── FMX/ # FMX 适配器层 │ ├── LCL/ # Lazarus 适配器层 │ └── Controls/ # 具体控件实现按控件名分目录 ├── Packages/ │ ├── Delphi13/ # 各版本对应的包工程文件 │ ├── Delphi12/ │ └── ... ├── Demos/ │ ├── VCL/ │ └── FMX/ └── Doc/关键目录是 Source/Core 和 Source/Controls。Core 里定义了 TTMSFNCCustomControl 这个基类所有 FNC 控件都从它继承。Controls 下面每个控件一个子目录比如 TTMSFNCTreeView 就在 Source/Controls/TreeView/ 里。Packages 目录下按 Delphi 版本分好了包工程Delphi 13 Florence 对应 Delphi13 子目录直接打开对应的 .dproj 编译安装就行。有个细节要注意FNC 的包是设计期包和运行期包分开的。运行期包以 r 结尾比如 TMSFNCUIPackR.dproj提供控件运行时逻辑设计期包以 d 结尾提供 IDE 里的控件面板注册和属性编辑器。安装顺序是先编译运行期包再编译设计期包顺序反了 IDE 会报找不到单元。2.3 安装到 Delphi 13.1 的具体步骤安装过程本身不复杂但有几个地方容易翻车。先确认你的 Delphi 13.1 已经装了 Update 1 或更高版本早期 13.0 的编译器对 FNC 某些泛型用法支持有问题。第一步把 Source 目录加到 IDE 的 Library Path 里。菜单 Tools → Options → Language → Delphi → Library在 Library Path 里加上 FNC 的 Source 根目录和 Source/Core 目录。这一步是为了让编译器能找到 FNC 的单元文件。第二步打开 Packages/Delphi13 下的运行期包工程右键 Build。如果报错说找不到某个 .inc 文件检查 Source 目录是否完整解压有些杀毒软件会把 .inc 当可疑文件删掉。第三步Build 通过后右键 Install 设计期包。安装成功后 IDE 的控件面板会出现 TMS FNC 分组里面能看到 TTMSFNCGrid、TTMSFNCTreeView 等控件图标。第四步验证安装。新建一个 VCL Application从面板拖一个 TTMSFNCGrid 到窗体上编译运行。如果窗体上出现一个带表头和空单元格的网格说明安装成功。如果控件显示为空白或者报 Class TTMSFNCGrid not found大概率是运行期包没编译成功或者 Library Path 里少了 Source/Core。注意Delphi 13.1 的 IDE 在安装第三方包之后建议重启一次 IDE 再新建项目否则控件面板的注册信息可能没完全刷新。3. 核心控件实战TTMSFNCTreeView 与 TTMSFNCGrid 的典型用法3.1 TTMSFNCTreeView 绑定数据库数据的完整流程TTMSFNCTreeView 是这套控件里用得最多的之一它比 VCL 原生的 TTreeView 强在支持多列、节点内嵌按钮、复选框、以及虚拟模式加载。实际项目里最常见的需求是从数据库读层级数据填充树。假设你有一个部门表 Dept字段是 ID、ParentID、DeptName要构建成树形结构。常见做法是用递归或者迭代方式先构建节点关系再一次性挂到 TreeView 上。FNC 的 TreeView 提供了 BeginUpdate/EndUpdate 来批量操作避免每加一个节点就重绘一次。procedure TForm1.LoadDeptTree; var qry: TFDQuery; NodeMap: TDictionaryInteger, TTMSFNCTreeViewNode; RootNode, ParentNode, NewNode: TTMSFNCTreeViewNode; DeptID, ParentID: Integer; begin // 关闭重绘批量操作 TMSFNCTreeView1.BeginUpdate; try TMSFNCTreeView1.Clear; NodeMap : TDictionaryInteger, TTMSFNCTreeViewNode.Create; try qry : TFDQuery.Create(nil); try qry.Connection : FDConnection1; qry.SQL.Text : SELECT ID, ParentID, DeptName FROM Dept ORDER BY ParentID, ID; qry.Open; while not qry.Eof do begin DeptID : qry.FieldByName(ID).AsInteger; ParentID : qry.FieldByName(ParentID).AsInteger; // 创建节点第一列显示部门名 NewNode : TMSFNCTreeView1.AddNode(nil); NewNode.Text[0] : qry.FieldByName(DeptName).AsString; NewNode.Data : Pointer(DeptID); // 把数据库 ID 存到节点上 // 根据 ParentID 挂到父节点下 if (ParentID 0) or (not NodeMap.TryGetValue(ParentID, ParentNode)) then NewNode.Parent : nil // 根节点 else NewNode.Parent : ParentNode; NodeMap.AddOrSetValue(DeptID, NewNode); qry.Next; end; finally qry.Free; end; finally NodeMap.Free; end; finally TMSFNCTreeView1.EndUpdate; end; end;这段代码的逻辑说明BeginUpdate 和 EndUpdate 之间所有节点操作不会触发重绘对于几百个节点的树性能差异非常明显。NodeMap 用来在 O(1) 时间内找到父节点避免每次遍历整棵树。NewNode.Data 存了数据库主键后续点击节点时可以直接取出来做业务查询。注意 AddNode(nil) 创建的是游离节点之后通过设置 Parent 属性挂到树上这个顺序不能反——先 AddNode 再设 Parent如果先设 Parent 再 AddNode 会报错。参数方面Text[0] 表示第一列的文本FNC TreeView 支持多列Text[1]、Text[2] 对应后续列。如果你在 Columns 属性里配置了多列记得给每列都赋值否则空列会显示为空白。Data 属性是 Pointer 类型存整数 ID 需要强制转换取出来时用 Integer(Node.Data) 还原。3.2 TTMSFNCGrid 的虚拟模式与大数据量渲染TTMSFNCGrid 处理大数据量时必须开虚拟模式否则几万行数据一次性创建单元格对象内存直接爆掉。虚拟模式的核心是 OnGetCellData 事件网格只创建可见区域的单元格滚动时动态向你要数据。procedure TForm1.TMSFNCGrid1GetCellData(Sender: TObject; ACol, ARow: Integer; var AValue: string); begin // 虚拟模式下网格只请求可见区域的单元格数据 // ARow 从 0 开始对应数据集的第 ARow 条记录 if (ARow 0) and (ARow FDataSet.RecordCount) then begin FDataSet.RecNo : ARow 1; // 数据集记录号从 1 开始 case ACol of 0: AValue : FDataSet.FieldByName(Code).AsString; 1: AValue : FDataSet.FieldByName(Name).AsString; 2: AValue : FDataSet.FieldByName(Amount).AsString; end; end else AValue : ; end; procedure TForm1.FormCreate(Sender: TObject); begin TMSFNCGrid1.VirtualMode : True; TMSFNCGrid1.RowCount : FDataSet.RecordCount; TMSFNCGrid1.ColumnCount : 3; TMSFNCGrid1.Options : TMSFNCGrid1.Options [goVirtualMode]; end;逻辑说明VirtualMode 设为 True 后网格不再为每个单元格维护独立对象而是通过 OnGetCellData 事件按需拉取。RowCount 要提前设好告诉网格总共有多少行滚动条才能正确计算比例。OnGetCellData 里用 RecNo 定位数据集记录这是最简单的做法但 RecNo 在部分数据库驱动上性能不好更好的方式是用书签或者维护一个 ID 数组。参数上ACol 和 ARow 是网格请求的列号和行号行号从 0 开始。AValue 是输出参数你给它赋什么单元格就显示什么。如果某个单元格需要特殊样式可以在 OnGetCellData 之后配合 OnCellBeforeDraw 事件改字体或背景色。注意虚拟模式下不要直接访问 TMSFNCGrid1.Cells[ACol, ARow]这个属性在虚拟模式下行为不确定所有数据交互都走 OnGetCellData。3.3 跨框架复用同一份代码在 VCL 和 FMX 下的差异处理FNC 的口号是框架中立但实际写代码时还是有一些差异要处理。最典型的是字体和颜色。VCL 下字体用 TFontFMX 下也是 TFont但 FMX 的 TFont 多了 FontFamily 的概念VCL 下直接设 Name 就行。FNC 控件内部做了适配你设 TMSFNCGrid1.Font.Name : Microsoft YaHei在 VCL 下生效在 FMX 下也会映射到对应的字体族。另一个差异是鼠标事件。VCL 的 OnMouseDown 给的是 Windows 消息坐标FMX 给的是设备无关坐标。FNC 统一成了 FNC 自己的坐标系统你在 OnCellClick 里拿到的 ACol、ARow 在两个框架下含义一致。真正需要条件编译的地方不多主要是文件路径和注册表操作。我一般会这样组织{$IFDEF MSWINDOWS} // Windows 平台特有逻辑比如读注册表 {$ENDIF} {$IFDEF ANDROID} // Android 平台特有逻辑 {$ENDIF}FNC 控件本身不需要条件编译你的业务代码里如果调了平台 API才需要包起来。这样同一套窗体代码VCL 项目编译出来是 exeFMX 项目编译出来可以部署到 Android 或 iOS。4. 避坑与排查FNC 控件安装和运行时的五个血泪教训4.1 坑一设计期包安装后控件面板不显示现象编译安装都成功了但 IDE 控件面板里找不到 TMS FNC 分组或者分组在但控件图标是灰色的。原因最常见的是设计期包没有正确注册或者 IDE 的包缓存没刷新。Delphi 13.1 的 IDE 在安装包之后有时候不会立即更新面板尤其是之前装过旧版本 FNC 的情况下旧包的注册信息还残留在注册表里。解决先到 Component → Install Packages 里检查 TMS FNC UI Pack 的设计期包是否在列表里且勾选了。如果勾选了但面板不显示取消勾选关掉 IDE删除项目目录下的 .dproj.local 和 __history 目录重新打开 IDE 再勾选。还不行的话在注册表 HKEY_CURRENT_USER\Software\Embarcadero\BDS\13.0\Known Packages 下找找有没有残留的 FNC 包路径有就删掉再重装。4.2 坑二编译时报找不到 FNC 单元现象Build 包工程时提示 Unit TMSFNCCore.pas not found 或类似错误。原因Library Path 没配全。FNC 的单元分布在 Source/Core、Source/VCL、Source/Controls 等多个子目录只加 Source 根目录不够编译器不会自动递归搜索子目录。解决在 Library Path 里把 Source 下所有包含 .pas 文件的子目录都加进去。偷懒的做法是用通配符但 Delphi 的 Library Path 不支持通配符只能一个个加。我一般会写个批处理脚本遍历 Source 目录下所有子目录生成 Library Path 字符串然后粘贴到 IDE 里。4.3 坑三TTMSFNCTreeView 节点多了之后展开卡顿现象树节点超过 500 个之后点击展开父节点要等一两秒才响应。原因默认情况下 TreeView 在展开节点时会重新计算所有子节点的布局和绘制。如果节点没有排序或者没有启用虚拟模式每次展开都是全量重算。解决开启 TreeView 的虚拟模式。设置 TMSFNCTreeView1.VirtualMode : True然后在 OnGetNodeData 事件里按需提供节点文本。虚拟模式下 TreeView 只处理可见节点展开操作是 O(可见节点数) 而不是 O(总节点数)。另外如果节点文本是固定的可以在加载完成后调用 TMSFNCTreeView1.Sort 做一次排序排序后的树在展开时查找效率更高。4.4 坑四FMX 下控件显示模糊或错位现象同样的代码在 VCL 下显示正常编译到 FMX尤其是 Android 或高分屏 Windows后控件文字模糊、边框错位。原因FMX 的坐标系统是设备无关像素VCL 是物理像素。FNC 控件内部虽然做了适配但如果你的窗体在 FMX 下没有正确设置 Scale或者控件的 Align 和 Anchors 配置不当就会出现错位。解决检查窗体的 Scaled 属性FMX 下建议设为 True。控件的 Align 尽量用 Client 或 Parent少用绝对坐标。如果文字模糊检查 TMSFNCGrid1.Font.Quality 属性FMX 下设为 fqClearType 或 fqHighQuality。另外FMX 的 TForm 有个 Quality 属性设为 HighQuality 可以改善渲染效果。4.5 坑五Full Source 版本编译后 DCU 冲突现象之前装过 DCU 版本的 FNC现在换成 Full Source 版本编译时报 Duplicate unit 或者运行时行为异常。原因Delphi 的搜索路径里同时存在旧 DCU 和新源码编译器优先链接了旧 DCU导致源码修改不生效。解决彻底清理旧版本。到 IDE 的 Library Path 里删掉所有指向旧 FNC 目录的路径到 Component → Install Packages 里卸载旧包然后手动搜索硬盘上所有 TMSFNC*.dcu 文件并删除。重新安装 Full Source 版本后在 Library Path 里确保源码目录排在所有 DCU 目录之前。5. 进阶技巧用 FNC 源码定制控件与调试运行时问题Full Source 版本最大的乐趣是可以直接改控件源码。比如 TTMSFNCGrid 的默认单元格边距我觉得太宽想改窄一点直接打开 Source/Controls/Grid/TMSFNCGrid.pas找到绘制单元格边框的那段代码把边距常量改小重新编译运行期包所有用到这个控件的项目都会生效。这种改法比继承一个子类再覆盖绘制方法要直接得多适合快速验证想法。但改源码有个后悔药问题下次升级 FNC 版本时你的修改会被覆盖。我的习惯是改之前先复制一份原始文件到 Source/_backup 目录改完后在代码里加注释标记 // MODIFIED: 2025-01-15这样升级时能快速定位自己改过哪些地方。更好的做法是把修改做成补丁文件用版本控制管理升级时重新应用补丁。调试运行时问题时Full Source 的优势更明显。比如 TTMSFNCTreeView 在某个特定数据下崩溃你可以在 IDE 里按 F7 单步进入控件内部看它是在 AddNode 的哪个阶段、哪次内存分配时出的问题。我遇到过一种情况节点文本包含特殊字符时TreeView 内部的一个字符串处理函数会越界。DCU 版本只能看到 Access violation 然后一脸懵Full Source 版本直接定位到具体行号发现是长度计算时没考虑多字节字符。这种问题在中文环境下尤其常见因为 Delphi 的 string 默认是 UnicodeString但 FNC 内部有些地方还在用 AnsiString 的长度假设。验证方法上我一般会写一个最小复现工程只放出问题的那个控件和最少的数据然后在这个工程里调试。确认问题后再决定是改源码、等官方更新、还是用子类覆盖。如果问题不紧急我会在 TMS 的官方论坛搜一下有没有人遇到过通常 Full Source 用户社区里已经有人踩过同样的坑。从那以后我每次拿到 Full Source 版本的控件包都强制走一遍先备份原始源码再建一个独立的测试工程验证基本功能最后才集成到正式项目里。这样即使改源码改出问题也能快速回滚。希望帮到你。本文还有配套的精品资源点击获取
