DevExpress VCL 18.1.6 源码包:安装部署与二次开发指南
简介这是一份面向Delphi开发者的DevExpress VCL 18.1.6完整源代码资源包附带DxAutoInstaller自动安装工具支持从Delphi 7到10.3 Rio的全系列IDE版本适用于需要离线部署控件库、解决网络安装不便、研究底层实现或进行组件二次开发的桌面应用工程师与项目团队。资源以rar压缩包形式发布整体大小约352.14MB包内包含完整源码、库文件及自动安装配置脚本文件总数与类型明细暂未随资源页公开但从标题可知核心内容为Full Source与自动安装器可满足一体化部署需求。目前已有420人浏览学习在Delphi社区中具有一定参考热度。下载解压后可借助DxAutoInstaller自动识别IDE版本并完成注册安装避免手动配置库路径、编译包顺序等繁琐步骤同时通过阅读源码理解控件事件机制与绘制流程便于按业务场景裁剪组件、定制样式或迁移老项目到更高版本Delphi从而提升开发效率与维护性。1. 一套能自装的 DevExpress VCL 源码包给老 Delphi 续命的 18.1.6接手一个十年前的物流调度项目界面全用 DevExpress VCL 的 Ribbon 和 cxGrid 画得很厚实可开发机装的是 Delphi 10.3 Rio工程一打开就是几百个带 dx 前缀的单元缺失红色波浪线几乎铺满编辑器。手动装这套界面库要按依赖顺序编译几十个包中途漏配一条路径就白干半天。这个资源包解决的就是这个痛点DevExpress VCL 18.1.6 完整源码自带 DxAutoInstaller 自动安装工具覆盖 Delphi 7 到 Delphi 10.3 Rio 全系 IDE。适合两类人项目被 DevExpress 绑定、急着恢复编译环境的老手以及想看控件内部实现、准备改源码做二次开发的深入派。2. 资源包内部结构从 DxAutoInstaller 到几十个控件包解压这份 rar 之后第一眼看到的布局非常老派但恰恰合理根目录下分三大块Library、Packages和DxAutoInstaller。Library里按套件名建子目录每个子目录塞满了 .pas 和 .inc 源文件Packages按 IDE 版本分子目录从Del7一直到Delphi10_3DxAutoInstaller里是自动安装程序和配置文件。下面拆开讲。2.1 顶层目录与版本分支Delphi 7 到 10.3 Rio 的对应关系DevExpress VCL 从来不是单一控件而是十几个套件的合集。核心的几块我在下表中列一下后面拆源码时你会反复遇到这些名字套件目录职责依赖ExpressCore基础类库封装 XML、GDIPlus、通用容器无ExpressDataController数据控制器连接 DataSet 与视图层CoreExpressGrid网格控件cxGrid 全家DataController、EditorsExpressEditors编辑器、按钮、文本框等输入控件CoreExpressBars菜单栏、工具栏、DockingCoreExpressRibbonOffice 风格 Ribbon 界面Bars、SkinExpressSkin皮肤引擎负责整套换肤CorePackages目录下按 IDE 版本分文件夹每个文件夹里放的是 .dpk 包工程和配套的 .dpkg.inc 文件。文件名带dcl前缀的是设计时包不带的是运行时包。比如ExpressGrid.dpk编译产出运行需要的.bpl和.dcudclExpressGrid.dpk则产出设计时在 IDE 里注册控件用的设计包。安装流程本质上就是按依赖顺序把这些 dpk 全部编译一遍把 dcu 路径写进 IDE 的库路径。版本分支是这份源码包很特别的地方。18.1.6 这个版本号是 2018 年的发布但 DevExpress 为了照顾存量项目一直在保留对老 Delphi 的兼容。于是你在Packages里能看到Del7、Del9、Del10_3这样的目录同一份核心源码在不同版本 IDE 下编译出不同结果的 BPL。这一点在后面的条件编译章节里会展开。2.2 DxAutoInstaller 的工作机制为什么它能跨版本自动编译DxAutoInstaller 本质上是一个编译调度器。它先扫描注册表找出当前机器上装了哪些 Delphi/BDS 版本然后根据你在界面上勾选的版本进入对应的Packages子目录按拓扑顺序调用命令行编译器。这个拓扑顺序就是包依赖顺序。ExpressCore 在最底下ExpressDataController 依赖 CoreExpressGrid 依赖 DataController 和 EditorsExpressRibbon 依赖 Bars 和 Skin层层往上叠。如果跳过 Core 直接编译 Grid编译器会立刻报找不到 dxLC.dcu 或者 dxGDIPlus.dcu。DxAutoInstaller 把这种依赖顺序内置了所以它能一口气把几十个包编完而不是让你一个包一个包去试错。安装完成后Installer 还会把每个套件的Library目录写到 IDE 的库路径配置里。Delphi 7 的库路径写在HKEY_CURRENT_USER\Software\Borland\Delphi\7.0而 Delphi 10.3 Rio 写在HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\20.0。这一步失败的话即使 BPL 已经编好IDE 也认不出这些单元。2.3 同源码多版本编译条件宏在起什么作用这份源码能跨版本编译靠的是单元文件里藏在头部的条件编译块。打开任意一个套件的核心 .pas比如cxGrid.pas能看到大量这种结构{$IFDEF DELPHI10_3} // 10.3 Rio 新增的样式处理 {$ENDIF} {$IFDEF DELPHI2009} // Unicode 分支老版本用 AnsiString {$ENDIF}这里的DELPHI10_3、DELPHI2009不是编译器内建的而是通过 .inc 文件统一映射出来的。DevExpress 源码里通常有一个dxVer.inc里面用 Delphi 自带的VER150、VER300这些版本号常量定义了可读宏。比如VER300对应的就是 Delphi 10.3 Rio。这样写控件代码时不必满篇散落魔法数字维护起来清晰得多。这个机制解释了一个问题为什么一份 18.1.6 的源码能同时喂饱 Delphi 7 和 10.3。老版本走 AnsiString 分支新版本走 Unicode 分支两边互不干扰。理解了这个机制后面改源码你才知道哪些{$IFDEF}能碰哪些最好别碰。3. 安装与运行把控件送进 Delphi IDE 的完整操作流这个资源是给 IDE 装控件库不是双击一个 exe 就能跑的那种普通软件。整个流程可以概括为四步解压、运行安装工具、验证组件面板、编译一个小 Demo。下面每一步都按实际操作来写参数给到可以直接照用的程度。3.1 环境准备与解压先确认你至少装了一个受支持的 Delphi 版本并且这个版本正式启动过一次。DxAutoInstaller 靠注册表识别 IDE绿色版或者从别的机器拷贝过来的 Delphi注册表里没有 BDS 键值Installer 根本列不出来。这一点经常被忽略是安装失败的最高频原因。解压路径不要带中文和空格DCC 命令行编译器对路径解析非常挑剔。我一般放在C:\Develop\DxVCL这种目录下。路径一层就够不要嵌在C:\Program Files (x86)这种带空格的地方否则编译到一半冒出F1026 File not found你根本想不到是路径空格造成的。解压完成之后关闭所有正在运行的 Delphi 实例。BPL文件在 IDE 启动时会被锁定编译器没法覆盖写。如果 IDE 开着就点安装到中途会报Cannot open file或者Unable to lock. 这个坑我踩了不止一次后来形成习惯装任何组件库之前先彻底退出 IDE连缓存进程都确认关干净。3.2 用 DxAutoInstaller 执行安装以管理员身份运行DxAutoInstaller.exe。界面通常是一个可勾选的列表左边是检测到的 IDE 版本右边是可操作的按钮。首次安装建议把 Clean 配置项打开让 Installer 先清掉潜在的旧版 DCU 缓存避免新旧文件混在一起。关键的配置项列在下面照着设基本不会翻车配置项作用建议值Target IDE选择要安装到的 Delphi 版本和目标 IDE 严格一致Clean first清除已有 DCU/BPL 缓存首次安装勾选Build runtime编译运行时包必须勾选Build design-time编译设计时包必须勾选否则组件面板为空Output path生成的 BPL/DCP 输出目录保持默认不推荐改Build typeDebug 或 Release首选 Release体积小且稳定点击安装后Installer 会拉出一个日志窗口里面是逐条执行的编译命令。看到类似dcc32.exe -B -U..\..\Library\ExpressCore ... ExpressCore.dpk这种命令行就是 DCC 开始干活了。除非报致命错误一般不用去干预。等日志尾部出现All packages are built或类似字样安装就完成了。3.3 验证安装设计期和运行期都要验一次重新打开 Delphi新建一个 VCL Application往窗体上拖一个TcxGrid。如果组件面板里出现了 DevExpress 页签说明设计时包注册成功如果拖出来的控件能正常放下并显示在窗体上说明运行时包也正常。注意这里其实是两步面板有页签只代表设计时包装上了控件能编译进窗体并且 IDE 不报错运行时包才算真的可用。再进一步在代码里动态创建一个视图把 cxGrid 和数据源接起来uses cxGridDBTableView; var AView: TcxGridDBTableView; begin AView : TcxGridDBTableView.Create(AGrid); AView.DataController.DataSource : ADODataSource1; AGrid.Views.Add(AView); end;这段代码在运行时创建视图比设计期拖拽更能验证安装结果因为动态创建的过程会真实走一遍控件注册和类查找逻辑。如果到这里没有弹出异常说明 DevExpress VCL 18.1.6 已经完整进入 IDE 了。3.4 手动编译单个包的命令Installer 之外的备选方案DxAutoInstaller 偶尔会抽风比如日志停在某个包上一直不动。这时可以直接进命令行手动编译。进入Packages\Delphi10_3目录执行dcc32.exe -B -DDEBUG -U..\..\Library\ExpressCore;..\..\Library\ExpressDataController -LRelease ExpressCore.dpk参数说明-B表示全量重建-D定义条件符号-U跟着的是单元搜索路径多个路径用分号隔开-L指定输出目录。手动编译时要严格按依赖顺序来先编译当前包再编译依赖它的上层包。这个命令只负责编译编译完还得把输出目录写进 IDE 的库路径否则 IDE 仍然找不到 DCU。4. 核心源码拆解一个 cxGrid 单元格从数据到像素的链路装好控件的意义不只是能用更在于能读源码。DevExpress VCL 的源码分层特别典型适合拿来学大型 VCL 控件框架。以最常用的 cxGrid 为例数据从 DataSet 里出来到最后画到屏幕上的一个像素中间经过三层数据控制器层、视图数据层、绘制样式层。下面逐层拆。4.1 数据层开头TcxDBDataController 的取数逻辑打开cxGridDataController.pas核心类是TcxDBDataController。它的职责是屏蔽 DataSet 的各种状态给上层视图提供稳定的行号和数据访问接口。function TcxDBDataController.GetRowValueByID(ARecordIndex: Integer; AItemIndex: Integer): Variant; var ADataSet: TDataSet; begin Result : Null; if (ARecordIndex 0) or (ARecordIndex FDataCount) then Exit; ADataSet : GetDataSet; if ADataSet nil then Exit; ADataSet.GotoBookmark(FBookmarks[ARecordIndex]); Result : ADataSet.Fields[AItemIndex].Value; end;逻辑说明这个方法把外部传入的记录序号转成 DataSet 书签再定位到对应行最后取字段值。为什么不用RecNo因为 DataSet 在筛选、排序或非顺序遍历状态下RecNo不保证稳定而书签是和记录绑定的过滤和排序再乱都能精确找回那条记录。参数里ARecordIndex是 DevExpress 内部记录索引AItemIndex是列序号。自己写扩展逻辑时尽量走这类封装方法不要直接访问 DataSet 的内部状态。4.2 视图数据层GridRow 到 RecordIndex 的映射视图层不直接碰 DataSet中间隔着一层TcxGridViewData。它维护一个行信息数组把网格当前显示的行号映射到数据控制器里的记录索引。procedure TcxGridViewData.PrepareRowInfo(ARowIndex: Integer; var AInfo: TcxRowInfo); begin AInfo.RecordIndex : FRecords[ARowIndex]; AInfo.Level : FLevels[ARowIndex]; AInfo.Expandable : FExpanded[ARowIndex]; end;逻辑说明PrepareRowInfo在每次滚屏或数据变更时被调用把网格行号ARowIndex转换为包含记录索引、层级、展开状态的TcxRowInfo结构。绘制层拿到的永远是TcxRowInfo不需要关心数据来自哪条 DataSet 记录。这种设计把界面滚动和数据寻址解耦所以 cxGrid 即使对上万行数据做排序和分组滚动时也不会卡到闪烁。4.3 绘制层样式决策与 Canvas 的间接关系单元格的字体、颜色和背景不是直接在 Canvas 上硬编码的而是先通过样式决策函数拿到一个TcxStyle对象再交给 Skin 引擎绘制。核心窗口是GetContentStyle虚方法procedure TcxGridTableView.GetContentStyle( const AView: TcxCustomGridView; AItem: TcxGridCustomTableItem; ARecord: Pointer; var AStyle: TcxStyle); begin if ARecord nil then AStyle : GetStyleFromData(AItem, ARecord); inherited GetContentStyle(AView, AItem, ARecord, AStyle); end;逻辑说明AItem是当前列对象ARecord是 DevExpress 内部记录指针AStyle最终决定绘制时使用的样式。GetStyleFromData根据列和记录的数据状态返回一个样式对象比如值为负时返回红色样式。这个间接层是皮肤机制的地基整套dxSkin*控件就是靠接管样式决策才做到全局换肤的。改样式时重写GetContentStyle比改 Canvas 绘制代码优雅得多。4.4 条件编译读代码时最容易忽略的分支cxGrid 源码文件头部通常会有一大片条件编译属于那种一眼看不完、但必须看的部分{$IFDEF DELPHIXE2} // 支持 64 位编译的代码 {$ENDIF} {$IFDEF DELPHI10_3} // 针对 Rio 的样式修复 {$ENDIF}逻辑说明这些分支的存在意味着你搜索代码时会看到同一个方法反复出现看起来像定义了多次实际是不同版本的实现。修改源码时第一原则是不要动跨版本公共分支只在目标 IDE 对应的宏分支里改。如果你在DELPHI10_3分支里加了一句代码然后换到 Delphi 7 编译那句代码会被编译器直接丢弃你追踪半天都不知道改动去哪了。5. 避坑手册源码安装与使用中的五个高频翻车点这个资源包本身很省心但安装和使用过程中还是有几个坑位几乎每个接手的人都会踩一遍。下面按现象、原因、解决三步写关键问题可以直接抄作业。5.1 DxAutoInstaller 列表里看不到已安装的 Delphi现象装了 Delphi 10.3Installer 的版本列表却是空的想勾选都没得选。原因你的 IDE 很可能是绿色版或者从别的机器拷贝过来的没有写入安装注册表。DxAutoInstaller 靠读注册表枚举 IDE注册表里没有就识别不到。解决检查HKEY_LOCAL_MACHINE\SOFTWARE\Embarcadero\BDS\20.0是否存在没有就手动补一个App键指向 IDE 安装目录。64 位系统上还要看WOW6432Node分支。这个操作本质上是让 Installer 能找到 BDS 的根路径路径指向的目录里必须有bin\dcc32.exe才算数。5.2 编译到中途报 File not found: dxLC.dcu现象编译过程走到某个套件时突然中断日志提示找不到dxLC.dcu之类的底层单元。原因包依赖顺序被打乱了。dxLC属于 ExpressCore是最底层的基础包如果上层包先编译编译器找不到底层 DCU 就当场报错。解决回到Packages目录确认当前编译的包所在的层级。DxAutoInstaller 默认的拓扑顺序是正确的不要单独手动编译某个包然后又不在 Installer 里跑完整流程。手动编译时严格按ExpressCore - ExpressDataController - ExpressGrid的顺序走越底层越靠前。5.3 老项目打开时提示 TcxGrid 不存在现象用 18.1.6 装好环境后打开一个老工程IDE 提示Class TcxGrid doesnt exist或者找不到类。原因老项目原先用的 DevExpress 版本比 18.1.6 旧DFM 里保存的构造类名或包名称与当前版本不一致IDE 在设计期反序列化窗体时认不出来。解决先确认工程文件里引用的运行时包名称和当前安装的包一致。如果 DFM 里存的是旧版本改过的类名用文本编辑器打开 DFM把对应类名改成 18.1.6 的类名这是最直接的做法。改之前记得备份 DFM 文件这不是玄学是后悔药。5.4 皮肤一片灰换上什么主题都没效果现象编译运行没有任何报错但 cxGrid、Ribbon 全都是灰底白字皮肤配置像没生效。原因皮肤相关的运行时包没有被链接进程序。DevExpress 的皮肤套件不是默认引用的项目里必须有对应的dxSkin*单元引用皮肤资源才会被编译进 executable。常见做法不生效多半是引用缺失。解决在项目主单元的uses里加上你需要的皮肤单元然后在运行时创建皮肤控制器uses dxSkinOffice2016Colorful; procedure TForm1.FormCreate(Sender: TObject); begin TdxSkinController.Create(Self).SkinName : Office2016Colorful; end;参数说明dxSkinOffice2016Colorful对应 Office 2016 的彩色皮肤单元名SkinName字符串必须是包内注册的皮肤名写错不会报错但皮肤仍是灰的排查时容易怀疑人生。5.5 编译安装都正常组件面板里却没有 DevExpress 页签现象Installer 日志显示所有包都编译成功打开 IDE 却在组件面板里找不到任何 DevExpress 控件。原因运行时包编译 OK但设计时包没有安装。没有dcl前缀的包只提供类实现设计时包才负责把控件注册到组件面板。解决回到 DxAutoInstaller确认 Build design-time 被勾选重新执行安装。装完后在 IDE 的组件包管理里检查dclExpressGrid等设计时包是否处于已安装状态。手动补救也可以用 IDE 的 Component Install Packages 把Packages\Delphi10_3\Release\dclExpressGrid.bpl添加进去。6. 二次开发改一个 cxGrid 样式并让改动真正生效拿到完整源码最值得做的事就是二次开发。但直接修改官方.pas文件是下策因为换版本或重新安装时改动会被新文件覆盖。我会先把 DevExpress 的源码当成一个黑匣子的参考自己的改动全部写成子类放进项目自己的单元里既不污染原包也好回滚。比如想给 cxGrid 的行加斑马纹可以在项目里新建一个单元继承TcxGridDBTableViewtype TZebraDBTableView class(TcxGridDBTableView) protected procedure GetContentStyle(const AView: TcxCustomGridView; AItem: TcxGridCustomTableItem; ARecord: Pointer; var AStyle: TcxStyle); override; end; implementation procedure TZebraDBTableView.GetContentStyle( const AView: TcxCustomGridView; AItem: TcxGridCustomTableItem; ARecord: Pointer; var AStyle: TcxStyle); var ARecordIndex: Integer; begin inherited GetContentStyle(AView, AItem, ARecord, AStyle); ARecordIndex : DataController.GetFocusedRecordIndex; if ARecordIndex mod 2 1 then AStyle : FOddStyle; // 自定义的奇数行样式 end;逻辑说明重写GetContentStyle先调用父类走默认样式决策再按记录索引判断奇偶行替换成自定义样式。这种方法比修改源码安全得多因为你在子类里只覆盖样式逻辑不碰 cxGrid 的绘制核心。要注意FOddStyle需要你自己在类里定义并初始化它是普通 TcxStyle 对象可以在使用这个视图的窗体里统一管理。创建视图时用子类替代默认类with TZebraDBTableView.Create(Self) do begin DataController.DataSource : ADataSource; end;这样代码逻辑里所有用到表格视图的地方实际对象都是TZebraDBTableView改动即时生效。验证方法很简单在一个 Demo 工程里挂上TDataSet和这个视图运行起来滚动数据看奇数行样式是否稳定出现。断点打在GetContentStyle里看触发行号是不是符合预期。这个验证比什么设计期预览都靠谱。源码包最大的价值就是允许你彻底看清楚控件的实现路径而子类化是成本最低的切入方式。从那以后我每次拿到一套新控件源码都会强制自己先写一个最小子类跑通改动链路再考虑要不要深入改源码。希望帮到你。本文还有配套的精品资源点击获取