Teknisk artikel

Duplicera ett XLSX-arbetsblad i Delphi med HotXLS

Du har byggt ett blad exakt rätt. Rubrikbandet är sammanfogat, kolumnbredderna passar data, de två översta raderna är frysta, utskriftsområdet och marginalerna är inställda för en ren A4-export och fliken är färgad så att ekonomiavdelningen kan hitta den. Nu behöver rapporten tolv sådana, en per region, som alla utgår från samma layout. Att bygga om det bladet i kod tolv gånger är hur en subtil avvikelse smyger sig in: region 7 får en kolumn som är en punkt smalare, region 11 förlorar frysningen, och ingen märker det förrän PDF-filen landar på en chefs skrivbord. Vad du egentligen vill ha är den programmatiska versionen av Excels högerklick, Flytta eller kopiera, Skapa en kopia: ta det färdiga bladet och stämpla ut oberoende dubbletter

XLSX-motorn i HotXLS, ett inbyggt Delphi- och C++Builder-bibliotek som läser och skriver Excel-filer utan att automatisera Excel själv, kunde redan flytta blad, ta bort blad och kopiera cellintervall mellan blad. Vad den inte kunde göra förrän v2.91.0 was clone a whole worksheet in one call. Den versionen lägger till två startpunkter: TXLSXWorksheet.CopyFrom, som kopierar tillstånd på bladnivå från ett kalkylblad till ett annat, och TXLSXSheets.Duplicate, som lägger till ett nytt blad och kör CopyFrom åt dig. Det intressanta är inte att det kopierar saker. Det är den medvetna gräns som dras mellan vad som djupkopieras och vad som inte gör det, och varför den gränsen ligger där den gör

Ett anrop för att klona ett färdigt blad

Huvudoperationen är Duplicate. Ge den det 1-baserade indexet för källbladet och den returnerar ett helt nytt kalkylblad som speglar originalets layout och data. Indexkonventionen matchar Items[] på XLSX-sidan, så blad ett har index 1, inte 0; skicka ett index utanför intervallet så får du nil istället för ett undantag, samma felkontrakt som resten av XLSX-bladsamlingen använder

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 översta raden + första kolumnen
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Klona med ett explicit namn...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...eller låt den välja standardnamnet i Excel-stil.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

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

Två saker i det kodavsnittet är värda att stanna upp vid. För det första tar FreezePanes sina argument med raden först, FreezePanes(ARow, ACol), så att det stämmer överens med Cells[Row, Col]-indexeringen; dubbletten ärver den exakta frysningen. För det andra heter metoden Duplicate och inte det mer uppenbara Copy, och det är inte ett stilval. Copy är en standardrutin i System-enheten, som ständigt används för strängar och dynamiska arrayer. En metod som heter Copy på a class would shadow it inside method bodies and create exactly the kind of resolution ambiguity that bites you six months later. Duplicate sidesteps the whole problem and reads correctly at the call site

Standardnamnet följer Excels egen regel

När du anropar överlagringen med ett argument, eller skickar en tom namnsträng, namnges det nya bladet efter källan med suffixet (2), och suffixet räknas upp tills namnet är unikt. Duplicera Template-bladet en gång så får du Template (2); duplicera det igen så får du Template (3), eftersom Template (2) redan är upptaget. Detta speglar namnen som Excel genererar från sitt eget kommando Skapa en kopia, så en arbetsbok som din kod producerar ser ut på det sätt som en användare förväntar sig att en manuellt duplicerad skulle se ut. Unikhetskontrollen körs mot den aktiva bladsamlingen, vilket innebär att den också kliver över namn som du skapat manuellt, inte bara de från tidigare dupliceringar

Om du genererar ett blad per region eller per månad bör du istället använda överlagringen med explicit namn. Ett förutsägbart schema som Region-North, Region-South är lättare att adressera senare än en sträng av (2)-, (3)-suffix, och det håller dina definierade namn och formler över blad läsliga

Vad CopyFrom djupkopierar

Under huven lägger Duplicate till bladet och anropar sedan CopyFrom(ASource), vilket du också kan anropa direkt när du vill klona till ett blad du redan skapat. CopyFrom garderar sig mot de två degenererade fallen i förväg: att kopiera från nil eller att kopiera ett blad till sig självt, båda returnerar omedelbart och gör ingenting. Allt efter det är själva kopieringen, och den är medvetet omfattande

Celldata kommer först. CopyFrom frågar källan efter dess UsedRange, den snäva markeringsramen för fyllda celler och sammanfogade områden, och återanvänder det befintliga CopyRangeTo-systemet för att överföra varje värde, formel och stilindex per cell till målet med början vid A1. Utöve cellerna spelar den upp hela lagret av tillstånd på bladnivå som gör att en mall ser färdig ut:

  • Sammanfogade intervall, återskapade per koordinat så att bannern spänner över samma rektangel
  • Kolumnbredder och radhöjder, plus de dolda, komprimerade och disposition-nivålistorna, kopierade ordagrant så att icke-standardrader och kolumner linjerar exakt
  • Frysningar (freeze panes) och vyläge: zoomnivå, visning av stödlinjer och nollvärden, höger-till-vänster-riktning samt vytyp
  • Skyddstillstånd med dess behörighetsalternativ per åtgärd, så att en låst mall förblir låst på samma sätt
  • Hela sidinställningsblocket: marginaler, orientering, pappersstorlek, skalning och anpassa till sida, utskriftsområde, utskriftsrubriker, sidhuvuden och sidfötter samt flaggorna för utskrift av stödlinjer och rubriker
  • AutoFilter-intervallet, flikfärgen och bladets synlighet

Resultatet är ett blad som skrivs ut, filtreras och presenteras identiskt med sin källa. Och eftersom cellerna, sammanfogningarna och dimensionslistorna återskapas fysiskt på det nya bladet snarare än att bara länkas (aliased), är dubbletten helt oberoende. Skriv 999 i en cell på kopian och källan behåller sitt ursprungliga värde; det oberoendet är den enskilt viktigaste egenskapen hos en klon avsedd för parallella regionala rapporter, och den medföljande demot SheetCopy bekräftar det uttryckligen

Vad den lämnar grunt, och varför

Nu till den ärliga delen. Diagram, inbäddade bilder, XLSX-tabeller, datavalideringar och regler för villkorsstyrd formatering kopieras inte. Detta är en dokumenterad, medveten gräns, inte ett förbiseende, och det är värt att förstå resonemanget så att du kan planera runt det istället för att bli överraskad av det

Var och en av dessa samlingar bär på identitet och referenser som inte överlever en naiv fältkopiering. Ett diagram pekar på ett käll-data-intervall och äger en ritningsrelation i OOXML-paketet; att klona objektet utan att mappa om relationen och seriereferenserna ger ett diagram som renderas mot fel data, eller ett paket som Excel flaggar som i behov av reparering. En tabell har ett namn som måste vara unikt inom arbetsboken, en rubrikrad kopplad till specifika kolumner och sin egen autogenererade relation. Villkorsstyrda format och datavalideringar är kopplade till koordinatintervall och kan, i valideringsfallet, referera till andra intervall via formel. Att djupkopiera något av detta korrekt innebär att skriva om referenser och prägla nya identiteter, vilket är ett reellt arbete med verkliga felsätt. Att göra det halvvags, genom att kopiera objektet men inte dess referenser, är värre än att inte kopiera alls: det ger en fil som öppnas med en reparationsprompt och tyst tappar innehåll. Därför kopierar motorn de saker den kan kopiera rent och lämnar de referensbärande samlingarna till anroparen, som vet vad målet ska peka på

Var duplicering passar in i en rapportpipeline

Kalkylbladsduplicering är den naturliga följeslagaren till platshållar-driven generering. Det symbolförankrade (token-anchored) tillvägagångssättet i guiden till mall-driven rapportgenerering i Delphi löser problemet med att skriva data till en layout som andra redigerar; duplicering löser problemet med att behöva den layouten många gånger i en arbetsbok. Kombinera dem så blir mönstret rent: behåll ett orört Template-blad med dess symboler, sammanfogningar och utskriftsinställningar, och för varje region eller period anropar du Duplicate, fyller klonens symboler med den datadelen och går vidare. Den orörda mallen ändras aldrig, så den förblir en pålitlig källa för nästa klon, och varje utdatablad startar från en byte-för-byte identisk layout

En sekvenseringsnotering sparar en hel klass av förvirring. Duplicera bladet innan du häller data i det, inte efter. En mall ska innehålla struktur och formatering, inte förra kvartalets siffror, och att klona ett tomt formaterat blad innebär att varje dubblett startar ren. Om du duplicerar ett blad som redan innehåller data följer den datan med, eftersom CopyFrom kopierar det använda intervallet troget; det är ibland vad man vill, men för en utspridd rapport är det vanligtvis inte det

En snabb verifieringsvana

Eftersom uppdelningen mellan djup- och grundkopiering är osynlig tills du letar efter den, bör du bygga in en femraders kontroll i jobbet snarare än att lita på att allt kom över. Efter dupliceringen läser du tillbaka de strukturella signalerna som klonen ska ärva och kontrollerar att de matchar källan

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)]));
// Bevisa oberoende: ändra kopian, bekräfta att källan är orörd.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value är fortfarande vad det än var.

Antalet sammanfogningar, en kolumnbredd, frysningsraden och flikfärgsflaggan talar om för dig att det lager som är kopierat faktiskt kom över. Separat, i alla blad som bar ett diagram, en tabell, valideringar eller villkorsstyrda format, behandla dessa som en lista att bygga om på kopian: deras frånvaro är avsiktlig och lösningen är några få anrop, inte en felrapport. Den mentala modellen, djup där det är säkert och grund där referenser skulle gå sönder, är hela historien om hur man använder denna funktion väl

Kalkylbladsduplicering och CopyFrom-bladkopieringen som beskrivs här levereras i v2.91.0 av den inbyggda HotXLS Delphi kalkylbladskomponenten, tillsammans med ett körbart SheetCopy-exempel som visar hela klona-och-ändra-cykeln