戰(zhàn):Delphi 老項(xiàng)目換膚與 WebBrowser 皮膚激活)
簡(jiǎn)介AlphaControls 14.24 懶人包面向使用 Delphi 與 CBuilder 進(jìn)行桌面界面開發(fā)的程序員尤其適合希望快速集成成熟皮膚控件、又不想被商業(yè)皮膚提示打擾的中高級(jí)開發(fā)者。壓縮包共收錄 1055 個(gè)文件約 21.52MB涵蓋 asz 皮膚文件、pas 源碼單元、res 資源、dpk 與 dproj 工程文件、dcu 編譯單元、dfm 窗體文件以及 cpp、cbproj 等 CBuilder 相關(guān)文件并附帶少量 exe、chm 幫助文檔與 ini 配置結(jié)構(gòu)完整便于按組件與皮膚模塊檢索。該版本已激活 WebBrowser 皮膚并去除商業(yè)皮膚提示開箱即可用于窗體美化、皮膚切換與控件定制等場(chǎng)景。目前已有 292 人學(xué)習(xí)下載可作為 Delphi 界面開發(fā)中快速驗(yàn)證皮膚效果、研究控件源碼與工程組織方式的實(shí)用參考包。1. 拿到 AlphaControls 14.24 懶人包Delphi 老項(xiàng)目換皮前的第一道判斷題如果你手上維護(hù)著一套跑了七八年的 Delphi 桌面程序界面還停留在 Windows 7 時(shí)代的灰按鈕加直角邊框那你大概率動(dòng)過(guò)換皮膚的念頭。AlphaControls 就是這條路上繞不開的一個(gè)控件包它把 VCL 原生控件整體接管換成可換膚的渲染層按鈕、表格、菜單、滾動(dòng)條全部跟著皮膚走。這次拿到的是 14.24 的懶人包標(biāo)題里寫得很直白含所有組件、激活 WebBrowser 皮膚、去掉商業(yè)皮膚提示。翻譯成工程語(yǔ)言就是——組件齊全瀏覽器控件也能吃到皮膚啟動(dòng)時(shí)那個(gè)煩人的未授權(quán)彈窗被處理掉了。適合誰(shuí)適合不想折騰編譯期依賴、只想把皮膚引擎塞進(jìn)現(xiàn)有工程看效果的 Delphi 開發(fā)者。但先別急著解壓這類整合包有幾個(gè)地方必須先判斷清楚否則后面全是返工。2. 拆包與工程接入從目錄結(jié)構(gòu)到第一個(gè)可編譯的皮膚窗體2.1 懶人包到底“懶”在哪目錄結(jié)構(gòu)與組件構(gòu)成先把壓縮包解開不要直接往 IDE 里拖。我一般會(huì)先看目錄樹判斷這個(gè)包是“源碼級(jí)整合”還是“二進(jìn)制級(jí)整合”。常見(jiàn)的懶人包結(jié)構(gòu)大致是這樣幾類內(nèi)容編譯好的 DCU 單元文件、運(yùn)行時(shí)包 BPL、設(shè)計(jì)期包 DPK 工程、皮膚文件.asz 或 .vsf、以及一個(gè)演示工程。DCU 是編譯產(chǎn)物版本敏感DPK 是設(shè)計(jì)期包的源碼工程決定你能否在 IDE 里看到控件面板。判斷標(biāo)準(zhǔn)很簡(jiǎn)單如果包里只有 DCU 沒(méi)有 DPK那它只適配特定 Delphi 版本換版本直接報(bào)“單元不兼容”。如果 DPK 齊全你可以在自己的 IDE 里重新編譯設(shè)計(jì)期包適配性就好很多。14.24 這個(gè)包按標(biāo)題描述是“含所有組件”意味著 DCU 和 DPK 應(yīng)該都在。目錄/文件類型作用接入時(shí)必須確認(rèn)的點(diǎn)編譯好的 DCU運(yùn)行時(shí)單元供工程引用編譯版本是否與你的 Delphi 一致DPK 設(shè)計(jì)期包在 IDE 注冊(cè)控件面板能否用當(dāng)前 IDE 重新編譯BPL 運(yùn)行時(shí)包動(dòng)態(tài)鏈接用是否與 DCU 同批次編譯皮膚文件換膚資源格式是 .asz 還是 .vsfDemo 工程參考用法能否直接編譯通過(guò)提示DCU 的編譯版本不匹配是最高頻的翻車點(diǎn)報(bào)錯(cuò)通常是“Unit was compiled with a different version of…”看到這個(gè)先懷疑版本不要懷疑代碼。2.2 把設(shè)計(jì)期包編進(jìn) IDEDPK 編譯與控件面板注冊(cè)接入的第一步不是寫業(yè)務(wù)代碼是讓控件出現(xiàn)在工具面板上。打開包里的 DPK 工程在 IDE 里直接 Build然后 Install。這一步會(huì)往 IDE 注冊(cè)一批控件注冊(cè)成功后工具面板會(huì)出現(xiàn) AlphaControls 分類。// 設(shè)計(jì)期包注冊(cè)單元的典型結(jié)構(gòu)不同版本類名略有差異 procedure Register; begin // 注冊(cè)核心控件到工具面板的 AlphaControls 頁(yè) RegisterComponents(AlphaControls, [ TsButton, // 換膚按鈕 TsEdit, // 換膚編輯框 TsPanel, // 換膚面板 TsSkinManager, // 皮膚管理器全局唯一 TsSkinProvider // 窗體級(jí)皮膚提供者 ]); end;這段代碼的邏輯是RegisterComponents的第一個(gè)參數(shù)是工具面板上的頁(yè)簽名第二個(gè)參數(shù)是控件類數(shù)組。編譯安裝后你在工具面板能看到這些控件才算接入成功。參數(shù)上要注意TsSkinManager通常一個(gè)工程只放一個(gè)放在主窗體上負(fù)責(zé)全局皮膚加載TsSkinProvider是每個(gè)窗體一個(gè)負(fù)責(zé)把皮膚應(yīng)用到當(dāng)前窗體。很多人只放了 Manager 忘了 Provider結(jié)果主窗體換膚了子窗體還是原生灰就是這里漏了。如果 Build 報(bào)錯(cuò)找不到某個(gè)單元先檢查包的搜索路徑Tools → Options → Library → Library Path有沒(méi)有把包目錄加進(jìn)去。路徑?jīng)]加編譯器找不到 DCU報(bào)錯(cuò)會(huì)指向 uses 子句。2.3 最小可運(yùn)行示例一個(gè)窗體加載皮膚并生效接入完成后先做一個(gè)最小驗(yàn)證不要一上來(lái)就改整個(gè)工程。新建一個(gè) VCL 工程主窗體上放一個(gè)TsSkinManager、一個(gè)TsSkinProvider、一個(gè)TsButton。procedure TForm1.FormCreate(Sender: TObject); begin // 指定皮膚文件路徑.asz 是 AlphaControls 的皮膚格式 TsSkinManager1.SkinName : Office2019Dark; // 激活皮膚引擎這一步不調(diào)用所有換膚控件都不生效 TsSkinManager1.Active : True; // 把當(dāng)前窗體交給 Provider 接管 TsSkinProvider1.SkinManager : TsSkinManager1; TsSkinProvider1.Form : Self; end;邏輯說(shuō)明SkinName指定要加載的皮膚名前提是皮膚文件已經(jīng)放在TsSkinManager1.Skins集合里或者放在可搜索的皮膚目錄下。Active : True是總開關(guān)不打開的話控件還是原生繪制。TsSkinProvider1.Form : Self把當(dāng)前窗體綁定到皮膚提供者這一步?jīng)Q定了窗體的非客戶區(qū)標(biāo)題欄、邊框是否也跟著換。參數(shù)上SkinName寫錯(cuò)不會(huì)報(bào)異常只會(huì)靜默回退到默認(rèn)皮膚所以換膚沒(méi)效果時(shí)先確認(rèn)皮膚名拼寫和文件是否存在。跑起來(lái)看到按鈕變成深色圓角標(biāo)題欄也跟著變說(shuō)明接入鏈路是通的。接下來(lái)才是往現(xiàn)有工程遷移。3. WebBrowser 皮膚激活與商業(yè)提示去除兩個(gè)“標(biāo)題承諾”怎么驗(yàn)證3.1 WebBrowser 控件吃皮膚的原理與驗(yàn)證方法標(biāo)題里專門提了“激活 WebBrowser 皮膚”這不是一句廢話。Delphi 自帶的TWebBrowser是 IE 內(nèi)核的 ActiveX 控件它自己繪制自己的滾動(dòng)條和邊框默認(rèn)不理會(huì) VCL 的皮膚引擎。AlphaControls 要讓它跟著換膚通常是通過(guò)掛鉤子Hook攔截它的窗口消息或者用一個(gè)換膚容器把它包起來(lái)。驗(yàn)證方法很直接放一個(gè)TWebBrowser加載一個(gè)本地 HTML看它的滾動(dòng)條是不是跟著皮膚變了。如果滾動(dòng)條還是系統(tǒng)默認(rèn)的灰色說(shuō)明 WebBrowser 皮膚沒(méi)激活。常見(jiàn)做法是確認(rèn)包里是否有專門的 WebBrowser 皮膚適配單元并在工程里引用它。// 偽代碼示意WebBrowser 皮膚適配通常需要在窗體創(chuàng)建后掛鉤 procedure TForm1.FormCreate(Sender: TObject); begin TsSkinManager1.Active : True; // 部分版本需要顯式開啟 WebBrowser 的皮膚支持 TsSkinManager1.Options : TsSkinManager1.Options [seSkinWebBrowser]; end;參數(shù)說(shuō)明seSkinWebBrowser這類選項(xiàng)在不同版本里名字可能不同有的版本是獨(dú)立單元控制。如果編譯報(bào)“標(biāo)識(shí)符未定義”說(shuō)明你的版本沒(méi)有這個(gè)選項(xiàng)需要查包里的適配單元。這一步不要硬猜直接看包里的 Demo 工程怎么寫的Demo 能跑通照抄它的初始化順序最穩(wěn)。3.2 商業(yè)皮膚提示的觸發(fā)點(diǎn)與去除后的邊界“去除商業(yè)皮膚提示”是懶人包的核心賣點(diǎn)之一。AlphaControls 的免費(fèi)版或未授權(quán)版在啟動(dòng)時(shí)會(huì)彈一個(gè)提示框或者在標(biāo)題欄加一行水印。去除的方式通常是改資源文件里的字符串或者替換掉檢查授權(quán)的那個(gè)單元。這里必須說(shuō)清楚邊界去除提示只影響顯示層不代表你獲得了商業(yè)授權(quán)。如果項(xiàng)目要正式發(fā)布授權(quán)問(wèn)題得自己解決懶人包解決的是“開發(fā)調(diào)試時(shí)不被彈窗打斷”這個(gè)具體問(wèn)題。驗(yàn)證方法連續(xù)啟動(dòng)程序五次看是否每次都干凈啟動(dòng)沒(méi)有延遲彈窗。有些去除方式不徹底會(huì)在特定皮膚下才觸發(fā)提示所以要多換幾個(gè)皮膚測(cè)。注意如果去除后程序在關(guān)閉時(shí)偶發(fā)異常大概率是授權(quán)檢查代碼被改壞了留了空指針。這種時(shí)候回退到未去除版本只做皮膚接入提示問(wèn)題單獨(dú)處理。3.3 皮膚文件管理與運(yùn)行時(shí)切換皮膚文件的管理是個(gè)容易被低估的活。.asz文件可以內(nèi)嵌到 exe 里也可以外置成目錄。內(nèi)嵌的好處是分發(fā)簡(jiǎn)單壞處是換皮膚要重新編譯。外置的好處是用戶能自己換壞處是文件丟了程序就回退默認(rèn)皮膚。procedure TForm1.LoadExternalSkin(const ASkinFile: string); begin if FileExists(ASkinFile) then begin // 從外部文件加載皮膚適合讓用戶自定義 TsSkinManager1.Skins.Clear; TsSkinManager1.Skins.Add; TsSkinManager1.Skins[0].SkinFile : ASkinFile; TsSkinManager1.SkinName : TsSkinManager1.Skins[0].Name; TsSkinManager1.Active : True; end else // 文件不存在時(shí)回退避免程序無(wú)皮膚可用 TsSkinManager1.Active : False; end;邏輯上先清空再添加是為了避免多次調(diào)用后皮膚集合膨脹。SkinFile指向磁盤路徑SkinName要跟文件內(nèi)部記錄的名字一致不一致就加載不上。運(yùn)行時(shí)切換皮膚時(shí)已經(jīng)打開的窗體需要重新應(yīng)用 Provider否則部分控件會(huì)殘留舊皮膚這個(gè)坑后面還會(huì)提。4. 避坑與排查整合包在真實(shí)工程里的五類翻車記錄4.1 現(xiàn)象編譯報(bào)“單元版本不兼容”原因DCU 與 IDE 版本錯(cuò)位這是整合包最高頻的問(wèn)題?,F(xiàn)象是 Build 時(shí)直接報(bào)某個(gè) DCU 是用不同版本編譯器編譯的。原因是懶人包里的 DCU 是針對(duì)某個(gè)特定 Delphi 版本編譯的你的 IDE 版本不同二進(jìn)制格式對(duì)不上。解決辦法有兩個(gè)一是找到對(duì)應(yīng)你 IDE 版本的包二是用包里的 DPK 源碼重新編譯一遍生成匹配當(dāng)前版本的 DCU。優(yōu)先選第二個(gè)因?yàn)樵创a在手適配性最好。4.2 現(xiàn)象主窗體換膚了子窗體還是原生樣式原因Provider 漏綁現(xiàn)象很迷惑主界面明明已經(jīng)是深色皮膚彈出的對(duì)話框還是灰的。原因是TsSkinProvider是窗體級(jí)的每個(gè)窗體都要綁一次。解決辦法是在子窗體的FormCreate里也加上 Provider 綁定或者寫一個(gè)基類窗體把綁定邏輯放在基類里所有子窗體繼承它。后者更省事也避免以后新增窗體又漏掉。4.3 現(xiàn)象換膚后表格滾動(dòng)條閃爍原因雙緩沖未開啟數(shù)據(jù)量大的表格在換膚后滾動(dòng)會(huì)閃這是皮膚引擎重繪和 VCL 原生重繪打架。解決辦法是給表格控件開啟雙緩沖。常見(jiàn)做法是設(shè)置DoubleBuffered : True或者在皮膚管理器的選項(xiàng)里開啟全局雙緩沖。參數(shù)上注意雙緩沖會(huì)略微增加內(nèi)存占用但在現(xiàn)代機(jī)器上可以忽略。4.4 現(xiàn)象WebBrowser 加載頁(yè)面后皮膚失效原因ActiveX 窗口重建WebBrowser 每次導(dǎo)航到新頁(yè)面內(nèi)部的 ActiveX 窗口會(huì)重建之前掛的皮膚鉤子就丟了?,F(xiàn)象是第一次加載有皮膚點(diǎn)個(gè)鏈接就變回原生滾動(dòng)條。解決辦法是在OnDocumentComplete事件里重新應(yīng)用一次皮膚鉤子。這個(gè)坑很隱蔽因?yàn)椴粚?dǎo)航就看不出來(lái)。4.5 現(xiàn)象程序啟動(dòng)變慢原因皮膚文件過(guò)大或加載時(shí)機(jī)不對(duì)換膚后啟動(dòng)明顯變慢尤其是內(nèi)嵌大皮膚文件時(shí)。原因是皮膚文件在窗體創(chuàng)建前就被完整解析。解決辦法是把皮膚加載挪到主窗體顯示之后或者用 splash 窗口先頂著。參數(shù)上皮膚文件不是越大越好帶大量位圖的皮膚解析慢純矢量風(fēng)格的皮膚輕很多。選皮膚時(shí)把體積也納入考量。5. 進(jìn)階把皮膚引擎做成可配置項(xiàng)與版本升級(jí)的后悔藥走到這一步工程已經(jīng)能跑起來(lái)了。但真正讓這套東西在項(xiàng)目里活得久的是把它做成可配置、可回退的。我一般會(huì)做三件事。第一件把皮膚配置外置成 ini 或 json讓程序啟動(dòng)時(shí)讀配置決定加載哪個(gè)皮膚、是否啟用 WebBrowser 皮膚。這樣現(xiàn)場(chǎng)出問(wèn)題改配置就行不用重新編譯。procedure TForm1.LoadSkinConfig; var cfg: TIniFile; begin cfg : TIniFile.Create(ExtractFilePath(ParamStr(0)) skin.ini); try // 從配置讀取皮膚名讀不到就用默認(rèn)值 TsSkinManager1.SkinName : cfg.ReadString(Skin, Name, Office2019Dark); TsSkinManager1.Active : cfg.ReadBool(Skin, Enabled, True); finally cfg.Free; end; end;第二件保留一份未修改的原始包。去除商業(yè)提示這類改動(dòng)一旦后續(xù)版本升級(jí)改動(dòng)點(diǎn)可能對(duì)不上。留著原始包升級(jí)時(shí)用 diff 對(duì)比能快速定位哪些文件被改過(guò)。這就是后悔藥。第三件皮膚切換后強(qiáng)制刷新所有已打開窗體。很多人切換皮膚只改了 Manager已打開的窗體不刷新看起來(lái)像沒(méi)生效。procedure TForm1.RefreshAllForms; var i: Integer; begin // 遍歷所有已創(chuàng)建窗體重新應(yīng)用皮膚提供者 for i : 0 to Screen.FormCount - 1 do begin if Screen.Forms[i].Active then Screen.Forms[i].Perform(WM_NCPAINT, 0, 0); end; end;這段的邏輯是遍歷當(dāng)前所有窗體觸發(fā)非客戶區(qū)重繪讓標(biāo)題欄和邊框跟著新皮膚走。WM_NCPAINT是重繪非客戶區(qū)的消息比整個(gè)窗體重建輕量。配置項(xiàng)作用建議默認(rèn)值Skin.Name指定皮膚跟隨項(xiàng)目風(fēng)格Skin.Enabled總開關(guān)TrueSkin.WebBrowser瀏覽器控件換膚按需Skin.DoubleBuffer全局雙緩沖True從那以后我每次接入這類整合包都強(qiáng)制先跑一遍最小驗(yàn)證工程確認(rèn) DCU 版本、Provider 綁定、WebBrowser 皮膚三件事都通了再往正式工程里遷。這個(gè)習(xí)慣幫我省了至少兩次整工程回滾。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取