Teknisk artikel

HotXLS flettede celler og layoutstyrede rapportskabeloner i Delphi

Gennemløb cellerne i en nyåbnet rapportskabelon, og en sammenlagt titel opfører sig som en lækage. Du læser A1 og får "Quarterly Statement"; du læser B1 til F1, som synligt sidder under samme banner, og du får ingenting. Skriv en værdi ind i C1 for at lappe overskriften, og den dukker aldrig op på skærmen. Gitteret mistede ikke dine data. Det gør præcis det, en sammenlægning betyder: i både XLS og XLSX gengiver et sammenlagt rektangel indholdet af én celle, øverste-venstre-ankeret, og behandler resten som dækket plads, der holder værdier, men aldrig viser dem. Excel-brugere absorberer dette gennem prøve-og-fejl. En rapportgenerator må kode det som en regel, fordi symptomet i genereret kode er en tom region med ingen undtagelse at spore den til. HotXLS, et nativt Object Pascal-bibliotek, der læser og skriver begge Excel-formater fra Delphi og C++Builder, eksponerer sammenlægningstabellen tydeligt nok til, at man kan programmere mod reglen frem for at genopdage den i en supportsag

Én værdi, ét anker

En sammenlægning er en visningsinstruktion lagt over et gitter, der ikke ændrer form. Hver dækket celle findes stadig i filen som sin egen plads; sammenlægningsposten fortæller blot en forbruger at male ankerets indhold ud over rektanglet. Den skelnen driver tre adfærdsmønstre, det er værd at internalisere, før du skriver noget layoutkode. At læse en dækket celle returnerer dens egen gemte værdi, hvilket for et banner, du har bygget, som regel er tomt, så al kode, der inspicerer en sammenlagt titel, må opløse og læse ankeret. At skrive til en dækket celle lykkes på filniveau og dukker ingen steder op, hvilket er den usynlige-overskrift-fælde fra åbningen. Og at ophæve en region afslører, hvad end der sad under den hele tiden, så en tilfældig værdi skrevet ind i dækket plads bliver til en synlig defekt den dag, nogen opløser sammenlægningen

Diagram over et HotXLS flettet banner, hvor dækkede celler beholder deres egne slots, mens læsninger opløses til anker A1 i Delphi regneark
HotXLS beholder hver dækket celle som en rigtig plads og genskriver kun anchor'en, så læsninger opløses gennem A1, mens skrivninger ind i dækket rum forbliver usynlige til en unmerge

På XLSX-siden er den tabel et førsteklasses objekt. Sheet.MergedCells bærer Add('A1:C1'), FindAt(Row, Col), DeleteAt og Items, og det kald, man griber til oftest, er FindAt: giv den en vilkårlig koordinat, og den returnerer den sammenlagte region, der dækker den celle, eller nil, når cellen står alene. Det ene opslag er fundamentet for begge halvdele af korrekt sammenlægningshåndtering, den sikre læsning og skrivevagten, og begge dukker op senere

To facader, to sammenlægningsidiomer

HotXLS holder den klassiske BIFF8-.xls-motor og OOXML-.xlsx-motoren som separate objektmodeller, og de staver sammenlægning forskelligt, fordi de nedstammer fra forskellige konventioner. XLS-facaden følger Excel COM-idiomet: du tager et område fra en to-argument-indekseret egenskab og kalder Merge med en OleVariant, hvis værdi afgør den geometri, du ender med

var
  Book: IXLSWorkbook;   // grænseflade-talt: ingen manuel Free
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // XLS-arksamlingen er 1-baseret
  Sh.Range['A1', 'F1'].Merge(False);    // False = én sammenlagt blok
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = merge across: én sammenlægning pr. række
  Book.SaveAs('layout.xls');
end;

Argumentet til Merge er den del, folk får galt. Over et to-rækkers område producerer Merge(True) to uafhængige én-rækkes sammenlægninger, hvilket er Excels "Merge Across" og præcis, hvad man ønsker til et stablet overskriftsbånd, der skal holde sine rækker adskillelige. Merge(False) smelter hele rektanglet sammen til én blok. Området rapporterer også MergeCells som et tilstandsflag, returnerer den indeholdende region via MergeArea og opløser sig selv med Unmerge. XLSX-facaden eksponerer de samme operationer under andre navne: Sheet.MergeCells(Row1, Col1, Row2, Col2) tager heltalsgrænser, TXLSXRange.Merge accepterer den tilsvarende Across-variant, og MergedCells-samlingen holder resultatet

En skabelon, der vokser med sine data

En rigtig rapportskabelon er ikke et fast gitter. Overskriften og totalerne er faste, men detaljesektionen mellem dem strækker sig til, hvad end forespørgslen returnerer. Det mønster, der holder, beholder én fuldt stylet detaljerække i skabelonen, kloner den én gang pr. post og åbner derefter et hul foran totalblokken, så alt, der er forankret nedenunder, glider ned uden at miste sin formatering

HotXLS rapportskabelon voksende i Delphi: den stylede detaljerække kloning pr. record, og InsertRows åbner et hul, så totalsblokken glider ned med fletninger intakte
At klone den stylede detaljerække bærer dens stilarter og formler med i hver kopi, og InsertRows skubber derefter totalbåndet ned med fletninger og formater intakte
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // værdien går til ankeret, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1;        // puljeindeks 0-baseret, celleside 1-baseret

// række 5 er den stylede detalje-skabelonlinje
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // stilarter og formler rejser med den

// åbn et hul over totalblokken; indhold nedenunder rykker ned
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

To linjer fortjener et ekstra kig. Skrifttildelingen bærer en off-by-one, der bider lydløst: Fonts.Add giver en 0-baseret puljeposition tilbage, mens en celle gemmer en 1-baseret skriftreference, hvor 0 betyder standardskriften, så det at udelade + 1 udløser intet, det styler bare din titel i den forkerte skrifttype. Den anden linje er CopyRange, som flytter formatering og formler sammen med værdier. Det er hele grunden til at klone en håndbygget skabelonrække frem for at genskabe dens udseende i kode. En designer ejer udseendet én gang, i skabelonen; generatoren hælder kun nogensinde data ind i kopier af den

Den opdeling skalerer yderligere, når det genanvendelige layout bor i sin egen projektmappe, sig et ark med overskrifts- og fodbånd delt på tværs af rapporter. CopyRangeTo udfører samme klon på tværs af regnearksgrænser, idet den tager et målark plus destinationskoordinater, så en generator kan holde ét pletfrit skabelonark og stemple dets regioner ind i lige så mange outputark, som et job har brug for. Alternativet, at mutere skabelonen på stedet og forsøge at genoprette den bagefter, er den slags, der virker, indtil den dag en kørsel afbrydes halvvejs

Hvad InsertRows flytter, og hvad den ikke gør

Voks-en-skabelon-mønstret virker kun, fordi XLSX InsertRows er en strukturel redigering frem for en celleomrokering. Når den åbner et hul, flytter den de sammenlagte regioner, rækkehøjder, hyperlinks, kommentarer, fastfrosne ruder, autofilter-områder, betinget formatering, datavalideringer, tabeller, definerede navne, billedankre og diagramankre, der sidder under indsættelsespunktet, ikke kun celleværdierne. Det er det, der lader totalblokken ankomme til sin nye række med sine sammenlægninger og taltformater intakte i stedet for at ankomme afklædt

Dens to dokumenterede grænser er dem, man skal designe omkring. Formeljustering er scopet til det ark, der redigeres: referencer inde i det ark bliver omskrevet, og en formel på et andet ark, der peger ind i det forskudte område, bliver også omskrevet, men justeringen følger kun referencer, der målretter det redigerede ark, så ethvert krydsprojektmappe-referenceskema fortjener sin egen revision frem for blind tillid. Den anden grænse er skarpere, og den er på XLS-siden. Pivottabeller overlever åbn-gem-cyklusser som rå bevarede poster, ikke som modellerede objekter, HotXLS kan flytte, så det at indsætte rækker ikke flytter en pivots fodaftryk. Enhver skabelon, du bygger til .xls-formatet, bør parkere sine pivotregioner godt væk fra ethvert bånd, der vokser

At nægte at skrive data ind i layoutplads

Den sammenlægningsfejl, der reelt når produktion, er ikke den kosmetiske. Den er strukturel: en detaljerække driver ind i et sammenlagt layoutbånd, dens værdier lander i dækkede celler og bliver usynlige, og kolonnetotalerne holder i det stille op med at matche, hvad enhver, der læser arket, kan se. Fordi FindAt besvarer dækningsregion-spørgsmålet for enhver koordinat, kan generatoren nægte den skrivning i det øjeblik, den ville ske, frem for at levere en rapport, der i det stille undertæller

// nægt at skrive detaljedata ind i en sammenlagt layoutregion
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
  raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;

Den samme grænsekontrol hører hjemme overalt, hvor en bruger senere vil sortere eller filtrere outputtet. Et område med sammenlægninger indeni kan ikke sortere rent, fordi sorteringen flytter rækker uafhængigt, og en sammenlægning, der spænder over rækker, har ingen enkelt række at rejse med; Excel svarer med en fejl eller et forvrænget layout. Den disciplin, der holder rapporter korrekte, er geografisk. Begræns sammenlægninger til titelbånd, sektionsopdelere og underskriftsblokke, og hold arkets tabulære midte flad. Artiklen om skabelonbaseret rapportgenerering udvikler denne layout-versus-data-opdeling til et fuldt placeholder-drevet workflow, og artiklen om betinget formatering og rich text dækker styling af det flade databånd

Hvordan sammenlægninger degraderer på vej ud

En sammenlægning er et projektmappebegreb, og hvert tekstorienteret eksportformat respekterer det i forskellig grad. At kende de tre adfærdsmønstre på forhånd sparer en QA-cyklus. HTML-eksport gengiver sammenlægninger loyalt, ved at udsende colspan og rowspan på én tabel, så en browserbaseret rapport beholder sit bånderede udseende. RTF-eksport spænder slet ikke over kolonner: ankerteksten lander i sin egen celle, og resten af sammenlægningens bredde kommer ud som tomme celler, hvilket efterlader en bred titel visuelt skubbet til venstre i en tekstbehandler. CSV har intet begreb om en sammenlægning, så ankerværdien optager ét felt, og hver dækket celle udsendes som et tomt felt. Konklusionen for en projektmappe, der også fodrer afgrænsede eksporter, er at holde alt bærende ude af sammenlagt geometri; artiklen om CSV-, TSV- og HTML-eksport gennemgår hvert format i detaljer

HotXLS flettet titel eksporteret fra Delphi til HTML med colspan og rowspan, RTF uden spans og CSV som fladtede felter
Samme flettede titel overlever HTML-eksport gennem colspan og rowspan, degraderer i RTF til en ensom venstrelåst celle og flader ud til en værdi plus tomme felter i CSV

Én forsikring til alle, der vejer dette op mod filstørrelse: sammenlægninger koster næsten intet på rapportskala. Sammenlægningstabellen er lillebitte ved siden af celledataene, og at læse en dækket celle går stadig gennem FindAt frem for scanning. Ydeevnepresset på store projektmapper kommer fra andre steder, primært stilpulje-vækst og den hukommelse, gemmestien holder på, hvilket artiklen om ydeevne for store projektmapper tager direkte fat på. Begge sammenlægnings-API'er, de strukturelle redigeringsoperationer og skabelondemonstrationerne følger med HotXLS-Delphi-komponenten