Teknisk artikkel

HotXLS sammenslåtte celler og oppsettstyrte rapportmaler i Delphi

Iterer cellene i en nylig åpnet rapportmal, og en sammenslått tittel oppfører seg som en lekkasje. Du leser A1 og får «Quarterly Statement»; du leser B1 gjennom F1, som synlig sitter under samme banner, og du får ingenting. Skriv en verdi inn i C1 for å lappe overskriften, og den dukker aldri opp på skjermen. Rutenettet mistet ikke dataene dine. Det gjør akkurat det en sammenslåing betyr: i både XLS og XLSX gjengir et sammenslått rektangel innholdet til én celle, ankeret øverst til venstre, og behandler resten som dekket plass som holder verdier, men aldri viser dem. Excel-brukere tar dette til seg gjennom prøving og feiling. En rapportgenerator må kode det som en regel, fordi symptomet i generert kode er en tom region uten noe unntak å spore den til. HotXLS, et nativt Object Pascal-bibliotek som leser og skriver begge Excel-formatene fra Delphi og C++Builder, eksponerer sammenslåingstabellen eksplisitt nok til at du kan programmere mot regelen i stedet for å oppdage den på nytt i en supportsak

Én verdi, ett anker

En sammenslåing er en visningsinstruksjon lagt over et rutenett som ikke endrer form. Hver dekket celle finnes fortsatt i filen som sin egen plass; sammenslåingsposten forteller bare en konsument å male ankerets innhold over rektangelet. Det skillet driver tre atferder verdt å internalisere før du skriver noe oppsettskode. Å lese en dekket celle returnerer sin egen lagrede verdi, som for et banner du bygde vanligvis er tom, så all kode som inspiserer en sammenslått tittel, må løse opp og lese ankeret. Å skrive til en dekket celle lykkes på filnivå og dukker ikke opp noe sted, som er den usynlige-overskrift-fellen fra innledningen. Og å oppheve sammenslåingen av en region avslører hva enn som satt under den hele tiden, så en villfaren verdi skrevet inn i dekket plass blir til en synlig defekt den dagen noen løser opp sammenslåingen

Diagram over en HotXLS sammenslått banner der dekkede celler beholder sine egne plasser, mens lesinger løses til ankeret A1 i Delphi regneark
HotXLS beholder hver dekkede celle som en ekte plass og maler bareankeret på nytt, så lesinger løses gjennom A1 mens skrivinger inn i dekket område forblir usynlige til en fletting oppheves

På XLSX-siden er den tabellen et førsteklasses objekt. Sheet.MergedCells bærer Add('A1:C1'), FindAt(Row, Col), DeleteAt og Items, og kallet du griper til mest, er FindAt: gi den en hvilken som helst koordinat, og den returnerer den sammenslåtte regionen som dekker den cellen, eller nil når cellen står alene. Det enkeltoppslaget er grunnlaget for begge halvdelene av korrekt sammenslåingshåndtering, den trygge lesingen og skrivevakten, og begge dukker opp senere

To fasader, to sammenslåingsidiomer

HotXLS holder den klassiske BIFF8 .xls-motoren og OOXML .xlsx-motoren som separate objektmodeller, og de staver sammenslåing forskjellig fordi de nedstammer fra forskjellige konvensjoner. XLS-fasaden følger Excel COM-idiomet: du tar et område fra en to-argument-indeksert egenskap og kaller Merge med en OleVariant hvis verdi avgjør geometrien du ender opp med

var
  Book: IXLSWorkbook;   // grensesnitt-tellet: ingen manuell Free
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // XLS-arksamlingen er 1-basert
  Sh.Range['A1', 'F1'].Merge(False);    // False = én sammenslått blokk
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = merge across: én sammenslåing per rad
  Book.SaveAs('layout.xls');
end;

Argumentet til Merge er delen folk får feil. Over et toradsområde produserer Merge(True) to uavhengige enrads-sammenslåinger, som er Excels «Merge Across» og akkurat det du vil ha for et stablet overskriftsbånd som bør holde radene sine atskillbare. Merge(False) smelter hele rektangelet sammen til én enkelt blokk. Området rapporterer også MergeCells som et tilstandsflagg, returnerer den omsluttende regionen gjennom MergeArea, og løser seg selv opp med Unmerge. XLSX-fasaden eksponerer de samme operasjonene under andre navn: Sheet.MergeCells(Row1, Col1, Row2, Col2) tar heltallsgrenser, TXLSXRange.Merge godtar den tilsvarende Across-varianten, og MergedCells-samlingen holder resultatet

En mal som vokser med dataene sine

En ekte rapportmal er ikke et fast rutenett. Overskriften og totalene er faste, men detaljseksjonen mellom dem strekker seg til hva enn spørringen returnerer. Mønsteret som holder mål, holder én fullt stilert detaljrad i malen, kloner den én gang per post, og åpner deretter et gap foran totalblokken slik at alt forankret under glir nedover uten å miste formateringen sin

HotXLS rapportmal som vokser i Delphi: den stilede detaljraden klones per post, og InsertRows åpner et gap slik at totalsum-blokken glir ned med sammenslåingene intakte
Å klone den stylede detaljraden bærer sine stiler og formler inn i hver kopi, og InsertRows skyver deretter summebåndet ned med flettinger og formater intakt
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // verdien går til ankeret, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1;        // bassengindeks 0-basert, cellesiden 1-basert

// rad 5 er den stilerte detaljmal-linjen
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // stiler og formler blir med

// åpne et gap over totalblokken; innhold under forskyves nedover
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

To linjer fortjener et nytt blikk. Fonttildelingen bærer en av-med-én-feil som biter stille: Fonts.Add gir tilbake en 0-basert bassengposisjon, mens en celle lagrer en 1-basert fontreferanse der 0 betyr standardfonten, så å droppe + 1 utløser ingenting, det bare stiler tittelen din i feil skrifttype. Den andre linjen er CopyRange, som flytter formatering og formler sammen med verdier. Det er hele grunnen til å klone en håndbygget malrad i stedet for å rekonstruere utseendet dens i kode. En designer eier utseendet én gang, i malen; generatoren heller aldri annet enn data inn i kopier av den

Den delingen skalerer videre når det gjenbrukbare oppsettet bor i sin egen arbeidsbok, si et ark med overskrifts- og bunntekstbånd delt på tvers av rapporter. CopyRangeTo utfører den samme kloningen på tvers av arkgrenser, og tar et målark pluss destinasjonskoordinater, slik at en generator kan holde ett pletfritt malark og stemple regionene sine inn i så mange utdataark en jobb trenger. Alternativet, å mutere malen på stedet og prøve å gjenopprette den etterpå, er den typen ting som fungerer helt til den dagen en kjøring avbrytes halvveis

Hva InsertRows flytter, og hva den ikke gjør

Voks-en-mal-mønsteret fungerer bare fordi XLSX InsertRows er en strukturell redigering snarere enn en celleomrokkering. Når den åpner et gap, flytter den de sammenslåtte regionene, radhøydene, hyperlenkene, kommentarene, fryste ruter, autofilterområder, betinget formatering, datavalideringer, tabeller, definerte navn, bildeankere og diagramankere som sitter under innsettingspunktet, ikke bare celleverdiene. Det er det som lar totalblokken ankomme sin nye rad med sammenslåingene og tallformatene intakte i stedet for å ankomme strippet

De to dokumenterte grensene er de å designe rundt. Formeljustering er begrenset til arket som redigeres: referanser inne i det arket skrives om, og en formel på et annet ark som peker inn i det forskjøvede området, skrives også om, men justeringen følger bare referanser som retter seg mot det redigerte arket, så ethvert arbeidsbok-krysende referanseopplegg fortjener sin egen revisjon fremfor blind tillit. Den andre grensen er skarpere, og den er på XLS-siden. Pivottabeller overlever åpne-lagre-sykluser som rå bevarte poster, ikke som modellerte objekter HotXLS kan flytte, så å sette inn rader flytter ikke en pivots fotavtrykk. Enhver mal du bygger for .xls-formatet, bør parkere pivotregionene sine godt unna ethvert bånd som vokser

Å nekte å skrive data inn i oppsettsplass

Sammenslått-celle-feilen som faktisk når produksjon, er ikke den kosmetiske. Den er strukturell: en detaljrad driver inn i et sammenslått oppsettsbånd, verdiene dens lander i dekkede celler og blir usynlige, og kolonnetotalene slutter stille å stemme med det alle som leser arket, kan se. Fordi FindAt besvarer dekkende-region-spørsmålet for enhver koordinat, kan generatoren nekte den skrivingen i det øyeblikket den ville skjedd, i stedet for å levere en rapport som stille underteller

// nekt å skrive detaljdata inn i en sammenslått oppsettsregion
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 grensesjekken hører hjemme overalt en bruker senere skal sortere eller filtrere utdataene. Et område med sammenslåinger inni seg kan ikke sortere rent, fordi sorteringen flytter rader uavhengig, og en sammenslåing som spenner over rader, har ingen enkelt rad å reise med; Excel svarer med en feil eller et forvirret oppsett. Disiplinen som holder rapporter korrekte, er geografisk. Begrens sammenslåinger til tittelbånd, seksjonsskillere og signaturblokker, og hold den tabellariske midten av arket flat. Artikkelen om malbasert rapportgenerering utvikler dette oppsett-versus-data-skillet til en fullstendig plassholderdrevet arbeidsflyt, og artikkelen om betinget formatering og rich text dekker stiling av det flate databåndet

Hvordan sammenslåinger degraderes på vei ut

En sammenslåing er et arbeidsbokkonsept, og hvert tekstorientert eksportformat respekterer det i ulik grad. Å kjenne de tre atferdene på forhånd sparer en QA-runde. HTML-eksport reproduserer sammenslåinger trofast, og sender ut colspan og rowspan på én enkelt tabell, så en nettleserbundet rapport beholder sitt båndede utseende. RTF-eksport spenner ikke over kolonner i det hele tatt: ankerteksten lander i sin egen celle, og den gjenværende bredden av sammenslåingen kommer ut som tomme celler, noe som lar en bred tittel visuelt bli skjøvet mot venstre i en tekstbehandler. CSV har ikke noe begrep om en sammenslåing, så ankerverdien opptar ett felt, og hver dekket celle sendes ut som et tomt felt. Konklusjonen for en arbeidsbok som også mater avgrensede eksporter, er å holde alt bærende utenfor sammenslått geometri; artikkelen om CSV-, TSV- og HTML-eksport går gjennom hvert format i detalj

HotXLS sammenslått tittel eksportert fra Delphi til HTML med colspan og rowspan, RTF uten spenn, og CSV som flatete felt
Samme flettede tittel overlever HTML-eksport gjennom colspan og rowspan, degraderer i RTF til en enslig venstrelåst celle, og flater ut til en verdi pluss tomme felt i CSV

Én betryggelse for alle som veier dette mot filstørrelse: sammenslåinger koster nesten ingenting i rapportskala. Sammenslåingstabellen er bitteliten ved siden av celledataene, og å lese en dekket celle går fortsatt gjennom FindAt i stedet for å skanne. Ytelsespresset på store arbeidsbøker kommer fra andre steder, hovedsakelig stilbasseng-vekst og minnet lagringsstien holder på, som artikkelen om ytelse for store arbeidsbøker tar opp direkte. Begge sammenslåings-API-ene, de strukturelle redigeringsoperasjonene, og maldemoene følger med HotXLS Delphi Component