Articolo tecnico

Duplicare un foglio di lavoro XLSX in Delphi con HotXLS

Hai configurato un foglio di lavoro alla perfezione. La fascia di intestazione è unita, la larghezza delle colonne si adatta ai dati, le prime due righe sono bloccate, l'area di stampa e i margini sono impostati per un'esportazione A4 pulita, e la scheda ha un colore specifico per consentire al reparto amministrativo di trovarla subito. Ora il report richiede dodici di questi fogli, uno per ciascuna area geografica, tutti basati sullo stesso layout. Ricostruire quel foglio via codice per dodici volte rischia di introdurre lievi discrepanze: la regione 7 riceve una colonna più stretta di un punto, la regione 11 perde il blocco dei riquadri, e nessuno se ne accorge finché il PDF non arriva sulla scrivania del responsabile. Ciò di cui si ha realmente bisogno è la versione a livello di codice del comando "Sposta o copia, Crea una copia" accessibile con il tasto destro in Excel: prendere il foglio completato e produrre duplicati indipendenti

Il motore XLSX di HotXLS, una libreria nativa per Delphi e C++Builder che legge e scrive file Excel senza ricorrere all'automazione di Excel, consentiva già di spostare fogli, eliminare fogli e copiare intervalli di celle tra i vari fogli. Ciò che non era possibile fare prima della versione v2.91.0 was clonare un intero foglio di lavoro in un'unica chiamata. Tale rilascio ha introdotto due punti di ingresso: TXLSXWorksheet.CopyFrom, che copia lo stato a livello di foglio da un foglio di lavoro all'altro, e TXLSXSheets.Duplicate, che aggiunge un nuovo foglio ed esegue CopyFrom. La parte interessante non risiede nella copia in sé, ma nella distinzione intenzionale tra ciò che viene copiato in modo profondo (deep copy) e ciò che invece non lo è, e nelle ragioni alla base di tale scelta

Un'unica chiamata per clonare un foglio completato

L'operazione ad alto livello è gestita da Duplicate. Passando l'indice (base 1) del foglio di origine, la funzione restituisce un foglio di lavoro nuovo che rispecchia il layout e i dati dell'originale. La convenzione degli indici segue quella di Items[] lato XLSX, per cui il primo foglio corrisponde all'indice 1 e non a 0; fornendo un indice non valido si riceve nil anziché un'eccezione, rispettando lo stesso contratto di errore utilizzato dal resto della raccolta dei fogli 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;

Due aspetti del frammento di codice meritano attenzione. Primo, FreezePanes accetta i parametri con precedenza alla riga, FreezePanes(ARow, ACol), allineandosi con l'indicizzazione Cells[Row, Col]; il duplicato erediterà la stessa identica suddivisione di blocco dei riquadri. Secondo, il metodo è denominato Duplicate anziché il più intuitivo Copy, e non si tratta di una scelta di stile. Copy è una routine standard dell'unità System, impiegata per stringhe e array dinamici. Un metodo denominato Copy su una classe ne oscurerebbe l'uso nei corpi dei metodi, creando proprio quel tipo di ambiguità nella risoluzione dei nomi che si ripresenta a distanza di mesi. Duplicate evita il problema all'origine, risultando chiaro nel punto di chiamata

Il nome predefinito segue le regole di Excel

Quando si chiama l'overload a un solo argomento, o si passa una stringa vuota per il nome, il nuovo foglio viene denominato come quello di origine con il suffisso (2), e il suffisso viene incrementato finché il nome non risulta univoco. Duplicando il foglio Template una prima volta si otterrà Template (2); duplicandolo nuovamente si otterrà Template (3), in quanto Template (2) è già utilizzato. Questo comportamento ricalca i nomi generati da Excel con il comando di copia, per cui una cartella di lavoro prodotta via codice risulterà naturale per l'utente finale. La verifica di univocità viene eseguita sulla raccolta dei fogli attivi, per cui riconosce anche i nomi creati manualmente, non solo quelli derivanti da precedenti duplicazioni

Se si sta generano un foglio per regione o per mese, si consiglia di usare l'overload con nome esplicito. Uno schema coerente come Region-North, Region-South risulterà più facile da gestire in seguito rispetto a una serie di suffissi (2), (3), mantenendo leggibili i nomi definiti e le formule tra i vari fogli

Cosa viene copiato in modo profondo da CopyFrom

Dietro le quinte, Duplicate aggiunge il foglio e poi chiama CopyFrom(ASource), metodo che può essere richiamato direttamente anche quando si desidera clonare dati su un foglio già creato. CopyFrom previene due casi anomali in partenza: la copia da un oggetto nil e la copia di un foglio su se stesso, che interrompono subito l'esecuzione senza compiere operazioni. Tutto il resto riguarda la copia effettiva, che è strutturata in modo esteso

I dati delle celle vengono copiati per primi. CopyFrom richiede al foglio di origine il suo UsedRange, il rettangolo che racchiude le celle popolate e gli intervalli uniti, e riutilizza il meccanismo CopyRangeTo esistente per copiare ogni valore, formula e indice di stile di cella nella destinazione a partire da A1. Oltre alle celle, riproduce l'intero livello di stato a livello di foglio che definisce la struttura finale del modello:

  • Intervalli uniti, ricreati in base alle coordinate per far sì che la fascia occupi lo stesso rettangolo
  • Larghezze delle colonne e altezze delle righe, incluse le liste degli elementi nascosti, compressi e a livello struttura, copiate letteralmente per allineare righe e colonne non predefinite
  • Blocco dei riquadri e stato di visualizzazione: livello di zoom, visualizzazione delle griglie e dei valori zero, direzione da destra a sinistra e tipo di vista
  • Stato di protezione con le relative opzioni di autorizzazione per azione, in modo che un modello protetto rimanga bloccato con le medesime modalità
  • L'intero blocco di impostazione pagina: margini, orientamento, dimensioni carta, scala e adattamento alla pagina, area di stampa, titoli di stampa, intestazioni e piè di pagina, e i flag di stampa griglia e intestazioni
  • L'intervallo AutoFilter, il colore della scheda e la visibilità del foglio

Il risultato è un foglio che si stampa, si filtra e si presenta in modo identico a quello di origine. E poiché le celle, le unioni e gli elenchi dimensionali sono ricreati fisicamente sul nuovo foglio anziché essere collegati per riferimento, il duplicato risulterà indipendente. Scrivere 999 in una cella della copia non modificherà il valore dell'originale; questa indipendenza è la proprietà principale di un clone destinato a report paralleli per aree geografiche, e la demo SheetCopy distribuita con il componente lo verifica esplicitamente

Cosa viene escluso dalla copia profonda, e perché

Ecco la precisazione importante. Grafici, immagini incorporate, tabelle XLSX, convalide dei dati e regole di formattazione condizionale non vengono copiati. Questo rappresenta un limite documentato e intenzionale, non una svista, ed è importante comprenderne le ragioni per pianificare lo sviluppo in modo corretto

Ciascuna di queste raccolte contiene identità e riferimenti che non sopravvrebbero a una copia superficiale. Un grafico punta a un intervallo di dati di origine e possiede una relazione di disegno nel pacchetto OOXML; clonare l'oggetto senza rimappare la relazione e i riferimenti alle serie produce un grafico associato ai dati errati, o un pacchetto che Excel segnalerà come danneggiato. Una tabella ha un nome che deve essere univoco all'interno della cartella di lavoro, una riga di intestazione collegata a colonne specifiche e una relazione generata automaticamente. I formati condizionali e le convalide dei dati si applicano a intervalli di coordinate e, nel caso delle convalide, possono fare riferimento a altri intervalli tramite formule. Eseguire una copia profonda corretta di ciascuno di questi elementi comporta la riscrittura dei riferimenti e la creazione di nuove identità, attività complessa e soggetta a errori. Eseguirla a metà, copiando l'oggetto ma non i suoi riferimenti, è peggio che non copiarlo affatto: produce un file che genera richieste di ripristino all'apertura e omette silenziosamente contenuti. Pertanto, il motore copia solo gli elementi che può duplicare in modo pulito, lasciando le raccolte contenenti riferimenti al codice chiamante, che conosce a cosa debba puntare la destinazione

In pratica, il flusso di lavoro per un modello più complesso prevede di: duplicare il foglio per ottenere celle, layout e impostazioni di stampa, quindi ricostruire il grafico, la tabella, le convalide o i formati condizionali sulla copia utilizzando le stesse API impiegate la prima volta. Poiché questi elementi vengono ricreati sugli intervalli specifici del foglio duplicato, i riferimenti risulteranno corretti. Per un grafico che legge A1:C10, aggiungi un nuovo grafico sulla copia che punti all'intervallo A1:C10 della copia stessa; se si desidera un AutoFilter attivo, si noti che l'intervallo del filtro viene mantenuto, per cui occorre solo riapplicare i criteri delle colonne. Le regole di formattazione condizionale e di convalida dei dati verranno aggiunte tramite le chiamate descritte nell'articolo sulle celle unite e layout dei modelli di report, che analizza la tabella delle unioni e il modello di intervallo ereditati dalla copia

Integrazione della duplicazione in una pipeline di reportistica

La duplicazione dei fogli di lavoro rappresenta il naturale complemento della generazione basata su segnaposto. L'approccio basato sui token illustrato nella guida alla generazione di report basata su modelli in Delphi risolve il problema della scrittura di dati in un layout modificato da terzi; la duplicazione risolve la necessità di riprodurre quel layout più volte in una cartella di lavoro. Combinando queste funzioni, lo schema risulta lineare: si mantiene un foglio Template originale intatto con i suoi token, le unioni e le impostazioni di stampa, quindi per ciascuna area o periodo si chiama Duplicate, si popolano i token del clone con i dati specifici e si procede oltre. Il modello originale non viene mai modificato, rimanendo disponibile come sorgente affidabile per il clone successivo, e ogni foglio di output presenterà un layout identico a livello di byte

Un'indicazione sulla sequenza previene possibili errori. Duplica il foglio prima di inserirvi i dati, non dopo. Un modello deve contenere la struttura e la formattazione, non i numeri del trimestre precedente, e clonare un foglio vuoto con stili garantisce che ogni duplicato parta pulito. Se si duplica un foglio che contiene già dei dati, anche questi ultimi verranno copiati, poiché CopyFrom copia l'intervallo utilizzato; questo comportamento è utile solo in casi specifici, ma di solito non per un report con ripartizione dei dati

Una rapida routine di verifica

Poiché la distinzione tra copia profonda e superficiale è invisibile finché non la si verifica, si consiglia di integrare un controllo rapido di cinque righe nel processo. Dopo la duplicazione, rileggi i parametri strutturali che il clone dovrebbe ereditare e verifica che corrispondano a quelli di origine

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.

Il numero di unioni, la larghezza di una colonna, la riga bloccata e il flag del colore della scheda indicano che la parte copiata è andata a buon fine. Separatamente, per ogni foglio che conteneva un grafico, una tabella, convalide o formati condizionali, considera questi elementi come una lista da ricostruire sulla copia: la loro assenza è prevista dal design e la soluzione richiede solo alcune chiamate. Questo modello mentale (copia profonda dove è sicuro, superficiale dove i riferimenti si romperebbero) riassume l'uso ottimale di questa funzionalità

La duplicazione dei fogli di lavoro e la copia dello stato del foglio tramite CopyFrom descritte in questa pagina sono disponibili a partire dalla versione v2.91.0 del componente per fogli di calcolo HotXLS Delphi nativo, insieme a un esempio SheetCopy pronto all'uso che illustra il ciclo di clonazione e modifica