戰(zhàn)與常見(jiàn)問(wèn)題排查)
簡(jiǎn)介EhLib 11.0.21 - D12是一套專為Delphi 12開(kāi)發(fā)者調(diào)優(yōu)的高性能VCL界面控件庫(kù)聚焦數(shù)據(jù)網(wǎng)格、多類型圖表、數(shù)據(jù)庫(kù)綁定、打印與導(dǎo)出等高頻需求適合快速構(gòu)建報(bào)表系統(tǒng)、MIS管理界面和數(shù)據(jù)密集型桌面應(yīng)用??丶С衷O(shè)計(jì)期高度定制可調(diào)整網(wǎng)格行列、圖表配色與報(bào)表布局運(yùn)行時(shí)提供排序、過(guò)濾、分頁(yè)、批量導(dǎo)出等交互能力減少重復(fù)開(kāi)發(fā)工作量。資源包共收錄2000個(gè)文件壓縮包大小67.79MB文件類型覆蓋519個(gè)dfm窗體布局、293個(gè)pas核心源碼、196個(gè)dproj工程定義、170個(gè)dpk包描述以及chm幫助、docx文檔、ico圖標(biāo)、png圖片等素材既可直接安裝組件也可對(duì)照示例代碼與幫助文檔快速上手。目前已有1407人學(xué)習(xí)下載適合有一定Delphi基礎(chǔ)、希望減少界面開(kāi)發(fā)量并提升用戶體驗(yàn)的開(kāi)發(fā)者在實(shí)際項(xiàng)目中直接復(fù)用。 老Delphi玩家的工具箱里控件庫(kù)這東西從來(lái)不是“裝上一個(gè)包然后一直用到老”那么簡(jiǎn)單。尤其是做數(shù)據(jù)庫(kù)應(yīng)用的人提到第三方表格控件幾乎繞不開(kāi)EhLib這個(gè)名字。前陣子我把項(xiàng)目遷到RAD Studio 12上第一件事就是把EhLib升到11.0.21這個(gè)專門(mén)標(biāo)注D12兼容的版本中間踩了不少坑也把安裝順序、路徑配置、運(yùn)行期報(bào)錯(cuò)都摸了一遍。這篇文章就當(dāng)一份安裝與實(shí)戰(zhàn)備忘給正準(zhǔn)備升級(jí)IDE、或者還在糾結(jié)要不要用第三方網(wǎng)格控件的朋友做個(gè)參考。1. 為什么在Delphi 12還值得配置EhLib1.1 原生DBGrid做不到的那些事Delphi自帶的TDBGrid夠輕夠快但真到做業(yè)務(wù)系統(tǒng)時(shí)你會(huì)很快發(fā)現(xiàn)它太“素”了??蛻粢谋砀裢ǔiL(zhǎng)這樣點(diǎn)列頭能下拉篩選、底部有合計(jì)行、某些列是關(guān)聯(lián)下拉選擇、導(dǎo)出成Excel時(shí)不能亂碼甚至還要多行表頭、單元格合并、分組統(tǒng)計(jì)。這些東西用原生DBGrid做不是說(shuō)做不出來(lái)而是要自己寫(xiě)一堆事件、畫(huà)布繪制和臨時(shí)組件維護(hù)成本極高。EhLib 11.0.21給VCL開(kāi)發(fā)者提供的就是一套開(kāi)箱即用的解決方案。它的核心組件TDBGridEh在原生網(wǎng)格基礎(chǔ)上做了大量增強(qiáng)自動(dòng)篩選行、字段匯總腳、多級(jí)表頭、下拉過(guò)濾、排序指示、樹(shù)形結(jié)構(gòu)展示、導(dǎo)入導(dǎo)出接口全都有。換句話說(shuō)它把Excel里常用的那些表格交互搬進(jìn)了Delphi而且是純VCL組件不額外引入重框架這對(duì)老項(xiàng)目遷移特別友好。1.2 版本號(hào)里的門(mén)道為什么認(rèn)準(zhǔn)D12EhLib的版本號(hào)和Delphi版本綁定得很緊。11.0.21這個(gè)版本標(biāo)的D12意思是官方已經(jīng)針對(duì)RAD Studio 12的RTL、編譯器特性和包格式做過(guò)適配。這里有個(gè)容易踩的坑拿舊版EhLib硬裝到新IDE上雖然解壓、加路徑都順利但編譯時(shí)往往直接報(bào)“Unit not found”或者一連串的HPPEMIT錯(cuò)誤。為什么第三方控件必須跟著IDE版本走因?yàn)镈elphi的RTL頭文件、System單元、Winapi聲明在不同版本間有差異控件源碼如果調(diào)用了被廢棄的API或者引用了舊的結(jié)構(gòu)體在新編譯器下就過(guò)不去。EhLib這種大型控件庫(kù)內(nèi)部對(duì)Windows API的封裝很多官方不做適配自己改起來(lái)非常痛苦。所以我個(gè)人的建議是既然官方打了D12的適配包就老老實(shí)實(shí)用11.0.21別為了“省事”沿用老版本。1.3 安裝成本其實(shí)不高但順序錯(cuò)了很折騰很多人一聽(tīng)第三方控件就頭大覺(jué)得要改一堆環(huán)境變量、編譯半天。實(shí)際上EhLib 11.0.21安裝不復(fù)雜核心就三步解壓源碼包、編譯運(yùn)行時(shí)包、安裝設(shè)計(jì)時(shí)包。真正煩人的是順序問(wèn)題——先裝哪個(gè)包、后裝哪個(gè)包、路徑加不加對(duì)直接影響后續(xù)使用體驗(yàn)。我把安裝前后需要確認(rèn)的事項(xiàng)整理成了表格照著檢查一遍基本不會(huì)翻車階段要確認(rèn)的事項(xiàng)安裝前IDE已關(guān)閉舊版EhLib路徑已從Library路徑移除項(xiàng)目源碼有備份安裝中運(yùn)行時(shí)包先編譯設(shè)計(jì)時(shí)包后InstallWin32和Win64都Build安裝后組件面板出現(xiàn)EhLib頁(yè)簽Library路徑優(yōu)先級(jí)正確新建項(xiàng)目拖一個(gè)TDBGridEh測(cè)試2. 上手安裝從解壓到組件面板出現(xiàn)2.1 環(huán)境準(zhǔn)備與備份開(kāi)始前先把RAD Studio 12完全關(guān)掉一個(gè)實(shí)例都不要留。接著建議把當(dāng)前項(xiàng)目里所有引用EhLib舊版本的搜索路徑刪干凈否則后面編譯新項(xiàng)目時(shí)編譯器可能優(yōu)先找到舊目錄里的DCU產(chǎn)生一堆莫名其妙的類型不匹配。然后是備份。因?yàn)镋hLib類組件分布在很多單元里萬(wàn)一你項(xiàng)目里同時(shí)用了舊版升級(jí)后可能會(huì)出現(xiàn)屬性變化、方法簽名不同這類問(wèn)題。我曾經(jīng)因?yàn)闆](méi)有備份升級(jí)后有個(gè)自定義排序事件編譯不過(guò)改了半天才想起來(lái)OldGrid是舊版專屬寫(xiě)法。所以別嫌麻煩先把整個(gè)項(xiàng)目目錄壓縮一份放旁邊出問(wèn)題能隨時(shí)回退。源碼解壓路徑也有講究。建議放到一個(gè)純英文、無(wú)空格的固定目錄下比如D:\Components\EhLib。放在包含空格或中文的路徑里雖然現(xiàn)代IDE大多能處理但個(gè)別第三方構(gòu)建工具、腳本會(huì)在路徑解析上出幺蛾子。我見(jiàn)過(guò)一個(gè)同行把控件裝到桌面結(jié)果每次打開(kāi)項(xiàng)目都要重新查找源文件就是因?yàn)槁窂嚼飵е厥庾址?.2 包編譯的正確順序EhLib 11.0.21安裝包里通常會(huì)配套好按Delphi版本區(qū)分的子目錄進(jìn)入對(duì)應(yīng)D12的目錄后你會(huì)看到兩類包一類是運(yùn)行時(shí)包不帶設(shè)計(jì)時(shí)注冊(cè)邏輯一類是設(shè)計(jì)時(shí)包帶Dcl前綴或者Install標(biāo)識(shí)。我的編譯習(xí)慣是這樣的先打開(kāi)運(yùn)行時(shí)包項(xiàng)目在Project Manager里切到Win32平臺(tái)執(zhí)行Build。注意是Build不是CompileBuild會(huì)把所有引用的源碼重新編譯一遍確保當(dāng)前IDE環(huán)境下的DCU是最新的。然后再切到Win64平臺(tái)同樣Build一次。之后打開(kāi)設(shè)計(jì)時(shí)包右鍵執(zhí)行Install。順序?yàn)槭裁床荒芊匆驗(yàn)樵O(shè)計(jì)時(shí)包要引用運(yùn)行時(shí)包導(dǎo)出的符號(hào)。如果運(yùn)行時(shí)包還沒(méi)編譯好Install時(shí)就會(huì)報(bào)“Cant load package”或者找不到類名更奇怪的是組件面板上可能會(huì)顯示一個(gè)灰色不可用的條目。最穩(wěn)妥的辦法就是嚴(yán)格執(zhí)行先運(yùn)行時(shí)后設(shè)計(jì)時(shí)。2.3 路徑配置這步別偷懶很多人在這一步偷懶結(jié)果新建項(xiàng)目時(shí)從組件面板拖控件沒(méi)問(wèn)題一編譯就報(bào)找不到單元“DBGridEh.pas”。這是因?yàn)橥峡丶r(shí)IDE加載的是BPL包里已經(jīng)編譯好的類但項(xiàng)目編譯時(shí)需要根據(jù)源文件重新定位這些單元。所以必須在Tools Options Language Delphi Library Library paths里把EhLib的關(guān)鍵源碼目錄加進(jìn)去具體包括Common目錄、DBGridEh目錄這類包含核心單元的文件路徑。這里我強(qiáng)烈建議用環(huán)境變量或IDE別名的方式配置路徑而不是寫(xiě)死絕對(duì)路徑。比如設(shè)置一個(gè)EHLIB_ROOT環(huán)境變量然后路徑寫(xiě)成%EHLIB_ROOT%\Common。這樣以后換目錄或者升級(jí)版本只需要改環(huán)境變量不用全局搜索替換。2.4 64位項(xiàng)目的附加操作Delphi 12默認(rèn)支持64位編譯但很多人在裝了控件后會(huì)發(fā)現(xiàn)32位編譯正常切換Win64后直接編譯失敗。原因很簡(jiǎn)單控件的運(yùn)行時(shí)包沒(méi)有編譯64位版本。雖然設(shè)計(jì)時(shí)組件是在32位IDE里運(yùn)行的但你的程序在64位平臺(tái)下需要對(duì)應(yīng)64位的DCU和BPL。解決辦法就是上面說(shuō)的在包項(xiàng)目里切到Win64平臺(tái)重新Build一遍。有的人為了方便直接讓項(xiàng)目只編譯32位眼不見(jiàn)心不煩。但現(xiàn)代業(yè)務(wù)系統(tǒng)連接第三方庫(kù)、調(diào)用系統(tǒng)API64位幾乎是大勢(shì)所趨。所以建議從安裝起就把兩個(gè)平臺(tái)都編譯好省得以后項(xiàng)目切平臺(tái)時(shí)被控件卡住。3. 高頻使用場(chǎng)景與細(xì)節(jié)拆解3.1 自動(dòng)篩選讓表格自帶Excel式過(guò)濾TDBGridEh最吸引人的功能之一就是自動(dòng)篩選。開(kāi)啟方式很簡(jiǎn)單在設(shè)計(jì)期把OptionsEh屬性中加上dghAutoFilterRow或者運(yùn)行時(shí)通過(guò)代碼操作DBGridEh1.OptionsEh : DBGridEh1.OptionsEh [dghAutoFilterRow];開(kāi)啟后表格列頭下方會(huì)出現(xiàn)一個(gè)篩選行用戶可以按鍵值精確篩選也可以通過(guò)下拉列表選擇唯一值。對(duì)于帶日期的列還能配置成區(qū)間篩選配合STFilter相關(guān)屬性體驗(yàn)非常接近Excel。但這里有個(gè)坑必須提醒EhLib的自動(dòng)篩選默認(rèn)是基于當(dāng)前數(shù)據(jù)集的過(guò)濾機(jī)制。如果數(shù)據(jù)庫(kù)端已經(jīng)有大量數(shù)據(jù)被加載到客戶端篩選時(shí)會(huì)有明顯卡頓。所以做篩選設(shè)計(jì)時(shí)最好提前在SQL層把范圍縮小或者只在必要時(shí)啟用自動(dòng)篩選別每個(gè)窗體都無(wú)腦打開(kāi)。3.2 多表頭、匯總行與下拉列表業(yè)務(wù)系統(tǒng)里經(jīng)常需要“產(chǎn)品名稱”、“產(chǎn)品編號(hào)”這類分組表頭。原生TDBGrid實(shí)現(xiàn)起來(lái)要重繪而TDBGridEh直接支持父子列結(jié)構(gòu)。在設(shè)計(jì)器里右鍵Columns創(chuàng)建父列后把子列拖到父列下方即可。父列作為分組標(biāo)題子列綁定字段運(yùn)行時(shí)還能和普通列一樣排序篩選。匯總行是我覺(jué)得最實(shí)用的功能之一。把DBGridEh1.FooterRowCount設(shè)為1然后找到對(duì)應(yīng)列的Footer屬性設(shè)置ValueType為fvtSum、fvtCount、fvtAvg底部就會(huì)自動(dòng)顯示統(tǒng)計(jì)值。這一步操作比在SQL里寫(xiě)ROLLUP靈活尤其是需要同時(shí)顯示明細(xì)和統(tǒng)計(jì)的時(shí)候。下拉列表對(duì)應(yīng)的是列屬性里的PickList或LookupParams。PickList適合靜態(tài)選項(xiàng)比如“啟用/禁用”LookupParams適合關(guān)聯(lián)主表比如從部門(mén)表中選擇部門(mén)名稱顯示名稱同時(shí)可以把部門(mén)ID回寫(xiě)到字段里。這在錄入界面里非常常用等于給表格加了數(shù)據(jù)驗(yàn)證避免臟數(shù)據(jù)。3.3 導(dǎo)出Excel時(shí)的中文和格式坑導(dǎo)出Excel是項(xiàng)目里無(wú)法回避的需求。EhLib 11.x在這塊做得很成熟TDBGridEh自帶SaveToXLSX方法一行代碼就能生成真正的xlsx文件不再依賴OLE和Excel進(jìn)程DBGridEh1.SaveToXLSX(D:\temp\員工列表.xlsx);老版本里常用的TDBGridEhExportToExcel方式還可以用但新方法更簡(jiǎn)潔而且不會(huì)出現(xiàn)“電腦沒(méi)裝Excel就導(dǎo)出失敗”的問(wèn)題。不過(guò)導(dǎo)出Excel時(shí)有兩個(gè)細(xì)節(jié)要留意。第一個(gè)是中文文件名和內(nèi)容亂碼。雖然新版的XLSX格式默認(rèn)走UTF-8基本不會(huì)亂碼但如果你在舊項(xiàng)目里沿用老導(dǎo)出組件一定要檢查字符集設(shè)置。第二個(gè)是格式。ExportExportExcel默認(rèn)導(dǎo)出的格式是比較樸素的不會(huì)帶上你在網(wǎng)格里設(shè)置的列寬、字體和背景色。所以我的經(jīng)驗(yàn)是先通過(guò)代碼臨時(shí)設(shè)置列寬和標(biāo)題樣式導(dǎo)出完成后再還原或者在導(dǎo)出對(duì)象上設(shè)置對(duì)應(yīng)的樣式屬性。3.4 大數(shù)據(jù)量下的兩種優(yōu)化思路很多團(tuán)隊(duì)在數(shù)據(jù)量上來(lái)之后抱怨EhLib卡其實(shí)網(wǎng)格渲染本身并不慢瓶頸多半在數(shù)據(jù)加載和篩選邏輯上。這里分享兩個(gè)思路。第一種是用VirtualMode。如果數(shù)據(jù)不需要編輯只是展示可以直接開(kāi)啟VirtualMode配合OnGetCellParams等事件按需提供數(shù)據(jù)。這樣網(wǎng)格不會(huì)一次性創(chuàng)建大量行數(shù)據(jù)內(nèi)存占用顯著下降。第二種是使用SearchPanel替代自動(dòng)篩選。SearchPanel是EhLib提供的搜索面板通過(guò)輸入關(guān)鍵詞快速定位不會(huì)觸發(fā)數(shù)據(jù)集全量掃描比自動(dòng)篩選在大數(shù)據(jù)量下表現(xiàn)更好。實(shí)測(cè)下來(lái)幾十萬(wàn)行的場(chǎng)景普通方式加載可能要兩三秒而用VirtualMode配合分頁(yè)查詢首屏呈現(xiàn)幾乎無(wú)感知。當(dāng)然這兩個(gè)方案都需要調(diào)整數(shù)據(jù)獲取方式不是純靠控件一個(gè)屬性就能解決。4. 報(bào)錯(cuò)實(shí)錄我踩過(guò)的坑和排查方法4.1 cannot perform this operation on an open dataset這個(gè)報(bào)錯(cuò)我在論壇上見(jiàn)過(guò)無(wú)數(shù)次自己也踩過(guò)。它本身是數(shù)據(jù)集組件報(bào)的但經(jīng)常在EhLib相關(guān)窗體里出現(xiàn)原因是數(shù)據(jù)集已經(jīng)打開(kāi)卻又執(zhí)行了Open操作。最常見(jiàn)的場(chǎng)景設(shè)計(jì)期在數(shù)據(jù)模塊里把FDQuery或ADOQuery的Active設(shè)為T(mén)rue然后在FormShow里又寫(xiě)了一次Query.Open。設(shè)計(jì)期數(shù)據(jù)集已經(jīng)處于打開(kāi)狀態(tài)運(yùn)行時(shí)代碼再Open一次就會(huì)觸發(fā)這個(gè)異常。排查方法很簡(jiǎn)單全局搜索.Open關(guān)鍵字逐個(gè)檢查是否有重復(fù)打開(kāi)再看數(shù)據(jù)模塊里所有數(shù)據(jù)集的Active屬性運(yùn)行期的打開(kāi)操作統(tǒng)一交給代碼。如果確實(shí)需要重復(fù)執(zhí)行某個(gè)查詢先判斷狀態(tài)if not FDQuery1.Active then FDQuery1.Open;這個(gè)錯(cuò)誤和EhLib本身關(guān)系不大但當(dāng)頁(yè)面里有多個(gè)DbAware控件時(shí)數(shù)據(jù)集狀態(tài)變化連鎖觸發(fā)網(wǎng)格重繪報(bào)錯(cuò)時(shí)機(jī)看起來(lái)就特別像控件問(wèn)題。4.2 無(wú)效的授權(quán)說(shuō)明這個(gè)問(wèn)題大多出現(xiàn)在使用非官方渠道下載、或者從舊版機(jī)械復(fù)制文件到新環(huán)境的場(chǎng)景。表現(xiàn)是編譯時(shí)提示授權(quán)無(wú)效或者運(yùn)行時(shí)控件進(jìn)入演示模式功能受限。處理思路一般分三步第一步徹底清理舊版殘留包括項(xiàng)目搜索路徑、IDE Packages列表和BPL、DCP文件。第二步重新從官方完整包解壓安裝時(shí)確認(rèn)使用的是對(duì)應(yīng)D12的目錄。第三步核對(duì)License文件是否存在于正確位置以及環(huán)境變量里有沒(méi)有指向舊版授權(quán)目錄的配置。這里要多說(shuō)一句正規(guī)商業(yè)授權(quán)在官網(wǎng)購(gòu)買(mǎi)后下載到的安裝包里已經(jīng)包含有效許可不需要額外破解。遇到授權(quán)問(wèn)題優(yōu)先檢查自己的路徑和殘留不要繞過(guò)正規(guī)渠道。4.3 安裝了但組件面板找不到安裝過(guò)程沒(méi)報(bào)錯(cuò)但打開(kāi)組件面板就是看不到EhLib頁(yè)簽。出現(xiàn)這種情況先別急著重裝。首先確認(rèn)設(shè)計(jì)時(shí)包真的Install成功在Component Install Packages里能查到并且勾選狀態(tài)正常。然后關(guān)閉IDE刪除AppData下對(duì)應(yīng)版本的IDE緩存目錄重啟后再看。如果還是沒(méi)有檢查是否裝到了錯(cuò)誤的平臺(tái)比如IDE以管理員權(quán)限運(yùn)行時(shí)組件的用戶級(jí)路徑和系統(tǒng)級(jí)路徑不一致。還有一個(gè)容易被忽略的點(diǎn)如果電腦上裝有多個(gè)Delphi版本安裝包默認(rèn)路徑可能指向了舊版本目錄導(dǎo)致新IDE沒(méi)加載到。安裝時(shí)留意路徑選擇別讓它默認(rèn)到上一個(gè)Delphi版本的組件庫(kù)里。4.4 編譯報(bào)錯(cuò)與版本混用錯(cuò)誤提示類似“E2201 Need imported data”或者“Unit was compiled with a different version of...”十有八九是DCU和當(dāng)前IDE版本不匹配。這通常是因?yàn)長(zhǎng)ibrary path里殘留了舊EhLib目錄編譯器優(yōu)先索引了舊DCU。解決辦法是把新增的EhLib源碼目錄放在Library路徑列表的前面同時(shí)刪除舊目錄引用。項(xiàng)目級(jí)別的Search Path也要檢查一遍有些老項(xiàng)目會(huì)在DPR里寫(xiě)死絕對(duì)路徑這個(gè)最容易漏。我的做法是全局搜索項(xiàng)目中所有包含“EhLib”字樣的路徑逐項(xiàng)確認(rèn)版本確保整個(gè)項(xiàng)目只引用一套控件源碼。5. 一點(diǎn)個(gè)人心得在我最近的項(xiàng)目里最滿意的組合是FDQuery關(guān)聯(lián)TDBGridEh打開(kāi)自動(dòng)篩選配上Footer合計(jì)和SaveToXLSX導(dǎo)出。這套搭配在錄入、查詢、統(tǒng)計(jì)、導(dǎo)出幾個(gè)核心場(chǎng)景都不需要額外造輪子業(yè)務(wù)代碼可以專注于數(shù)據(jù)邏輯而不是表格交互細(xì)節(jié)。最后順便分享一個(gè)小習(xí)慣如果是長(zhǎng)期維護(hù)的項(xiàng)目建議把TDBGridEh封裝到一個(gè)基礎(chǔ)Frame里公共屬性統(tǒng)一在Frame初始化時(shí)設(shè)置。比如自動(dòng)篩選、匯總行這些默認(rèn)開(kāi)啟個(gè)別頁(yè)面再按需調(diào)整。這樣做能減少重復(fù)配置也能避免不同窗體之間表格行為不一致后面對(duì)接新需求時(shí)改一處就能全局生效。本文還有配套的精品資源點(diǎn)擊獲取