Pouzdan način za izradu oblikovanog Excel izvješća iz Delphija je započeti s radnom knjigom koju je dizajner već izradio. Netko u financijama postavlja izgled računa u Excelu: logotip, zaglavlja stupaca, obrube na traci s pojedinostima, podebljani redak s ukupnim vrijednostima, formate valuta. Vaš kod otvara tu datoteku, ispušta aktivne podatke u ćelije koje je dizajner rezervirao za njih i sprema rezultat. Izgled je njihov; brojevi su vaši. HotXLS, nativna Delphi i C++Builder biblioteka koja čita i piše XLS i XLSX radne knjige bez pokretanja Excela, pruža vam tri operacije koje ovaj pristup zahtijeva: pretraživanje ćelije prema njezinom tekstu, kopiranje raspona s netaknutim stilovima i formulama, te umetanje redaka tako da se sve ispod pomiče prema dolje s podacima.
Jedino pravilo koje razlikuje generator koji preživljava izmjene predloška od onoga koji puca na prvoj izmjeni jest da nikada ne adresira ćelije prema doslovnim brojevima redaka i stupaca. Predložak je dokument koji drugi ljudi uređuju. Financijski tim dodaje liniju poreza, podiže visinu retka s logotipom, preuređuje blok adrese, a format datoteke vam uopće ne pomaže: spremanje u BIFF ili OOXML uspijeva bez obzira na to znači li redak 10 i dalje ono što je značio prošlog tromjesečja. Ovdje je generator koji piše prvu liniju pojedinosti u tvrdo kodirani redak 10, pa kada netko prvi put umetne blok iznad odjeljka s pojedinostima, ispiše stavke preko krivih ćelija i zbroji raspon ukupnih vrijednosti koji više ne pokriva podatke. Ništa ne javlja pogrešku, svako spremanje vraća uspjeh, a jedini signal je da klijent primijeti pogrešan račun.
Usidrite svaku koordinatu za rezervirani token
Rješenje je učiniti da predložak nosi vlastite koordinate. Dizajner upisuje tokene kao što su {{CUSTOMER}}, {{DATE}} i {{DETAIL_START}} u ćelije koje generator mora dotaknuti, a generator u vrijeme izvođenja izračunava svaki položaj na temelju mjesta gdje pronađe te tokene. Izmjene izgleda više nisu važne jer se token pomiče s ćelijom u kojoj se nalazi. Druga polovica ugovora je pravilo o neuspjehu: ako nedostaje potreban token, posao se zaustavlja prije nego što bilo kakvi klijentski podaci stignu u datoteku. Predložak koji je odstupio trebao bi proizvesti karticu neuspjelog posla, a ne isporučeni dokument.
Pronalaženje tokena: FindText i ReplaceText
Obobje obitelji klasa u HotXLS-u izlažu pretraživanje na razini radnog lista. FindText vraća redak i stupac prve ćelije čiji tekst odgovara upitu, uz preopterećenje koje dodaje osjetljivost na velika i mala slova. ReplaceText zamjenjuje svako pojavljivanje i vraća broj izmijenjenih unosa. Te dvije metode pokrivaju dvije vrste tokena koje obično imate. Jedno sidro poput imena kupca locirate jednom i pišete pored njega; token koji se treba pojaviti točno jednom, poput datuma izvješća, zamjenjujete i provjeravate broj zamjena. Na strani XLSX-a, popunjavanje koje se usidruje na ovaj način izgleda ovako:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R, C: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('invoice-template.xlsx') <> 1 then
raise Exception.Create('Cannot open invoice template');
Sheet := Book.Sheets[0]; // TXLSXSheets.Items is 0-based
if not Sheet.FindText('{{CUSTOMER}}', R, C) then
raise Exception.Create('Template drift: {{CUSTOMER}} anchor missing');
Sheet.Cells[R, C].Value := 'ACME Corp';
if Sheet.ReplaceText('{{DATE}}',
FormatDateTime('yyyy-mm-dd', Date)) = 0 then
raise Exception.Create('Template drift: {{DATE}} token missing');
// detail expansion and save follow below
finally
Book.Free;
end;
end;
Pojedinosti koje su važne. Prvo, FindText i ReplaceText traže tekstualnu vrijednost ćelije; token ugrađen unutar niza formule njima je nevidljiv, stoga rezervirani tokeni pripadaju običnim ćelijama, nikada unutar formula. Drugo, broj zamjena je vaš detektor odstupanja predloška. Predložak koji bi trebao sadržavati točno jedan token {{DATE}}, ali javlja nula zamjena, bio je uređivan, a podizanje iznimke u tom trenutku je upravo ono što pretvara tihi odmak izgleda u vidljivi neuspjeh.
Kloniranje retka pojedinosti bez gubitka stilova ili formula
Odjeljak pojedinosti računa raste s podacima. Pisanje vrijednosti izravno u prazne retke ispod ogledne linije odbacuje sve što je dizajner pripremio: obrube, formate brojeva, formule po retku. Uzorak koji čuva sve to jest ostaviti jedan potpuno oblikovan ogledni redak u predlošku i klonirati ga za svaku stavku. CopyRange duplicira stilove i formule in a single call, nakon čega generator prepisuje samo ćelije s vrijednostima.
const
DetailRow = 10; // the formatted sample row in the template
var
I: Integer;
begin
// Open space before the totals block first, so the SUM range
// below the detail band stretches together with the data.
if Length(Items) > 1 then
Sheet.InsertRows(DetailRow + 1, Length(Items) - 1);
for I := 0 to High(Items) do
begin
if I > 0 then // clone styles + formulas from the sample row
Sheet.CopyRange(DetailRow, 1, DetailRow, 5, DetailRow + I, 1);
Sheet.Cells[DetailRow + I, 1].Value := Items[I].Name;
Sheet.Cells[DetailRow + I, 2].Value := Items[I].Qty;
Sheet.Cells[DetailRow + I, 3].Value := Items[I].UnitPrice;
Sheet.Cells[DetailRow + I, 4].Formula :=
Format('B%d*C%d', [DetailRow + I, DetailRow + I]); // no '=' prefix
end;
end;
Pažljivo pratite dodjelu formule. XLSX svojstvo Formula prima izraz bez vodećeg znaka jednakosti, dok XLS sučelje očekuje '=B10*C10' dodijeljeno putem Value. Miješanje ovih dviju konvencija najčešća je pogreška pri portiranju između obitelji klasa, i prolazi bez pritužbi: ćelija jednostavno drži doslovni niz koji Excel prikazuje kao tekst. Ako predložak ukrašava traku pojedinosti sa spojenim redcima naslova, zapamtite da samo gornja lijeva ćelija spojenog područja nosi vrijednost. Pravila rasporeda u pratećem članku o spojenim ćelijama u predlošcima izvješća vođenim izgledom objašnjavaju zašto regije spajanja pripadaju izvan podatkovne trake u potpunosti.
Što InsertRows pomiče, a što ostavlja iza sebe
Umetanje redaka ispred bloka s ukupnim vrijednostima omogućuje rastezanje raspona SUM kako raste odjeljak s pojedinostima. Na XLSX strani, InsertRows prenosi dugačak popis ovisnih struktura dolje s ćelijama: spojene raspone, visine redaka, hiperveze, komentare, zamrznuta okna, raspone automatskog filtriranja, uvjetna oblikovanja, provjere valjanosti podataka, tablice, definirane nazive te sidra slika i grafikona. Postoji jedna granica na tom popisu koju vrijedi zapamtiti. Prepisivanje formula doseže samo reference unutar istog lista. Formula na sažetom listu koja pokazuje na premješteno područje kupuje svoje stare koordinate i tiho čita pogrešne ćelije, zbog čega se ukupni iznosi izvučeni preko listova sigurnije izražavaju putem naziva na razini radne knjige. Prateći članak o definiranim nazivima i formulama između listova detaljno opisuje taj uzorak.
Naslijeđeni XLS format postavlja granicu na strože mjesto. HotXLS drži pivot tablice, tablice upita i vanjske podatkovne veze u BIFF datotekama kao sirove blokove bajtova. Oni preživljavaju otvaranje i spremanje nepromijenjeni, ali nisu modelirani, pa ih umetanje redaka nikada ne dotiče. Predložak koji smješta pivot tablicu ispod odjeljka s pojedinostima koji se širi sprema se bez ikakvog upozorenja dok pravokutnik izvora pivot tablice odlazi od podataka. Rješenje je strukturno, a ne obrambeno: držite sadržaj pivot tablica i upita na listovima u koje generator nikada ne umeće retke i zastarjelost se ne može dogoditi.
Ponovno izračunajte prije isporuke, ili znajte zašto ste to preskočili
HotXLS ne izračunava formule tijekom SaveAs. Kada osoba otvori datoteku, Excel ponovno izračunava sve (XLS sučelje izlaže CalculationMode i RecalcOnSave ako trebate time upravljati), tako da izvješće namijenjeno ljudskom sandučiću ne zahtijeva ništa više od vas. Slika se mijenja onog trenutka kada radna knjiga hrani drugi program. Izvoz u CSV zapisuje formule kao doslovan tekst i nikada ih ne izračunava, a bilo koji nizvodni parser koji vjeruje predmemoriranim vrijednostima čitat će zastarjele brojeve ili praznine. Za te putanje izračunajte na poslužitelju pomoću Calculate, koji procjenjuje proizvoljni izraz u odnosu na učitanu radnu knjigu i vraća rezultat:
var
Total: Variant;
LastDetail: Integer;
begin
LastDetail := DetailRow + Length(Items) - 1;
Total := Book.Calculate(Format('SUM(Invoice!D%d:D%d)',
[DetailRow, LastDetail]));
if (not VarIsNumeric(Total)) or
(Abs(Total - ExpectedTotal) > 0.005) then
raise Exception.Create('Invoice total does not match the order record');
if Book.SaveAs('invoice-2026-0611.xlsx') <> 1 then
raise Exception.Create('Save failed: check output path and permissions');
end;
Provjera izračunatog ukupnog iznosa u odnosu na zapis narudžbe prije spremanja je jeftino osiguranje s dobrim rezultatom. Pretvara pogrešan račun u neuspjeli posao. Operater može ponoviti neuspjeli posao u nekoliko sekundi; pogrešan račun koji je već u klijentovu sandučiću košta voditelja ključnih kupaca isprike i ispravka.
Dvije obitelji klasa, jedan algoritam
Ista se logika prenosi između formata, ali ne i isti kod. TXLSWorkbook za naslijeđeni .xls temelji se na sučeljima i brojanju referenci, s indeksiranjem listova od 1, i nikada ga ne oslobađate ručno. TXLSXWorkbook za .xlsx je običan objekt koji morate osloboditi u try..finally bloku, s indeksiranjem listova od 0 i konvencijom formula prikazanom gore. FindText, ReplaceText, CopyRange i InsertRows žive na obje strane, pa se oblik sidrenja, kloniranja i ponovnog izračuna čisto prenosi. Praktičan savjet je posvetiti se jednom formatu po cjevovodu ili sakriti dva životna ciklusa objekata iza vlastitog tankog adaptera, umjesto raspršivanja razlika kroz generator.
Veličina rijetko igra ulogu za vrstu izvješća koju ovaj uzorak proizvodi. Kloniranje oblikovanog retka nekoliko tisuća puta nije ništa za trenutni hardver. Putanja spremanja postaje usko grlo tek kada traka s pojedinostima dosegne šesteroznamenkasti broj redaka, a u toj točki postavljanje StreamingWrite šalje XML radnog lista izravno u izlazni paket umjesto da ga sprema u međuspremnik; članak o strujnom pisanju za grupne poslove poslužitelja pokriva kada se ta razmjena isplati. Grafikoni se ponašaju kao i ostatak izgleda: na XLSX strani i sidro grafikona i reference serija pomiču se kada se InsertRows izvodi iznad njih, tako da grafikon ispod retka s ukupnim vrijednostima ostaje vezan za ispravne podatke, dok na XLS strani grafikoni sjede na vlastitim listovima grafikona i, poput pivot tablica, nikada se ne pomiču. To je još jedan argument više za držanje prezentacijskih listova podalje od lista koji generator proširuje.
Ovaj pristup sidrenja, kloniranja i ponovnog izračuna omogućuje dizajneru da posjeduje izgled radne knjige, dok vaš kod posjeduje ono što ona govori, što je obično ono zbog čega se generirani Excel izlaz isplati održavati. Pozivi za pretraživanje, kopiranje i umetanje prikazani ovdje, zajedno s mehanizmom za formule koji se koristi za provjeru ukupnog iznosa prije isporuke, dolaze s HotXLS komponentom za Delphi i C++Builder.