ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TMS FNC Core 跨框架控件安装与避坑指南:从 XE7 到 13 Florence

TMS FNC Core 跨框架控件安装与避坑指南:从 XE7 到 13 Florence

简介:TMS FNC Core v4.2.2.4 是面向 Delphi 与 C++ Builder XE7-13 Florence 开发者的跨平台控件库,适合需要一套代码覆盖 Windows、macOS、Linux 及移动端的中高级开发者,用于构建统一 UI、减少平台适配成本。压缩包共 824 个文件,约 6.36MB,以 452 个 pas 源码、34 个 dpr 工程、63 个 dproj 项目文件、29 个 dpk 包文件为主,另含 dfm/fmx 窗体、dcr 组件资源、res/ico 图标及少量 html、style、jar 等,完整源码便于二次编译与定制。资源附带文档与示例代码,覆盖按钮、列表、编辑框等常用控件,支持高级图形渲染、触摸与鼠标输入,并集成响应式与模块化设计思路,可帮助读者快速上手、理解控件特性并集成到现有项目。目前已有 44 人学习下载。

1. TMS FNC Core 到底是什么:从 XE7 到 13 Florence 的跨框架底座

如果你手上有一份TMS FNC Core v4.2.2.4 for Delphi & CB XE7-13 Florence Full Source.rar,先别急着解压双击安装。这个包真正值钱的地方,不是里面有多少个.pas文件,而是它解决了一个从 Delphi XE7 一路拖到 13 Florence 都没被官方彻底摆平的问题:同一套 UI 控件代码,怎么在 VCL、FMX、LCL 三种框架上跑出接近一致的行为。TMS FNC Core 就是这套跨框架控件体系的公共底座,FNC 是 Framework Neutral Components 的缩写,Grid、Chart、TreeView 这些上层控件全都依赖它。你拿到的 Full Source 版本意味着可以跟源码调试、改绘制逻辑、绕开授权限制做二次封装。适合谁?适合那些维护着 XE7 老项目、又想逐步迁到 13 Florence,同时不愿意把 UI 层推倒重写的人。它不解决业务逻辑,只解决“控件在哪个框架下都能用”这件事。

2. 拆包与安装:把 Full Source 变成 IDE 里能拖的控件

2.1 解压后的目录结构与该先看哪个文件

拿到 rar 之后,我一般先解到一个短路径,比如D:\Components\FNC\,因为 Delphi 的编译期包对路径长度和空格都比较敏感,路径里带中文或空格是经典翻车点。解压后典型结构是这样:

目录/文件作用是否必须
Source\所有 FNC Core 的.pas源码必须
Packages\各版本.dpk/.dproj包工程必须
Demos\示例工程,验证安装是否成功建议保留
Lib\预编译 DCU(Full Source 下可忽略)可选
Readme/License版本与授权说明看一眼

关键动作是先确认Packages下有没有对应你 IDE 版本的包目录。XE7 到 13 Florence 跨度很大,包工程通常按版本分文件夹,比如Packages\Delphi 13 Florence\。如果只有老版本目录,不要硬拿 XE7 的 dpk 去 13 里编译,字符集和 RTL 单元引用会直接报一堆找不到的标识符。

2.2 编译期包与运行期包的安装顺序

FNC Core 是典型的“先编译期、后运行期”结构。顺序错了,IDE 里能装上去但一拖控件就报类未注册。标准流程:

# 1. 先打开运行期包(Runtime),编译并 Install # 文件名通常形如 FNC.Core.RT.dpk # 2. 再打开设计期包(Design),编译并 Install # 文件名通常形如 FNC.Core.DT.dpk # 3. 两个包都 Install 成功后,重启 IDE

逻辑说明:运行期包提供实际运行需要的单元,设计期包只负责把控件注册到工具箱并挂上属性编辑器。先装设计期包的话,IDE 在注册控件时会去加载运行期单元,找不到就报Class Txxx not found。参数上唯一要注意的是 Output 路径,建议把 DCU 输出统一指到一个独立目录,别让它散落在源码树里,否则以后升级版本会残留旧 DCU 导致玄学编译错误。

2.3 在 XE7 与 13 Florence 上分别验证

装完之后别急着写业务,先做最小验证。新建一个 VCL Application,从工具箱拖一个 FNC 的基础控件(比如TTMSFNCBitmapContainer之类),能拖出来、能编译、能运行,说明底座通了。再新建一个 FMX Application 重复一遍。如果 VCL 能跑 FMX 报错,八成是 FMX 相关的条件编译单元没被正确引用,回去检查包的Requires里有没有把 FMX 的 rtl/fmx 单元列全。XE7 和 13 Florence 的差异主要体现在 Unicode 处理和泛型语法上,Full Source 的好处就是报错能直接跳进源码看是哪一行,比黑盒 DCU 强太多。

3. 跨框架适配的核心机制:FNC 怎么做到一套代码三处跑

3.1 框架中立层与条件编译的分工

FNC Core 的思路不是“写一套万能代码”,而是“抽象一层 + 条件编译兜底”。它把绘制、消息、字体、鼠标事件这些框架相关的东西抽成接口,VCL 下走 Canvas/Windows 消息,FMX 下走 TCanvas/FMX 消息,LCL 下再走一套。你在业务代码里调用的永远是 FNC 暴露的那层 API,底层切换由编译期决定。这就是为什么同一个控件在三个框架下外观接近但细节有差异——差异被隔离在底层实现里了。

理解这一点很重要,因为它决定了你排错的方向:如果控件在 VCL 下正常、FMX 下错位,问题几乎一定在框架适配层,而不是你的业务代码。Full Source 让你能直接进Source目录搜条件编译指令,定位到具体是哪个{$IFDEF FMX}分支出了问题。

3.2 用条件编译指令隔离平台差异

实际改源码时,最常见的操作是加平台分支。比如你要在某个绘制方法里针对 FMX 做特殊处理:

procedure TTMSFNCCustomControl.DoPaint; begin inherited; {$IFDEF VCL} // VCL 下直接用 Canvas.Handle 做 GDI 绘制 PaintVCL(Canvas); {$ENDIF} {$IFDEF FMX} // FMX 下必须走 TCanvas 的 DrawRect/FillRect,不能碰 Handle PaintFMX(Canvas); {$ENDIF} {$IFDEF LCL} PaintLCL(Canvas); {$ENDIF} end;

逻辑说明:{$IFDEF}是编译期指令,不是运行期判断,所以不会带来运行时开销,但也意味着你不能在运行期动态切换框架——框架在编译那一刻就定死了。参数上要注意,VCL 和 FMX 的 Canvas 类型不同,方法签名也不一样,改源码时别想着写一个通用签名糊过去,老老实实分三个方法。我一般会在改之前先Ctrl+F搜一遍现有代码里同类控件是怎么处理的,照着它的模式来,比自创一套稳。

3.3 事件模型与消息循环的差异处理

跨框架最容易踩的坑是事件模型。VCL 的鼠标事件带Shift: TShiftState,FMX 的OnMouseDown签名里坐标是Single而不是Integer,LCL 又接近 VCL 但消息常量不同。FNC Core 在中间做了一层转换,把三套事件归一化成自己的事件类型。你在用 FNC 控件时绑定的OnMouseDown其实是 FNC 自己的事件,不是框架原生的。

这意味着两件事:第一,你拿不到框架原生的全部信息,比如某些底层消息参数会被丢掉;第二,如果你需要原生行为,得通过 FNC 暴露的NativeXXX属性或事件去拿。常见做法是先用 FNC 事件做业务,真需要底层时再挂框架原生事件做补充。别指望 FNC 事件能覆盖所有原生场景,它的目标是“够用且一致”,不是“完全等价”。

4. 避坑与排查:Full Source 安装后最常见的 5 个翻车现场

4.1 装完控件拖不出来,报 Class not found

现象:包 Install 显示成功,工具箱里也有图标,但一往 Form 上拖就弹Class TTMSFNCxxx not found。 原因:设计期包注册了控件,但运行期包的 DCU 没被 IDE 找到,或者运行期包根本没编译成功只是被跳过了。 解决:回到Packages目录,先单独打开运行期包,Build 一次看有没有报错,确认 Output 目录里有对应 DCU,再重新 Install 设计期包。路径里带空格或中文的,换短路径重来。

4.2 XE7 工程升到 13 Florence 后编译报字符集错误

现象:老工程在 XE7 下好好的,换到 13 Florence 编译,一堆E2003 Undeclared identifier: 'UTF8Encode'之类的错。 原因:13 Florence 的 RTL 里部分字符串处理单元做了调整,老代码里直接调用的某些函数签名变了或被移走了。 解决:别去改 FNC 源码,先在你的工程里加System.SysUtils、System.Character等显式引用,把隐式依赖补全。Full Source 的价值在这里体现——报错能跳进 FNC 源码看它引用了哪个单元,照着补。

4.3 FMX 下控件能显示但点击无响应

现象:VCL 下一切正常,FMX 下控件画出来了,但鼠标点上去没反应。 原因:FMX 的命中测试(HitTest)机制和 VCL 不同,FNC 控件在 FMX 下需要正确设置HitTest和AutoCapture,否则事件被上层容器吃掉。 解决:检查控件的HitTest属性是否为 True,父容器的HitTest是否把它挡住了。必要时在 FNC 源码里看它的MouseDown是怎么做命中判断的,对照 FMX 的坐标体系调。

4.4 同时装了旧版 FNC,新版装完行为诡异

现象:升级到 v4.2.2.4 后,某些控件行为跟文档对不上,像是旧版本在生效。 原因:旧版 DCU 残留在 IDE 的搜索路径里,编译时优先命中了旧 DCU。 解决:把 IDE 的 Library Path 里所有指向旧 FNC 目录的条目删掉,只留新版路径。再狠一点,把旧目录整个改名,逼 IDE 找不到。这个坑血泪经验最多,因为报错往往不直接指向版本冲突。

4.5 编译期包能装,运行期包 Install 按钮是灰的

现象:打开运行期包,Install 菜单是灰的,点不了。 原因:运行期包本来就不该 Install,它只需要 Compile。只有设计期包才需要 Install。 解决:这是正常现象,不是 bug。运行期包 Build 成功即可,Install 交给设计期包。很多人第一次用 FNC 会在这里卡半天,以为包坏了。

5. 从 Full Source 到二次封装:把 FNC Core 用出源码级价值

拿到 Full Source 最大的好处,是你能在它上面做二次封装而不受黑盒限制。我一般的做法是:不直接改 FNC 源码,而是新建一个自己的单元,继承 FNC 的基类,把项目里反复用到的样式、默认属性、事件绑定逻辑收进去。这样升级 FNC 版本时,只要基类接口没大改,我的封装层几乎不用动。

具体技巧是抓住 FNC 的Custom基类。FNC 的控件大多有一个TTMSFNCCustomXXX基类,它不含具体外观,只含行为和属性框架。继承它,你就能做出符合自己项目规范、又保留跨框架能力的控件。验证方法很简单:写一个继承自 FNC 基类的最小控件,分别在 VCL 和 FMX 下拖出来,看行为是否一致。一致,说明你的封装没破坏框架中立性;不一致,说明你在封装层里混进了框架相关代码。

另一个值得投入的点是调试符号。Full Source 编译出来的 DCU 默认带调试信息,配合 IDE 的“Use debug DCUs”能在 FNC 内部下断点。排查那些“控件行为诡异但报错不明确”的问题时,能直接跟到 FNC 的绘制或消息处理逻辑里,比猜快十倍。我现在的习惯是:任何 FNC 相关的疑难杂症,先开调试 DCU 跟一遍调用栈,再决定是改源码还是改用法。这个习惯帮我省下的时间,远比折腾安装那点成本多。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进