Technický článek

Duplikujte list XLSX v Delphi s HotXLS

Máte jeden list přesně tak, jak má být. Pás záhlaví je sloučený, šířky sloupců sedí na data, první dva řádky jsou zmrazené, oblast tisku i okraje jsou nastavené pro čistý export do A4 a karta je barevně označená, aby ji finance snadno našly. Teď report potřebuje dvanáct takových listů, po jednom pro každý region, všechny se stejným rozložením. Přepisovat ten list v kódu dvanáctkrát je přesně ten způsob, jak se nenápadně začne rozcházet: region 7 dostane sloupec o bod užší, region 11 ztratí zmrazení a nikdo si toho nevšimne, dokud se PDF nedostane na stůl manažerovi. Ve skutečnosti chcete programovou podobu Excelu Move or Copy, Create a copy: vzít hotový list a vytvořit z něj nezávislé kopie

Jádro XLSX v HotXLS, nativní knihovně pro Delphi a C++Builder, která čte a zapisuje soubory Excel bez automatizace samotného Excelu, už umělo přesouvat listy, mazat listy a kopírovat rozsahy buněk mezi listy. Až do verze v2.91.0 ale neumělo naklonovat celý list jedním voláním. Toto vydání přidává dva vstupní body: TXLSXWorksheet.CopyFrom, který kopíruje stav úrovně listu z jednoho listu na druhý, a TXLSXSheets.Duplicate, který přidá nový list a za vás spustí CopyFrom pro vás. Zajímavé není to, že kopíruje věci. Důležitá je záměrná hranice mezi tím, co se kopíruje do hloubky a co ne, a proč je právě tam, kde je

Jedno volání pro naklonování hotového listu

Operace vyšší úrovně je Duplicate. Stačí jí předat 1-based index zdrojového listu a vrátí zcela nový list, který zrcadlí rozložení i data originálu. Konvence indexování odpovídá Items[] na straně XLSX, takže první list má index 1, ne 0; pokud předáte index mimo rozsah, dostanete nil místo výjimky, tedy stejnou smlouvu o chybě, jakou používá zbytek kolekce listů XLSX

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);          // freeze top row + first column
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Clone with an explicit name...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...or let it pick the Excel-style default name.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

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

Dvě věci v tom úryvku stojí za zastavení. Zaprvé, FreezePanes bere argumenty v pořadí řádků, FreezePanes(ARow, ACol), takže se to shoduje s Cells[Row, Col] indexováním, a duplikát přebírá přesně stejné rozdělení zmrazení. Zadruhé, metoda se jmenuje Duplicate a ne zjevnější Copy, a to není otázka stylu. Copy je standardní rutina v jednotce System, používaná neustále pro řetězce a dynamická pole. Metoda nazvaná Copy na třídě by ji uvnitř těla metod zastínila a vytvořila přesně tu nejednoznačnost rozlišení, která vás kousne za šest měsíců. Duplicate se celému problému vyhne a na místě volání se čte správně

Výchozí název sleduje vlastní pravidlo Excelu

Když zavoláte jednoargumentovou přetíženou verzi nebo předáte prázdný název, nový list dostane jméno podle zdroje s příponou (2), a přípona se zvyšuje, dokud název není jedinečný. Když jednou duplikujete list Template, dostanete Template (2); když ho duplikujete znovu, dostanete Template (3), protože Template (2) už je obsazený. To odpovídá názvům, které Excel generuje ze svého příkazu Create a copy, takže se sešit, který váš kód vytvoří, chová tak, jak by uživatel čekal od listu duplikovaného ručně. Kontrola jedinečnosti běží proti živé kolekci listů, takže přeskakuje i názvy, které jste vytvořili ručně, nejen ty z dřívějších duplikací

Když generujete jeden list pro každý region nebo měsíc, sáhněte raději po přetížení s explicitním názvem. Předvídatelné schéma Region-North, Region-South je později snadněji adresovatelné než řada přípon (2), (3) a drží vaše pojmenované oblasti i vzorce napříč listy čitelné

Co CopyFrom kopíruje do hloubky

Pod kapotou Duplicate přidá list a pak zavolá CopyFrom(ASource), které můžete zavolat i přímo, když chcete klonovat na list, který jste už vytvořili. CopyFrom předem hlídá dva degenerační případy: kopírování z nil nebo kopírování listu na něj samotný vrátí okamžitě a nic neprovedou. Všechno potom už je samotné kopírování, a to je záměrně široké

Nejprve přijdou data buněk. CopyFrom si od zdroje vyžádá jeho UsedRange, těsný ohraničující obdélník vyplněných buněk a sloučených oblastí, a znovu použije stávající CopyRangeTo mechanismus, aby přenesl každou hodnotu, vzorec i index stylu jednotlivé buňky do cíle začínaje na A1. Nad buňkami potom přehraje celou vrstvu stavů na úrovni listu, která ze šablony dělá hotový výstup:

  • Sloučené oblasti, znovu vytvořené podle souřadnic tak, aby pruh zabíral stejný obdélník
  • Šířky sloupců a výšky řádků plus seznamy skrytých, sbalených a outline úrovní, zkopírované doslova tak, aby ne výchozí řádky a sloupce seděly přesně
  • Zmrazené příčky a stav zobrazení: úroveň zoomu, zobrazení mřížky a nulových hodnot, směr zprava doleva a typ zobrazení
  • Stav ochrany s možnostmi oprávnění pro jednotlivé akce, takže uzamčená šablona zůstane zamčená stejně
  • Celý blok nastavení stránky: okraje, orientace, velikost papíru, škálování a přizpůsobení na stránku, oblast tisku, názvy tisku, záhlaví a zápatí, a příznaky tisku mřížky a nadpisů
  • Oblast AutoFilter, barva karty a viditelnost listu

Výsledek je list, který tiskne, filtruje a zobrazuje se stejně jako zdroj. A protože se buňky, sloučení i seznamy rozměrů znovu fyzicky vytvářejí na novém listu místo toho, aby šlo jen o alias, je duplikát plně nezávislý. Zapište 999 do buňky na kopii a zdroj si ponechá původní hodnotu; tato nezávislost je jediné nejdůležitější vlastnictví klonu určeného pro paralelní regionální reporty a dodávaná SheetCopy ukázka to výslovně ověřuje

Co zůstává mělké a proč

A teď upřímná část. Grafy, vložené obrázky, tabulky XLSX, ověření dat a pravidla podmíněného formátování se nekopírují. To je zdokumentovaná, záměrná hranice, ne opomenutí, a stojí za to porozumět důvodu, abyste s tím uměli počítat místo toho, aby vás to překvapilo

Každá z těchto kolekcí nese identitu a odkazy, které naivní kopie polí nepřežijí. Graf odkazuje na zdrojový datový rozsah a vlastní vztah kresby v balíčku OOXML; naklonování objektu bez přemapování vztahu a odkazů na řady vytvoří graf vykreslený nad nesprávnými daty, nebo balíček, který Excel označí jako vyžadující opravu. Tabulka má název, který musí být v rámci sešitu jedinečný, záhlaví svázané s konkrétními sloupci a vlastní automaticky generovaný vztah. Podmíněné formáty a ověřování dat se vážou na souřadnicové rozsahy a v případě ověřování mohou odkazovat na jiné rozsahy pomocí vzorce. Správné hluboké kopírování kteréhokoli z nich znamená přepisovat odkazy a razit nové identity, což je skutečná práce se skutečnými místy selhání. Dělat to napůl, tedy zkopírovat objekt, ale ne jeho odkazy, je horší než nekopírovat vůbec: výsledkem je soubor, který se otevře s výzvou k opravě a tiše zahodí obsah. Proto engine kopíruje to, co umí zkopírovat čistě, a kolekce nesoucí odkazy nechává na volajícím, který ví, na co má cíl ukazovat

V praxi to znamená, že postup pro bohatší šablonu je tento: duplikujte list, abyste získali buňky, rozložení a nastavení tisku, a potom na kopii znovu sestavte graf, tabulku, ověření nebo podmíněné formátování se stejným API, jakým jste je vytvořili poprvé. Protože je znovu vytváříte nad vlastními rozsahy duplikátu, odkazy vyjdou správně už konstrukcí. U grafu, který čte A1:C10, přidejte na kopii nový graf ukazující na A1:C10 vlastního oblast; u AutoFilter, který chcete mít živý, vězte, že článek o sloučených buňkách a rozložení reportové šablony, který prochází tabulku sloučení a model rozsahů, který kopie přebírá

Kde duplikace zapadá do reportovacího procesu

Duplikace listu je přirozeným partnerem generování z tokenových zástupných znaků. Přístup ukotvený na tokenech v průvodci generováním reportů ze šablony v Delphi řeší problém zápisu dat do rozložení, které ostatní lidé upravují; duplikace řeší problém, že to samé rozložení potřebujete v jednom sešitu mnohokrát. Když tyto dva přístupy spojíte, vzorec je čistý: nechte jeden bezchybný list template s jeho tokeny, sloučeními a nastavením tisku a pro každý region nebo období zavolejte Template, vyplňte tokeny klonu příslušným výřezem dat a pokračujte dál. Původní šablona se nikdy nemění, takže zůstává spolehlivým zdrojem pro další klon a každý výstupní list začíná na rozložení identickém do posledního bajtuDuplicateJedna poznámka k pořadí kroků vám ušetří celou třídu zmatků. List duplikujte

předtím, než do něj nalijete data, ne potom. Šablona má nést strukturu a formátování, ne čísla z minulého čtvrtletí, a klonování prázdného stylovaného listu znamená, že každá kopie začíná čistě. Pokud duplikujete list, který už nese data, tato data se přenesou také, protože věrně kopíruje použitý rozsah; někdy je to přesně to, co chcete, ale pro fan-out report obvykle neCopyFromRychlý ověřovací návyk

Protože je rozdíl mezi hlubokou a mělkou kopií neviditelný, dokud ho nebudete hledat, přidejte do úlohy pětřádkovou kontrolu místo toho, abyste jen doufali, že se všechno přeneslo. Po duplikaci si přečtěte strukturální signály, které má klon zdědit, a ověřte, že se shodují se zdrojem

Počet sloučení, šířka sloupce, řádek zmrazení a příznak barvy karty vám řeknou, že vrstva, která se skutečně kopíruje, se opravdu přenesla. U jakéhokoli listu, který obsahoval graf, tabulku, ověření nebo podmíněné formátování, s nimi zacházejte jako se seznamem k přestavbě na kopii: jejich nepřítomnost je záměrná a oprava je jen několik volání, ne hlášení chyby. Tento mentální model, do hloubky tam, kde je to bezpečné, a mělký tam, kde by se odkazy rozbily, je celý příběh toho, jak tuto funkci používat dobře

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)]));
// Prove independence: mutate the copy, confirm the source is untouched.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value is still whatever it was.

Duplikace listu a kopie stavu listu popsaná zde jsou součástí verze v2.91.0 nativního HotXLS Delphi spreadsheet component, spolu se spustitelnou

ukázkou, která procvičuje cyklus klonování a úprav od začátku do konceCopyFrom sheet-state copy described here ship in v2.91.0 of the native HotXLS Delphi spreadsheet component, alongside a runnable SheetCopy sample that exercises the clone-and-mutate cycle end to end