表控件遷移與實(shí)操指南)
簡(jiǎn)介Delphi作為經(jīng)典的RAD開(kāi)發(fā)工具其VCL框架的向后兼容性讓老項(xiàng)目得以延續(xù)但第三方報(bào)表控件如何匹配不同IDE版本成為開(kāi)發(fā)者常見(jiàn)痛點(diǎn)。ReportMachine作為一款跨版本的報(bào)表控件通過(guò)完整的編譯矩陣支持Delphi 5至XE12其設(shè)計(jì)期/運(yùn)行期包分離機(jī)制與Band打印模型為報(bào)表開(kāi)發(fā)提供了靈活的架構(gòu)基礎(chǔ)。在實(shí)際工程中合理安裝控件包、配置Library路徑、處理運(yùn)行時(shí)包依賴是保障項(xiàng)目順利遷移的關(guān)鍵。面對(duì)預(yù)覽閃退、導(dǎo)出亂碼等問(wèn)題需從字體、數(shù)據(jù)源、緩存等多個(gè)維度逐一排查。本文以Delphi 12.3環(huán)境安裝ReportMachine 7.0為例分享從解壓安裝到報(bào)表遷移的完整實(shí)操流程幫助開(kāi)發(fā)者避開(kāi)常見(jiàn)陷阱高效維護(hù)老項(xiàng)目中的報(bào)表模塊。 Delphi 12.3里清一色的紅波浪線那個(gè)瞬間我有點(diǎn)恍惚。項(xiàng)目文件里躺著幾十個(gè)報(bào)表窗體每一個(gè)都引用了ReportMachine的單元而IDE的組件面板上卻連一個(gè)報(bào)表控件的影子都找不到。這個(gè)場(chǎng)景對(duì)維護(hù)老Delphi項(xiàng)目的人來(lái)說(shuō)應(yīng)該不陌生——報(bào)表控件作為項(xiàng)目里幾乎繞不開(kāi)的依賴一旦和IDE版本不對(duì)付整個(gè)模塊直接癱瘓。所以當(dāng)朋友把這份ReportMachine 7.0 for D5-XE12的包丟過(guò)來(lái)的時(shí)候我第一反應(yīng)是終于可以從頭捋一捋為什么一個(gè)報(bào)表控件要跨這么多Delphi版本以及在新版Delphi 12.3里裝它、用它、遷移老項(xiàng)目到底有哪些坑。這篇就當(dāng)作一份實(shí)操記錄給所有被報(bào)表控件折騰過(guò)的Delphi開(kāi)發(fā)者參考。1. 先把標(biāo)題拆透D5-XE12、HH、24.9.29到底代表什么1.1 一個(gè)報(bào)表控件為什么要在意“從D5到XE12”很多人看到“for D5-XE12”的第一反應(yīng)是“支持好多版本真厲害”但老Delphi開(kāi)發(fā)者應(yīng)該立刻會(huì)心一笑這背后其實(shí)是Delphi生態(tài)里最獨(dú)特的“跨代兼容”問(wèn)題。從1999年的Delphi 5到如今的12.3 AthensVCL框架保持了驚人的向后兼容性——二十多年前寫(xiě)的窗體代碼大部分在今天依然能編譯、能運(yùn)行。這既是優(yōu)勢(shì)也是負(fù)擔(dān)老項(xiàng)目賴著不升級(jí)新工具又必須兼容老環(huán)境于是像報(bào)表控件這種和窗體高度綁定的組件就得跟著搞出一套橫跨二十多個(gè)IDE版本的編譯矩陣。對(duì)維護(hù)老項(xiàng)目的人來(lái)說(shuō)這個(gè)矩陣就是報(bào)表控件的“生死線”。我在項(xiàng)目里見(jiàn)過(guò)太多因?yàn)閳?bào)表控件不支持新版本而被逼著重寫(xiě)報(bào)表的團(tuán)隊(duì)那才是真正的災(zāi)難報(bào)表數(shù)量動(dòng)輒幾十上百?gòu)埫繌埨锶麧M了業(yè)務(wù)邏輯重寫(xiě)一輪少說(shuō)一兩個(gè)月。所以選報(bào)表控件時(shí)“支持版本范圍”不是參數(shù)是剛需。ReportMachine在這個(gè)領(lǐng)域能一直有聲音靠的就是把D5到XE12這條線完整串了起來(lái)不管你是從老古董項(xiàng)目里割舍不掉的Delphi 7還是已經(jīng)追到Delphi 12.3的嘗鮮派它都給你留了對(duì)應(yīng)的安裝包和源碼。1.2 打開(kāi)7z包之后先看什么目錄結(jié)構(gòu)決定安裝策略拿到這份7.7z壓縮包別急著解壓后雙擊就裝。先看目錄結(jié)構(gòu)一份靠譜的控件發(fā)行包通常包含幾塊Source完整源碼排查問(wèn)題、定制行為全靠它、Packages或Compiled按Delphi版本預(yù)編譯好的包文件、Demos示例工程強(qiáng)烈建議先打開(kāi)看、Help文檔。這些目錄只要缺了Source往后出了問(wèn)題你連改的機(jī)會(huì)都沒(méi)有基本可以判斷是個(gè)不完整的包。Delphi控件包里的編譯產(chǎn)物有幾類(lèi)關(guān)鍵后綴要認(rèn)識(shí).dpk是包工程文件.bpl是運(yùn)行時(shí)或設(shè)計(jì)時(shí)包.dcu是編譯好的單元文件。你的IDE能識(shí)別控件本質(zhì)上就是在Package列表里正確加載了對(duì)應(yīng)的.bpl。判斷這份包支持哪些版本直接看Packages目錄下的子目錄命名就行——常見(jiàn)的有D5、D7、D10、D11、D12這種代號(hào)也有些包用版本號(hào)數(shù)字標(biāo)識(shí)。打開(kāi)Delphi 12.3對(duì)應(yīng)的目錄里面應(yīng)該有至少兩個(gè).dpk一個(gè)給運(yùn)行時(shí)用一個(gè)給設(shè)計(jì)時(shí)用后者通常以dcl開(kāi)頭。認(rèn)準(zhǔn)這個(gè)規(guī)律哪怕?lián)Q一個(gè)控件包你也能快速定位該打開(kāi)哪個(gè)文件。1.3 “HH 24.9.29”這類(lèi)命名背后的打包慣例標(biāo)題里的“HH 24.9.29”不是官方版本號(hào)而是打包者自定的構(gòu)建標(biāo)識(shí)。在控件分發(fā)、整理的圈子里“字母縮寫(xiě)日期”的命名方式太常見(jiàn)了HH大概率是作者或分發(fā)渠道的標(biāo)記24.9.29是構(gòu)建日期至于是2024年9月29日還是某個(gè)周期號(hào)要結(jié)合包的發(fā)布時(shí)間判斷。這類(lèi)命名雖然不影響使用但有一個(gè)實(shí)際價(jià)值——它能幫你判斷這個(gè)包是不是針對(duì)最新IDE做過(guò)適配。比如包名里出現(xiàn)24年9月的日期那說(shuō)明打包者在這之后至少整理過(guò)一次對(duì)Delphi 12.3的支持大概率是驗(yàn)證過(guò)的。2. 安裝ReportMachine 7.0新手最容易翻車(chē)的三個(gè)環(huán)節(jié)2.1 解壓路徑與IDE搜索路徑先找個(gè)“干凈的窩”安裝控件的第一步不是安裝是選路徑。我強(qiáng)烈建議把控件源碼解壓到一個(gè)固定目錄比如D:\Components\ReportMachine7然后把這個(gè)路徑加入IDE的Library路徑。但這里有個(gè)關(guān)鍵區(qū)別Tools Options Language Delphi Library里的Library Path管的是編譯時(shí)能不能找到.dcu和源文件Browsing Path管的是代碼編輯器里能不能Ctrl點(diǎn)擊跳轉(zhuǎn)。很多人裝完控件編譯報(bào)錯(cuò)“Unit not found”十有八九是Library Path沒(méi)加對(duì)或者只加了Browsing Path。另外還有兩個(gè)特別容易被忽略的點(diǎn)。第一路徑里最好不要有中文和空格部分老版本控件在帶空格的路徑下編譯會(huì)出詭異問(wèn)題我都懷疑是不是底層make工具的鍋但實(shí)測(cè)確實(shí)如此。第二IDE必須以管理員身份運(yùn)行——控件包在安裝設(shè)計(jì)期包時(shí)需要往IDE的安裝目錄寫(xiě)入文件如果權(quán)限不夠打開(kāi)時(shí)看似成功重啟后控件就是消失這種“假成功”最耗人耐心。所以我在安裝任何控件前都會(huì)先檢查一下IDE是不是管理員模式省得后面白折騰。2.2 編譯順序與包管理Runtime和Design-Time不能搞反ReportMachine這類(lèi)帶設(shè)計(jì)器的控件安裝時(shí)一定要分兩步走先編譯運(yùn)行期包Runtime Package再安裝設(shè)計(jì)期包Design-Time Package。順序反了IDE大概率會(huì)報(bào)“Cant load package”或者“Package xxx requires package yyy”。原因其實(shí)不難理解設(shè)計(jì)期包里的控件注冊(cè)代碼依賴運(yùn)行期包的單元運(yùn)行期包沒(méi)先編譯好設(shè)計(jì)期包加載時(shí)自然找不到依賴。我走的標(biāo)準(zhǔn)流程是這樣在Packages目錄里找到對(duì)應(yīng)Delphi 12.3的.dpk文件先打開(kāi)運(yùn)行期包不帶dcl前綴的那個(gè)。在Project Manager里右鍵選擇Build等待編譯完成確認(rèn)沒(méi)有報(bào)錯(cuò)。再打開(kāi)設(shè)計(jì)期包dcl前綴的那個(gè)右鍵選擇Install?;氐絀DE主界面在Component菜單里刷新確認(rèn)ReportMachine出現(xiàn)在了組件面板的指定頁(yè)簽。裝完之后還有一個(gè)動(dòng)作別漏在Tools Options里把運(yùn)行期包加進(jìn)Runtime Packages列表。否則你的項(xiàng)目可能會(huì)選擇靜態(tài)鏈接也可以正常工作但如果之后想用運(yùn)行時(shí)包的動(dòng)態(tài)更新能力就會(huì)發(fā)現(xiàn)少了這一步。更糟的情況是項(xiàng)目里多處引用了同一個(gè).bpl漏配后部署時(shí)忘了帶包文件目標(biāo)機(jī)器上一運(yùn)行就報(bào)“程序無(wú)法啟動(dòng)缺少xxx.bpl”這種問(wèn)題在客戶現(xiàn)場(chǎng)追起來(lái)非常狼狽。2.3 安裝失敗時(shí)先查這三類(lèi)報(bào)錯(cuò)我見(jiàn)過(guò)最多的三類(lèi)安裝報(bào)錯(cuò)“Cannot find unit xxx.dcu”基本是Library Path沒(méi)配好或者打開(kāi)的是錯(cuò)誤版本的.dpk工程。先檢查路徑再確認(rèn)打開(kāi)的文件在對(duì)應(yīng)版本目錄里。“Package xxx is already installed”舊版本還在IDE的包列表里新版本裝不進(jìn)去。到Components Install Packages里把舊的Remove掉再重試。“Access denied”或“無(wú)法寫(xiě)入”IDE權(quán)限不夠或者殺毒軟件在后臺(tái)鎖定了文件。用管理員身份重開(kāi)IDE或者暫時(shí)關(guān)閉實(shí)時(shí)防護(hù)。遇到這些報(bào)錯(cuò)我一般不會(huì)死磕單條信息而是按順序做三件事卸載所有舊包、清理IDE緩存%APPDATA%\Embarcadero\BDS\23.0目錄下的.package相關(guān)緩存文件、然后以管理員身份重新打開(kāi)IDE再裝一遍。這套“三板斧”下來(lái)絕大多數(shù)安裝問(wèn)題都能解決。如果還不行再考慮是不是下載的包本身缺文件——去Source目錄里看看有沒(méi)有關(guān)鍵單元缺失好過(guò)在IDE里反復(fù)試。3. 報(bào)表開(kāi)發(fā)上手從數(shù)據(jù)源到一張能交付的報(bào)表3.1 數(shù)據(jù)源連接與Band結(jié)構(gòu)先理解“循環(huán)打印”安裝只是熱身真正干活從把TfrxReport控件拖到窗體上開(kāi)始。ReportMachine的設(shè)計(jì)思路和FastReport很接近報(bào)表模板獨(dú)立于窗體運(yùn)行時(shí)加載數(shù)據(jù)源在外部接好再喂給它。這樣做的好處是模板可以丟給業(yè)務(wù)人員改樣式程序員不用每次都重新編譯。最常用的連接方式是這樣的先用ADOQuery寫(xiě)好SQL設(shè)定好ConnectionString然后把報(bào)表里的TfrxDBDataSet的DataSet屬性指到ADOQuery上。TfrxReport本身并不直接連數(shù)據(jù)庫(kù)它是通過(guò)TfrxDBDataSet這個(gè)“橋”去拿數(shù)據(jù)的。很多新手在這里栽跟頭——在TfrxReport上找了半天沒(méi)有DataSource屬性其實(shí)就是沒(méi)理解這層間接關(guān)系。如果你用的是ODAC、FireDAC或者其他數(shù)據(jù)庫(kù)組件套路完全一樣DataSet的類(lèi)型換一下而已。Band是報(bào)表排版的核心概念我習(xí)慣把它理解成“打印帶”。通俗地說(shuō)ReportMachine的頁(yè)面是由一條條Band從上到下組成的報(bào)頭ReportTitle只在第一頁(yè)打印一次頁(yè)頭PageHeader每頁(yè)頂部都會(huì)打印數(shù)據(jù)區(qū)MasterData是循環(huán)的——數(shù)據(jù)源有多少條記錄它就重復(fù)多少行頁(yè)腳PageFooter每頁(yè)底部打印匯總區(qū)ReportSummary在報(bào)表最后打一次。想實(shí)現(xiàn)“每頁(yè)固定行數(shù)”“分組小計(jì)”這些需求本質(zhì)上都是在調(diào)整Band的排列和事件。我見(jiàn)過(guò)不少新手一上來(lái)就按坐標(biāo)擺Label結(jié)果一行報(bào)表數(shù)據(jù)跑飛問(wèn)題就出在沒(méi)理解Band的循環(huán)機(jī)制。3.2 腳本與計(jì)算把報(bào)表邏輯留在模板里報(bào)表不只是靜態(tài)畫(huà)面。合計(jì)、平均值、按客戶分組的訂單金額這些如果在SQL里寫(xiě)死后期改報(bào)表邏輯就得動(dòng)SQL、改代碼、重新編譯風(fēng)險(xiǎn)大周期長(zhǎng)。ReportMachine內(nèi)置了Pascal腳本引擎常見(jiàn)的做法是直接在報(bào)表模板里寫(xiě)腳本數(shù)據(jù)區(qū)的OnAfterPrint事件里累加金額匯總區(qū)的OnBeforePrint事件里把合計(jì)值賦給某個(gè)Memo組件。用腳本的關(guān)鍵是理解事件時(shí)機(jī)OnBeforePrint在Band打印前觸發(fā)適合準(zhǔn)備數(shù)據(jù)、計(jì)算字段OnAfterPrint在Band打印后觸發(fā)適合累加計(jì)數(shù)器。我經(jīng)??吹接腥税牙塾?jì)邏輯寫(xiě)在OnBeforePrint里結(jié)果數(shù)值永遠(yuǎn)慢一行其實(shí)換個(gè)事件就好。計(jì)算字段也可以直接在腳本里動(dòng)態(tài)賦值比如“折扣后金額 原價(jià) * 折扣率”比在SQL里反復(fù)寫(xiě)CASE WHEN要直觀得多。這里有個(gè)小經(jīng)驗(yàn)?zāi)_本里盡量別寫(xiě)太復(fù)雜的業(yè)務(wù)邏輯報(bào)表腳本的本質(zhì)是展示邏輯業(yè)務(wù)校驗(yàn)留在后端否則報(bào)表模板被改壞了排查起來(lái)特別費(fèi)勁。3.3 打印和導(dǎo)出的邊界問(wèn)題紙張、字體、合并報(bào)表最終交付的形態(tài)無(wú)非是打印和導(dǎo)出但這兩步的坑一個(gè)比一個(gè)多。打印時(shí)最常見(jiàn)的問(wèn)題是紙張大小和頁(yè)邊距ReportMachine自身維護(hù)了一套頁(yè)面設(shè)置和Windows打印機(jī)驅(qū)動(dòng)里的默認(rèn)紙張經(jīng)常不一致導(dǎo)致預(yù)覽正常、打印錯(cuò)位。我的建議是在報(bào)表模板的Page屬性里顯式指定紙張而不要依賴打印機(jī)的默認(rèn)值。否則同一個(gè)報(bào)表在辦公室的A4打印機(jī)上正常到了客戶現(xiàn)場(chǎng)的針式打印機(jī)上就走樣。導(dǎo)出PDF時(shí)中文字體是個(gè)老大難問(wèn)題。如果導(dǎo)出后中文變成方塊或者亂碼大概率是報(bào)表里用的字體在PDF引擎里沒(méi)有正確嵌入。處理方式是把報(bào)表里所有中文相關(guān)組件的字體統(tǒng)一設(shè)置為中文字體比如宋體或微軟雅黑然后在導(dǎo)出設(shè)置里開(kāi)啟字體嵌入。導(dǎo)出Excel時(shí)同樣有坑ReportMachine默認(rèn)按單元格逐個(gè)導(dǎo)出如果報(bào)表里的Memo跨列合并了導(dǎo)出的Excel格式可能不理想需要在導(dǎo)出設(shè)置里調(diào)整合并選項(xiàng)。有一個(gè)取巧的辦法是用報(bào)表的HTML導(dǎo)出做中間格式再用Excel打開(kāi)某些復(fù)雜版式下效果反而更好雖然多了一步但勝在穩(wěn)定。4. 從QuickReport/Rave遷移到ReportMachine的實(shí)操?gòu)?fù)盤(pán)4.1 遷移之前先做報(bào)表清單而不是急著打開(kāi)IDE我接手過(guò)好幾個(gè)需要從老報(bào)表控件遷移到ReportMachine的項(xiàng)目第一反應(yīng)千萬(wàn)別是“打開(kāi)Delphi開(kāi)始拖控件”。正確的做法是先把報(bào)表清單梳理清楚全項(xiàng)目全局搜索舊控件的單元引用把每張報(bào)表用到的數(shù)據(jù)源、SQL、打印場(chǎng)景、特殊邏輯列成一份表格。這個(gè)過(guò)程看起來(lái)很笨但能讓你在動(dòng)手前就發(fā)現(xiàn)那些“寫(xiě)死了的”報(bào)表——比如某張報(bào)表不是從數(shù)據(jù)庫(kù)取數(shù)而是動(dòng)態(tài)生成了一堆文本塊這種報(bào)表遷移起來(lái)特別麻煩需要單獨(dú)處理。清單梳理還有另一個(gè)作用確認(rèn)遷移范圍。很多時(shí)候業(yè)務(wù)方說(shuō)的“所有報(bào)表都要遷移”里有一半其實(shí)已經(jīng)停用了。我和業(yè)務(wù)方逐張確認(rèn)時(shí)往往能砍掉三分之一的工作量。這比悶頭敲代碼高效得多。按清單推進(jìn)還有個(gè)好處就是每遷移一張報(bào)表就能在清單上打個(gè)勾進(jìn)度感很強(qiáng)跟客戶匯報(bào)時(shí)也拿得出數(shù)據(jù)。4.2 屬性映射表替換組件時(shí)最容易被忽略的坑從QuickReport遷移過(guò)來(lái)時(shí)最大的坑是屬性語(yǔ)義不對(duì)應(yīng)。QuickReport的很多屬性在ReportMachine里名字變了但功能類(lèi)似有些屬性名一樣語(yǔ)義卻完全不同。比如QuickReport的頁(yè)面邊距設(shè)置和ReportMachine的對(duì)應(yīng)屬性計(jì)算方式可能一個(gè)是毫米、一個(gè)是像素字體屬性的默認(rèn)值不同會(huì)導(dǎo)致打印出來(lái)的版面和原來(lái)差之千里。這種差異不搞清楚排查起來(lái)會(huì)非常痛苦。我自己的習(xí)慣是先選一張簡(jiǎn)單的報(bào)表做全流程遷移試點(diǎn)跑通之后建立一張“屬性映射表”舊控件屬性A對(duì)應(yīng)新控件屬性B、舊控件某個(gè)事件里做了什么邏輯、新控件要寫(xiě)在哪里。后面的報(bào)表照著這張表批量遷移效率會(huì)高很多。這份映射表還可以沉淀成團(tuán)隊(duì)文檔下次再有類(lèi)似遷移項(xiàng)目直接復(fù)用。別小看這個(gè)準(zhǔn)備工作它能幫你躲掉至少一半的隱藏問(wèn)題。4.3 “客戶認(rèn)準(zhǔn)了舊樣式”的報(bào)表怎么安全遷移有一種報(bào)表遷移起來(lái)不是技術(shù)問(wèn)題而是業(yè)務(wù)問(wèn)題。比如客戶已經(jīng)認(rèn)定了某張報(bào)表的打印樣式簽字蓋章時(shí)都要對(duì)照舊版哪怕字體大了一個(gè)像素客戶都能看出來(lái)。這類(lèi)報(bào)表遷移后必須做到肉眼幾乎看不出差別。我的做法是把舊報(bào)表導(dǎo)出成PDF然后逐頁(yè)比對(duì)新舊打印效果。ReportMachine的預(yù)覽和導(dǎo)出都在客戶端完成只要模板里把字體、邊距、縮放比例控制好重現(xiàn)舊版式并不是遙不可及的事。這里最實(shí)用的一個(gè)技巧是把舊報(bào)表的PDF截圖放在屏幕上當(dāng)參照?qǐng)D一邊調(diào)試新模板一邊比對(duì)而不靠記憶去還原。肉眼比對(duì)雖然土卻是最可靠的方式。字體大小差一磅、行距差兩像素眼睛看久了會(huì)疲勞但截圖放大后對(duì)比就直觀多了。處理完一張就歸檔一張整個(gè)過(guò)程有點(diǎn)像做文物修復(fù)急不得但做完了很有成就感。5. 熱搜里高頻出現(xiàn)的控件疑難雜癥排查實(shí)錄5.1 每次打開(kāi)IDE控件就消失多半不是控件壞了之前看到有人問(wèn)“Delphi控件版本問(wèn)題導(dǎo)致每次進(jìn)入IDE都丟失控件重新放置保存后還是那樣”這問(wèn)題一看就是鍋不在控件本身而在包管理器的狀態(tài)。最常見(jiàn)的原因是同一個(gè)控件被安裝了多個(gè)版本后安裝的版本覆蓋了先安裝的包但I(xiàn)DE的包緩存里還留著舊版的記錄每次啟動(dòng)IDE包加載失敗被靜默跳過(guò)組件面板就空空如也。排查方法很直接打開(kāi)Components Install Packages看有沒(méi)有帶感嘆號(hào)或者顯示為紅色的包條目再到項(xiàng)目屬性里檢查是否引用了舊的.dcu路徑。修正之后重啟IDE基本能解決。還有一個(gè)隱蔽原因容易被忽略Windows Defender或第三方殺毒軟件把某些.bpl文件誤判為風(fēng)險(xiǎn)文件直接隔離了——你在Packages列表里看包是“已安裝”狀態(tài)但實(shí)際文件已經(jīng)沒(méi)了。這種問(wèn)題怎么排查直接去.bpl所在的物理路徑檢查文件是否存在、體積是否為0一眼就能看出來(lái)。5.2 預(yù)覽閃退和導(dǎo)出亂碼按順序排查才高效預(yù)覽直接閃退我通常先懷疑三件事報(bào)表里引用了不存在的字體、腳本事件在打印時(shí)拋了異常、或者數(shù)據(jù)源在預(yù)覽時(shí)已經(jīng)被關(guān)閉。前兩種在ReportMachine的日志里會(huì)有記錄開(kāi)啟調(diào)試模式后能捕獲異常詳情第三種是新手最容易犯的——Form的OnClose里關(guān)了ADOConnection然后在報(bào)表預(yù)覽事件里又去取數(shù)據(jù)不報(bào)錯(cuò)才怪。導(dǎo)出亂碼的問(wèn)題前面提過(guò)字體嵌入這里再補(bǔ)充一個(gè)排查順序先確認(rèn)系統(tǒng)里有沒(méi)有這個(gè)字體再確認(rèn)報(bào)表模板里每個(gè)組件是否統(tǒng)一使用了同一種字體最后看導(dǎo)出設(shè)置。三步走完大部分亂碼都能解決。如果亂碼只出現(xiàn)在PDF里、打印卻正?;揪褪亲煮w嵌入沒(méi)勾上如果打印也亂碼那就要檢查客戶端系統(tǒng)字體了。別一上來(lái)就懷疑控件有問(wèn)題先把邊界畫(huà)清楚效率會(huì)高很多。5.3 和ADO/Excel/ODAC數(shù)據(jù)源打交道時(shí)的經(jīng)驗(yàn)網(wǎng)上搜Delphi問(wèn)題十個(gè)里有八個(gè)繞不開(kāi)“Delphi ADO連接Excel”“字符串處理”“MD5計(jì)算”這些基礎(chǔ)操作報(bào)表出問(wèn)題也往往是在這類(lèi)基礎(chǔ)操作上疊加出來(lái)的。比如用ADO把Excel當(dāng)數(shù)據(jù)源做報(bào)表最常遇到的問(wèn)題是Excel文件被Excel程序占用時(shí)連接會(huì)直接失敗另外Excel的列類(lèi)型推斷不靠譜同一列前面幾行是數(shù)字、后面幾行是文本ADO讀出來(lái)可能就變成Null。我的應(yīng)對(duì)方案是把Excel數(shù)據(jù)先導(dǎo)入數(shù)據(jù)庫(kù)臨時(shí)表報(bào)表再讀數(shù)據(jù)庫(kù)。雖然多一步導(dǎo)入動(dòng)作但繞開(kāi)了Excel的坑報(bào)表性能也更好。如果是ODAC連Oracle連接字符串和字符集設(shè)置要注意報(bào)表里中文亂碼時(shí)優(yōu)先查NLS參數(shù)FireDAC則要注意驅(qū)動(dòng)版本和連接定義的一致性。做Delphi開(kāi)發(fā)接觸的控件不止報(bào)表這一種——打印控件、上傳控件、各種ActiveX組件都有自己的脾氣每個(gè)都要求你多一點(diǎn)耐心。但報(bào)表控件相對(duì)特殊因?yàn)樗苯用鎸?duì)最終用戶每次樣式不對(duì)都是客戶最先發(fā)現(xiàn)。把數(shù)據(jù)源這層邊界處理好報(bào)表控件自己出問(wèn)題的情況其實(shí)少之又少。最后再分享一個(gè)我自己的使用習(xí)慣報(bào)表模板盡量放到外部文件運(yùn)行時(shí)用TfrxReport的LoadFromFile動(dòng)態(tài)加載。這樣業(yè)務(wù)方改報(bào)表樣式你只需要發(fā)一個(gè)新的模板文件過(guò)去連程序都不用重新編譯。以前維護(hù)老項(xiàng)目時(shí)改一張報(bào)表就要出一個(gè)版本客戶等得煩你也累把模板抽離出來(lái)之后報(bào)表維護(hù)的工作量直線下降。Delphi單體老項(xiàng)目本來(lái)就改不動(dòng)太多這種小改動(dòng)性價(jià)比很高建議你也在下一個(gè)項(xiàng)目里試試。本文還有配套的精品資源點(diǎn)擊獲取