Teknisk artikel

Duplikér et XLSX-regneark i Delphi med HotXLS

Du har bygget ét ark helt rigtigt. Overskriftsbåndet er flettet, kolonnebredderne passer til dataene, de to øverste rækker er frosset, printområdet og margenerne er sat til en ren A4-udskrift, og fanen har farve, så økonomiafdelingen kan finde den. Nu skal rapporten bruge tolv af disse ark, ét pr. region, alle med samme udgangspunkt i layoutet. Genopbygger du arket i kode tolv gange, er det sådan, subtil afvigelse sniger sig ind: region 7 får en kolonne, der er en anelse smallere, region 11 mister frysningen, og ingen opdager det, før PDF'en lander på en leders skrivebord. Det, du egentlig vil have, er den programmatiske udgave af Excels højreklik, Flyt eller kopiér, Opret en kopi: tag det færdige ark og stemp uafhængige duplikater ud

XLSX-motoren i HotXLS, et native Delphi- og C++Builder-bibliotek der læser og skriver Excel-filer uden at automatisere selve Excel, kunne allerede flytte ark, slette ark og kopiere celleområder mellem ark. Det, den ikke kunne før v2.91.0, var at klone et helt regneark med ét kald. Den udgivelse tilføjer to indgangspunkter: TXLSXWorksheet.CopyFrom, som kopierer ark-niveau tilstand fra ét regneark til et andet, og TXLSXSheets.Duplicate, som tilføjer et nyt ark og kører CopyFrom for dig. Det spændende er ikke, at den kan kopiere ting. Det er den bevidste grænse, der er trukket mellem det, der dybdekopieres, og det, der ikke gør, og hvorfor grænsen ligger lige dér

Diagram over HotXLS Duplicate- og CopyFrom-indgangspunkter, der kloner et Delphi XLSX-regneark gennem én delt copy engine med nil- og selv Kopieringsguards, der returnerer et uafhængigt ark
Duplicate pakker CopyFrom ind med et nyt ark, mens et dårligt indeks returnerer nil i stedet for at udløse en exception

Ét kald til at klone et færdigt ark

Den overordnede operation er Duplicate. Angiv kildearkets 1-baserede indeks, og den returnerer et splinternyt regneark, der spejler originalens layout og data. Indekskonventionen følger Items[] på XLSX-siden, så ark ét har indeks 1, ikke 0; angiver du et indeks uden for grænserne, får du nil i stedet for en undtagelse, samme fejlkontrakt som resten af XLSX-arksamlingen bruger

var
  Book: TXLSXWorkbook;
  Template, Copy: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Template := Book.Sheets.Add('Template');
    Template.Cells[1, 1].Value := 'Quarterly Statement';
    Template.Range['A1:C1'].Merge;
    Template.ColWidth[1] := 18;
    Template.FreezePanes(2, 1);          // frys øverste række + første kolonne
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Klon med et eksplicit navn...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...eller lad den vælge Excel-stilens standardnavn.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

    Book.SaveAs('regions.xlsx');
  finally
    Book.Free;
  end;
end;

Der er to ting i det udsnit, det er værd at stoppe op ved. For det første tager FreezePanes sine argumenter i rækkefølgen række-først, FreezePanes(ARow, ACol), så det passer med indekseringen i Cells[Row, Col]; duplikatet arver præcis den samme frysningsopdeling. For det andet hedder metoden Duplicate og ikke det mere oplagte Copy, og det er ikke en stilpræference. Copy er en standardrutine i enheden System, som bruges konstant til strenge og dynamiske arrays. En metode kaldet Copy på en klasse ville skygge for den inde i metodekroppe og skabe præcis den slags opløsningstvetydighed, der bider dig seks måneder senere. Duplicate undgår hele problemet og læses korrekt ved kaldestedet

Standardnavnet følger Excels egen regel

Når du kalder den enkeltargument-overload, eller angiver en tom navnestreng, får det nye ark navn efter kilden med et (2)-suffiks, og suffikset tæller op, indtil navnet er unikt. Duplikerer du Template-arket én gang, får du Template (2); duplikerer du det igen, får du Template (3), fordi Template (2) allerede er optaget. Det spejler de navne, Excel selv genererer fra sin kommando Opret en kopi, så en arbejdsbog, din kode producerer, ser ud, som en bruger ville forvente af en manuelt duplikeret én. Unikhedstjekket kører mod den aktive arksamling, hvilket betyder, at den også springer uden om navne, du har oprettet manuelt, ikke kun dem fra tidligere dupliceringer

Genererer du ét ark pr. region eller pr. måned, bør du hellere bruge overloaden med eksplicit navn. Et forudsigeligt Region-North, Region-South-skema er lettere at adressere senere end en række (2), (3)-suffikser, og det holder dine definerede navne og krydsark-formler læsbare

Hvad CopyFrom kopierer dybt

Under motorhjelmen tilføjer Duplicate arket og kalder derefter CopyFrom(ASource), som du også kan kalde direkte, når du vil klone ind på et ark, du allerede har oprettet. CopyFrom tjekker for de to degenererede tilfælde med det samme: kopiering fra nil eller kopiering af et ark ind på sig selv returnerer begge med det samme og gør ingenting. Alt derefter er selve kopieringen, og den er bevidst bred

Celledataene kommer først. CopyFrom beder kilden om dens UsedRange, den tætte afgrænsningsboks for udfyldte celler og flettede områder, og genbruger den eksisterende CopyRangeTo-maskine til at overføre hver værdi, formel og pr.-celle stilindeks til målet med start ved A1. Oven på cellerne genafspiller den hele laget af ark-niveau tilstand, der får en skabelon til at se færdig ud:

  • Flettede områder, genskabt efter koordinat, så banneret dækker det samme rektangel
  • Kolonnebredder og rækkehøjder, samt listerne over skjulte, sammenklappede og dispositionsniveauer, kopieret ordret, så rækker og kolonner, der afviger fra standard, passer nøjagtigt
  • Fastfrosne ruder og visningstilstanden: zoomniveau, visning af gitterlinjer og nulværdier, retning fra højre mod venstre, og visningstypen
  • Beskyttelsestilstand med dens pr.-handling tilladelsesindstillinger, så en låst skabelon forbliver låst på samme måde
  • Hele siden-opsætning-blokken: margener, retning, papirstørrelse, skalering og tilpas-til-side, printområde, printtitler, sidehoveder og sidefødder, samt flagene for udskrivning af gitterlinjer og overskrifter
  • AutoFilter-området, fanens farve og arkets synlighed

Resultatet er et ark, der printer, filtrerer og fremtræder identisk med kilden. Og fordi cellerne, fletningerne og dimensionslisterne fysisk genskabes på det nye ark i stedet for at blive aliaseret, er duplikatet fuldstændig uafhængigt. Skriv 999 ind i en celle på kopien, og kilden bevarer sin oprindelige værdi; den uafhængighed er den vigtigste enkeltstående egenskab ved et klon beregnet til parallelle regionale rapporter, og den medfølgende SheetCopy-demo påviser det eksplicit

Hvad det lader være overfladisk, og hvorfor

Nu til den ærlige del. Diagrammer, indlejrede billeder, XLSX-tabeller, datavalideringer og regler for betinget formatering bliver ikke kopieret. Det er en dokumenteret, bevidst grænse, ikke en forglemmelse, og det er værd at forstå ræsonnementet, så du kan planlægge efter det i stedet for at blive overrasket over det

Hver af disse samlinger bærer identitet og referencer, der ikke overlever en naiv feltkopiering. Et diagram peger på et kildedataområde og ejer en tegnerelation i OOXML-pakken; at klone objektet uden at omlægge relationen og seriereferencerne giver et diagram, der tegnes mod de forkerte data, eller en pakke, som Excel markerer som havende brug for reparation. En tabel har et navn, der skal være unikt inden for arbejdsbogen, en overskriftsrække bundet til bestemte kolonner, og sin egen automatisk genererede relation. Betingede formater og datavalideringer knytter sig til koordinatområder og kan i valideringens tilfælde referere til andre områder via formel. At dybdekopiere nogen af disse korrekt betyder at omskrive referencer og præge nye identiteter, hvilket er reelt arbejde med reelle fejltilstande. At gøre det halvt, ved at kopiere objektet men ikke dets referencer, er værre end slet ikke at kopiere: det giver en fil, der åbner med en reparationsprompt og stiltiende dropper indhold. Så motoren kopierer de ting, den kan kopiere rent, og overlader de referencebærende samlinger til den kaldende kode, som ved, hvad målet skal pege på

I praksis betyder det, at arbejdsgangen for en rigere skabelon er: duplikér arket for at få cellerne, layoutet og printopsætningen, og genopbyg derefter diagrammet, tabellen, valideringerne eller de betingede formater på kopien med samme API, du brugte til at oprette dem første gang. Fordi du genskaber dem mod duplikatets egne områder, kommer referencerne ud korrekt i kraft af konstruktionen. For et diagram, der læser A1:C10, tilføjer du et nyt diagram på kopien, der peger på kopiens A1:C10; for et AutoFilter, du vil have levende, bemærk, at filterets område faktisk overføres, så du kun skal genanvende kolonnekriterierne. Reglerne for betinget formatering og datavalidering skal du genskabe gennem de samme kald, som beskrives i artiklen om flettede celler og skabelonlayout til rapporter, som gennemgår fletningstabellen og områdemodellen, kopien arver

Diagram, der deler HotXLS XLSX regnearksduplikering i Delphi i layouttilstanden, CopyFrom deep-copier, og de referencebærende diagrammer, tabeller og regler, der efterlades til kalderen at genopbygge
Layout, printopsætning og beskyttelse deep-copyes rent; diagrammer, tabeller og regler bærer referencer, der skal genopbygges

Hvor duplikering passer ind i en rapporteringspipeline

Arkduplikering er den naturlige makker til pladsholder-drevet generering. Den token-forankrede tilgang i guiden til skabelondrevet rapportgenerering i Delphi løser problemet med at skrive data ind i et layout, andre redigerer; duplikering løser problemet med at have brug for det layout mange gange i én arbejdsbog. Kombinerer du dem, bliver mønsteret rent: behold ét urørt Template-ark med dets tokens, fletninger og printopsætning, og kald derefter Duplicate for hver region eller periode, udfyld klonens tokens med den udsnit af data, og gå videre. Det urørte skabelonark bliver aldrig ændret, så det forbliver en pålidelig kilde til den næste klon, og hvert outputark starter fra et byte-for-byte identisk layout

Én bemærkning om rækkefølgen sparer dig for en hel klasse af forvirring. Duplikér arket før, du hælder data i det, ikke efter. En skabelon bør indeholde struktur og formatering, ikke sidste kvartals tal, og at klone et tomt, formateret ark betyder, at hvert duplikat starter rent. Duplikerer du et ark, der allerede bærer data, følger de data med, fordi CopyFrom kopierer det anvendte område trofast; det er lejlighedsvis, hvad du vil have, men for en fan-out-rapport er det som regel ikke

En hurtig verifikationsvane

Fordi opdelingen mellem dybdekopi og overfladekopi er usynlig, indtil du leder efter den, bør du bygge et fem-linjers tjek ind i jobbet i stedet for at stole på, at alt kom med. Efter dupliceringen læser du de strukturelle signaler tilbage, som klonen forventes at have arvet, og bekræfter, at de matcher kilden

Copy := Book.Sheets.Duplicate(1, 'Region-North');
WriteLn(Format('merged=%d  colA=%.1f  freezeRow=%d  tabAuto=%d',
  [Copy.MergedCells.Count, Copy.ColWidth[1],
   Copy.FreezeRow, Integer(Copy.TabColorIsAuto)]));
// Bevis uafhængighed: ændr kopien, bekræft at kilden er urørt.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value er stadig, hvad den var.

Fletningsantallet, en kolonnebredde, frysningsrækken og fane-farve-flaget fortæller dig, at det lag, der faktisk kopieres, kom med. Separat skal du i ethvert ark, der bar et diagram, en tabel, valideringer eller betingede formater, behandle dem som en genopbygningsliste på kopien: deres fravær er tilsigtet, og løsningen er nogle få kald, ikke en fejlrapport. Den mentale model, dyb hvor det er sikkert og overfladisk hvor referencer ville gå i stykker, er hele historien om, hvordan man bruger denne funktion godt

Arkduplikering og den CopyFrom-ark-tilstandskopiering, der er beskrevet her, følger med i v2.91.0 af den native HotXLS Delphi regnearkskomponent, sammen med et kørbart SheetCopy-eksempel, der afprøver klon-og-ændr-cyklussen fra ende til anden

Diagram over en HotXLS rapporteringspipeline i Delphi, der duplikerer ét urørt stylet XLSX skabelonark til peregion kloner, hver fyldt med sine egne data først efter kloning
Hold skabelonen uplettet, dupliker den pr. region, og hæld kun data ind efter kloningen