Delphi 13.1 高DPI界面美化:StyleControls 585 圆角阴影与皮肤加载实战
简介StyleControls585是一套面向Delphi 13.1开发者的第三方界面控件包适合希望快速构建现代化UI、减少自绘工作量的中高级Object Pascal程序员。资源包共186个文件约1.47MB以55个pas源码单元、42个res资源文件、21个cbproj与21个cpp工程文件、15个dpk与15个dproj包工程文件为主另含dfm窗体、inc、h、dcr等辅助文件覆盖从源码到编译工程的完整结构便于直接集成到现有项目。控件可用于实现平滑转场、阴影、自定义绘制等视觉效果帮助应用在保持系统风格一致的同时突出独特设计。目前已有78人学习下载具备一定社区参考价值。借助源码与工程文件读者可研究控件实现思路、属性与事件用法并据此扩展或定制自己的界面组件提升开发效率与交互体验。1. StyleControls 585 在 Delphi 13.1 里到底解决了什么问题如果你正在用 Delphi 13.1 做桌面端界面大概率遇到过这种场景客户看了一眼原型图说按钮要圆角、面板要阴影、列表项要悬停高亮最好再来点渐变。你打开 VCL 默认控件面板发现 Button 是方的Panel 是灰的想改个圆角得自己写CreateRoundRectRgn再处理重绘改完发现 DPI 缩放一开全糊了。StyleControls 这类控件包就是冲着这个痛点来的——它把按钮、面板、编辑框、列表、菜单这些常用控件做成了可换肤、可调圆角、可加阴影的版本让 VCL 界面不用换框架也能做出接近现代 UI 的观感。585 这个版本号对应的是该控件包的一个发布批次配合 Delphi 13.1 的编译器和运行时主要解决的是高 DPI 下的绘制一致性和皮肤资源的加载效率。这篇文章面向的是已经在用 Delphi 做 Windows 桌面项目、想在不推翻现有 VCL 代码的前提下把界面拉高一个档次的开发者也适合那些从 Delphi 7 老项目迁移上来、第一次接触皮肤控件包的人。2. 把 StyleControls 585 装进 Delphi 13.1 的完整路径2.1 安装前的环境确认与目录规划Delphi 13.1 的 IDE 对包Package的加载顺序比老版本敏感尤其是同时装了多个第三方控件包的时候。我一般会先做三件事确认 IDE 是 32 位还是 64 位编译目标、确认系统里没有残留的旧版 StyleControls 的 BPL 文件、把解压出来的源码目录放在一个不含中文和空格的路径下。路径里带空格是血泪经验某些版本的安装脚本在调用brcc32编译资源时会因为引号处理不当直接报错。安装包解压后通常能看到几个关键目录Source放的是.pas单元文件Packages放的是.dpk和.dprojResources放的是皮肤文件和图片资源Demos是示例工程。先别急着打开 IDE用文本编辑器看一眼Packages目录下对应 Delphi 13.1 的.dproj文件确认DCC_Namespace里引用的单元路径和实际Source目录一致。不一致的话后面编译会报找不到单元。# 建议的目录结构路径不要带空格和中文 D:\Dev\StyleControls585\ Source\ Packages\ Delphi13\ Resources\ Skins\ Demos\上面这个结构是我习惯的摆放方式。Packages\Delphi13这个子目录名不是固定的具体看压缩包里怎么分的有的版本直接放在Packages根下。关键是让.dpk文件里的{$IFDEF VER360}之类的条件编译能命中 Delphi 13.1 对应的编译器版本号。Delphi 13.1 的编译器版本宏和 12.x 不一样如果包里的条件编译没覆盖到需要手动补一个分支。2.2 编译并安装设计期包打开 Delphi 13.1走File → Open Project定位到Packages目录下的.dpk文件。通常会有两个包一个运行期包名字里带R或Runtime一个设计期包名字里带D或Design。先编译运行期包再编译设计期包顺序反了会报找不到单元。// 运行期包 .dpk 的典型内容结构不要直接改先看 package StyleControls_R; {$R *.res} {$ALIGN 8} {$ASSERTIONS ON} {$WEAKLINKRTTI ON} {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])} requires rtl, vcl, vclx; contains scStyles in ..\Source\scStyles.pas, scButtons in ..\Source\scButtons.pas, scPanels in ..\Source\scPanels.pas, scEdits in ..\Source\scEdits.pas; end.这段是运行期包的骨架。requires里列的是它依赖的 Delphi 自带包contains里是实际编译进去的单元。编译时如果报Unit not found八成是contains里的相对路径和实际文件位置对不上按实际目录改。编译通过后右键点设计期包选InstallIDE 会提示重启或者直接在当前会话注册控件。注册成功后工具箱里会多出一个StyleControls分类页。提示安装设计期包之前先把 IDE 里所有已打开的项目关掉。有项目开着的时候装包有时候控件面板刷新不出来得重启 IDE 才认。2.3 在窗体上放第一个皮肤按钮并验证装完之后从工具箱拖一个scButton到窗体上再拖一个scSkinManager。scSkinManager是管皮肤资源的不挂它的话按钮还是默认的 Windows 灰。双击scSkinManager在皮肤列表里加载Resources\Skins下的皮肤文件选一个应用。// 在 FormCreate 里动态加载皮肤比设计期挂更灵活 procedure TForm1.FormCreate(Sender: TObject); begin scSkinManager1.SkinFile : ExtractFilePath(Application.ExeName) Skins\Office2019.scs; scSkinManager1.Active : True; scButton1.Caption : 确定; scButton1.RoundCorners : 6; // 圆角半径单位像素 scButton1.Shadow.Enabled : True; // 阴影开关 scButton1.Shadow.Size : 4; // 阴影扩散范围 end;这段代码做了三件事指定皮肤文件路径、激活皮肤管理器、设置按钮的圆角和阴影。SkinFile用ExtractFilePath(Application.ExeName)拼相对路径是为了发布时皮肤文件跟着 exe 走不写死绝对路径。RoundCorners设成 6 是经验值再大按钮看起来会有点胖再小圆角效果不明显。Shadow.Size超过 6 之后绘制开销会明显上升尤其是窗体上按钮多的时候。编译运行如果按钮还是方的检查scSkinManager1.Active是不是在FormCreate里被设成了True以及皮肤文件路径是否真的存在。路径不对的话SkinManager不会报错只是静默用默认样式这个坑我踩过不止一次。3. 圆角、阴影、DPI 这三个参数怎么调才不翻车3.1 圆角半径与控件尺寸的匹配关系圆角不是随便设的。一个 32 像素高的按钮圆角设 16 就变成胶囊形了设 2 又几乎看不出来。我一般按控件短边高度的 15% 到 25% 来估。比如按钮高 30 像素圆角取 5 到 7 之间比较自然。面板类控件因为面积大圆角可以稍微大一点但超过 12 之后边缘的抗锯齿会开始发虚尤其是背景不是纯色的时候。// 按控件高度动态算圆角避免手调 procedure TForm1.AdjustCorners; begin scButton1.RoundCorners : Round(scButton1.Height * 0.2); scPanel1.RoundCorners : Round(scPanel1.Height * 0.08); scEdit1.RoundCorners : Round(scEdit1.Height * 0.15); end;这段逻辑放在OnResize里调用控件尺寸变了圆角跟着变。系数 0.2、0.08、0.15 分别对应按钮、面板、编辑框是我在几个项目里调出来的经验值。面板系数低是因为面板通常承载内容圆角太大内容区域会被裁掉。编辑框系数居中是因为它高度小圆角稍微大一点视觉上更柔和。3.2 阴影参数对渲染性能的影响阴影好看但费性能。StyleControls 的阴影实现方式是在控件背后画一层半透明渐变控件多的时候每一帧都在重绘。我做过一个测试一个窗体上放 20 个带阴影的按钮滚动的时候帧率从 60 掉到 35 左右。所以阴影要克制用一般只给主要操作按钮和浮动面板加列表项和工具栏按钮不加。参数建议值说明Shadow.Enabled仅主按钮开列表项、工具栏按钮关闭Shadow.Size35超过 6 绘制开销明显上升Shadow.Opacity0.30.5太高会显得脏太低看不见Shadow.Color黑色或深灰彩色阴影只在特定主题下用这张表里的值不是死的但Shadow.Size超过 6 之后性能下降是实测结论。Shadow.Opacity设 0.5 以上在浅色背景上会有一圈明显的灰边看起来像没擦干净的污渍。我一般从 0.35 开始试根据背景色微调。3.3 高 DPI 下的缩放适配Delphi 13.1 对高 DPI 的支持比老版本好但第三方控件如果没做缩放适配在 150% 缩放下还是会糊。StyleControls 585 在绘制时用的是矢量路径加抗锯齿理论上缩放不糊但前提是窗体的Scaled属性设成True并且scSkinManager的ScaleMode设成smAuto。// 窗体创建时设置 DPI 相关属性 procedure TForm1.FormCreate(Sender: TObject); begin Scaled : True; scSkinManager1.ScaleMode : smAuto; scSkinManager1.ScaleRatio : 1.0; // 让控件自己算不要手动干预 end;ScaleRatio设成 1.0 不是说不缩放而是让scSkinManager根据系统 DPI 自动算比例。手动设成 1.5 之类的固定值在非整数倍缩放的显示器上会出现半像素偏移边缘发虚。这个坑我在一个双屏项目里踩过主屏 100%、副屏 150%手动设比例之后副屏上的控件边缘全是毛刺。注意如果项目是从 Delphi 7 或 2007 迁移上来的窗体的Scaled属性默认可能是False记得改成True否则高 DPI 下整个界面会缩成一团。4. 皮肤资源加载与运行时切换的避坑清单4.1 皮肤文件路径写错导致静默失败现象编译运行后控件还是系统默认样式没有任何报错。原因SkinFile指向的路径不存在scSkinManager找不到文件就静默回退到默认绘制。解决在FormCreate里加一句文件存在性检查不存在就弹提示或者写日志。procedure TForm1.FormCreate(Sender: TObject); var SkinPath: string; begin SkinPath : ExtractFilePath(Application.ExeName) Skins\Default.scs; if not FileExists(SkinPath) then begin ShowMessage(皮肤文件缺失 SkinPath); Exit; end; scSkinManager1.SkinFile : SkinPath; scSkinManager1.Active : True; end;这段代码比直接赋值多了一个FileExists判断。发布的时候皮肤文件经常忘了拷进Skins目录加了判断至少能第一时间发现。4.2 设计期皮肤和运行期皮肤不一致现象设计器里看着是蓝色主题编译运行变成灰色。原因设计期加载的皮肤文件路径是绝对路径运行期用的是相对路径两个路径指向的文件不一样。解决统一用相对路径设计期也把皮肤文件拷到 exe 输出目录下。4.3 多个 SkinManager 同时激活导致绘制冲突现象窗体上部分控件是 A 皮肤部分是 B 皮肤颜色对不上。原因一个窗体上放了两个scSkinManager都设了Active : True。解决一个窗体只保留一个scSkinManager需要换肤的时候改它的SkinFile再重新激活。4.4 皮肤文件版本和控件包版本不匹配现象加载皮肤后控件布局错乱按钮上的文字偏移。原因皮肤文件是给旧版控件包做的新版控件包改了绘制逻辑皮肤里的边距参数对不上。解决用控件包自带的皮肤编辑器重新导出皮肤或者找和 585 版本配套的皮肤文件。4.5 运行时切换皮肤后部分控件不刷新现象点了换肤按钮大部分控件变了但某个面板还是旧颜色。原因那个面板的ParentBackground属性设成了False它不跟随父级重绘。解决把需要跟随皮肤的面板ParentBackground设成True或者在换肤后手动调Invalidate。5. 把 StyleControls 和现有 VCL 代码混用的进阶技巧5.1 在标准 TForm 上混用 scButton 和 TButton老项目里全是TButton不可能一次性全换。我的做法是分两步先把主要操作按钮换成scButton次要按钮保持TButton但把Flat设成True、Style设成bsCommandLink之类的让视觉差距不那么大。然后在scSkinManager里把TButton的默认绘制也接管掉这样即使不换控件标准按钮也能跟着皮肤走。// 让 SkinManager 接管标准 TButton 的绘制 procedure TForm1.FormCreate(Sender: TObject); begin scSkinManager1.Active : True; scSkinManager1.Options : scSkinManager1.Options [soSkinStandardControls]; end;soSkinStandardControls这个选项打开之后TButton、TEdit、TPanel这些标准控件也会用皮肤绘制。这样老代码一行不用改界面风格就统一了。代价是标准控件的某些原生行为比如TEdit的右键菜单可能会受影响需要单独测。5.2 用 scPanel 做卡片式布局的间距控制卡片式布局的核心是间距。scPanel的Margins和AlignWithMargins配合用能做出比较规整的卡片流。我一般把卡片间距设成 12 像素卡片内边距设成 16 像素这个比例在 1080P 和 4K 下看着都舒服。// 动态创建卡片面板 procedure TForm1.CreateCard(const ACaption: string); var Card: TscPanel; begin Card : TscPanel.Create(Self); Card.Parent : scScrollBox1; Card.Align : alTop; Card.AlignWithMargins : True; Card.Margins.SetBounds(12, 6, 12, 6); // 左右 12上下 6 Card.Height : 80; Card.RoundCorners : 8; Card.Shadow.Enabled : True; Card.Shadow.Size : 3; Card.Caption : ACaption; end;Margins.SetBounds(12, 6, 12, 6)四个参数分别是左、上、右、下。上下设 6 是因为卡片之间垂直堆叠6 像素的间距加上卡片自身的阴影扩散视觉上刚好。左右设 12 是给滚动条留位置。RoundCorners设 8 配合 80 像素高度比例合适。5.3 皮肤资源的按需加载与内存占用皮肤文件加载后会常驻内存一个复杂的皮肤大概占 2 到 5 MB。如果项目里做了多套皮肤切换不要一次性全加载用的时候再SkinFile : ...切换。切换的时候旧皮肤的资源会被释放但释放不是即时的频繁切换会有内存碎片。我一般限制用户最多在 3 套皮肤之间切切完等 1 秒再允许下一次。// 带防抖的皮肤切换 procedure TForm1.SwitchSkin(const AFile: string); begin if scSkinManager1.SkinFile AFile then Exit; scSkinManager1.Active : False; scSkinManager1.SkinFile : AFile; scSkinManager1.Active : True; Application.ProcessMessages; // 让释放和重绘走完 end;先Active : False再改SkinFile再Active : True比直接改SkinFile要稳。直接改的话某些版本会在旧资源没释放完的时候就去加载新资源导致绘制错乱。Application.ProcessMessages是给消息队列一个喘息的机会不加的话连续快速切换会卡界面。5.4 用皮肤编辑器自定义一套品牌色StyleControls 自带一个皮肤编辑器可以基于现有皮肤改颜色、圆角、字体。我一般拿一个中性皮肤做底把主色改成客户品牌色导出成新的.scs文件。导出的时候注意把用到的字体也嵌进去不然换台机器字体缺失文字会变成默认宋体整个界面气质就垮了。导出选项建议原因嵌入字体勾选避免目标机器缺字体压缩资源勾选皮肤文件小一半左右保留源文件勾选后续改色不用从头来版本兼容选 585和控件包版本对齐这张表里的选项在导出对话框里都能找到。嵌入字体之后皮肤文件会大 1 到 2 MB但换来的是跨机器一致性值得。版本兼容选 585 是因为控件包版本和皮肤格式是绑定的选错了加载会报错。5.5 发布时别忘了带上的文件编译出来的 exe 单独跑不起来至少需要Skins目录下的皮肤文件。如果用了scSkinManager的图片资源还要带上对应的图片目录。我一般用 Inno Setup 打包把Skins目录整个拷进去安装时释放到 exe 同级目录下。# 发布目录结构 Release\ YourApp.exe Skins\ Default.scs Dark.scs Resources\ Icons\这个结构里Skins和Resources都是运行时需要的。少了Skins界面变默认样式少了Resources图标显示不出来。打包脚本里把这两个目录设成recursesubdirs不然子目录里的文件拷不进去。希望帮到你。本文还有配套的精品资源点击获取