Article technique

Dupliquer une feuille XLSX dans Delphi avec HotXLS

Vous avez construit une feuille exactement comme il faut. Le bandeau d'en-tête est fusionné, les largeurs de colonnes correspondent aux données, les deux premières lignes sont figées, la zone d'impression et les marges sont réglées pour une exportation A4 nette, et l'onglet est coloré pour que l'équipe financière le retrouve. Maintenant, le rapport en réclame douze, une par région, chacune partant de la même mise en page. Recréer cette feuille en code douze fois, c'est laisser s'installer de légers écarts: la région 7 se retrouve avec une colonne un peu plus étroite, la région 11 perd le figé des volets, et personne ne s'en rend compte avant que le PDF n'arrive sur le bureau d'un responsable. Ce que vous voulez vraiment, c'est la version programmatique du clic droit d'Excel, Déplacer ou copier, Créer une copie: prendre la feuille terminée et en produire des doublons indépendants

Le moteur XLSX de HotXLS, une bibliothèque Delphi et C++Builder native qui lit et écrit les fichiers Excel sans automatiser Excel lui-même, savait déjà déplacer des feuilles, supprimer des feuilles et copier des plages de cellules d'une feuille à l'autre. Ce qu'il ne pouvait pas faire avant la v2.91.0, c'était cloner une feuille entière en un seul appel. Cette version ajoute deux points d'entrée: TXLSXWorksheet.CopyFrom, qui copie l'état au niveau de la feuille d'une feuille source vers une autre, et TXLSXSheets.Duplicate, qui ajoute une nouvelle feuille et exécute CopyFrom pour vous. La partie intéressante n'est pas qu'il copie des choses. C'est la ligne délibérée entre ce qui est copié en profondeur et ce qui ne l'est pas, et la raison pour laquelle cette ligne se trouve là

Un seul appel pour cloner une feuille terminée

L'opération de haut niveau est Duplicate. Donnez-lui l'indice basé sur 1 de la feuille source et elle renvoie une nouvelle feuille de calcul qui reproduit la mise en page et les données de l'original. La convention d'indexation correspond à Items[] sur le côté XLSX, donc la feuille un est à l'indice 1, pas 0; passez un indice hors plage et vous obtenez nil au lieu d'une exception, selon le même contrat d'échec que le reste de la collection de feuilles XLSX utilise

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;

Deux points de cet extrait méritent qu'on s'y attarde. D'abord, FreezePanes prend ses arguments ligne d'abord, FreezePanes(ARow, ACol), donc il s'aligne sur l'indexation de Cells[Row, Col] ; la copie hérite exactement du même découpage de figement. Deuxièmement, la méthode s'appelle Duplicate et non Copy, et ce n'est pas une question de style. Copy est une routine standard dans l'unité System, utilisée constamment pour les chaînes et les tableaux dynamiques. Une méthode appelée Copy sur une classe l'écraserait à l'intérieur du corps des méthodes et créerait exactement le genre d'ambiguïté de résolution qui vous rattrape six mois plus tard. Duplicate contourne entièrement le problème et se lit correctement à l'appel

Le nom par défaut suit la règle d'Excel

Quand vous appelez la surcharge à un argument ou que vous passez une chaîne de nom vide, la nouvelle feuille est nommée d'après la source avec un (2)suffixe, et ce suffixe augmente jusqu'à ce que le nom soit unique. Dupliquez la Template feuille une fois et vous obtenez Template (2); dupliquez-la encore et vous obtenez Template (3), parce que Template (2) est déjà pris. Cela reflète les noms qu'Excel génère avec sa propre commande Créer une copie, de sorte qu'un classeur produit par votre code ressemble à ce qu'un utilisateur attendrait d'une duplication manuelle. Le contrôle d'unicité s'effectue sur la collection de feuilles active, ce qui signifie qu'il saute aussi les noms que vous avez créés manuellement, pas seulement ceux issus de duplications précédentes

Si vous générez une feuille par région ou par mois, appuyez-vous plutôt sur la surcharge avec nom explicite. Un schéma prévisible Region-North, Region-South est plus facile à adresser plus tard qu'une suite de suffixes (2), (3) et il garde lisibles vos noms définis et vos formules inter-feuilles

Ce que CopyFrom copie en profondeur

Under the hood, Duplicate adds the sheet and then calls CopyFrom(ASource), which you can also call directly when you want to clone onto a sheet you have already created. CopyFrom guards against the two degenerate cases up front: copying from nil, or copying a sheet onto itself, both return immediately and do nothing. Everything after that is the copy itself, and it is deliberately broad

The cell data comes first. CopyFrom asks the source for its UsedRange, the tight bounding box of populated cells and merged regions, and reuses the existing CopyRangeTo machinery to carry every value, formula, and per-cell style index into the target starting at A1. On top of the cells, it replays the full layer of sheet-level state that makes a template look finished:

  • Merged ranges, re-created by coordinate so the banner spans the same rectangle
  • Column widths and row heights, plus the hidden, collapsed, and outline-level lists, copied verbatim so non-default rows and columns line up exactly
  • Freeze panes and the view state: zoom level, gridline and zero-value display, right-to-left direction, and the view type
  • Protection state with its per-action permission options, so a locked template stays locked the same way
  • The entire page-setup block: margins, orientation, paper size, scaling and fit-to-page, print area, print titles, headers and footers, and the print-gridlines and print-headings flags
  • The AutoFilter range, the tab colour, and the sheet's visibility

The result is a sheet that prints, filters, and presents identically to its source. And because the cells, merges, and dimension lists are physically re-created on the new sheet rather than aliased, the duplicate is fully independent. Write 999 into a cell on the copy and the source keeps its original value; that independence is the single most important property of a clone meant for parallel regional reports, and the shipped SheetCopy demo asserts it explicitly

Ce qu’il laisse superficiel, et pourquoi

Now the honest part. Charts, embedded images, XLSX tables, data validations, and conditional-formatting rules are not copied. This is a documented, deliberate boundary, not an oversight, and it is worth understanding the reasoning so you can plan around it rather than be surprised by it

Each of those collections carries identity and references that do not survive a naive field copy. A chart points at a source data range and owns a drawing relationship in the OOXML package; cloning the object without remapping the relationship and the series references produces a chart that renders against the wrong data, or a package that Excel flags as needing repair. A table has a name that must be unique within the workbook, a header row tied to specific columns, and its own auto-generated relationship. Conditional formats and data validations attach to coordinate ranges and, in the validation case, can reference other ranges by formula. Deep-copying any of these correctly means rewriting references and minting fresh identities, which is real work with real failure modes. Doing it halfway, by copying the object but not its references, is worse than not copying at all: it yields a file that opens with a repair prompt and silently drops content. So the engine copies the things it can copy cleanly and leaves the reference-bearing collections to the caller, who knows what the target should point at

In practice that means the workflow for a richer template is: duplicate the sheet to get the cells, layout, and print setup, then rebuild the chart, table, validations, or conditional formats on the copy with the same API you used to create them the first time. Because you are recreating them against the duplicate's'own ranges, the references come out correct by construction. For a chart that reads A1:C10, add a new chart on the copy pointing at the copy's A1:C10; for an AutoFilter you want live, note that the filter range does carry over, so you only re-apply the column criteria. The conditional-formatting and data-validation rules you would re-add through the same calls described in the article on merged cells and report-template layout, which walks the merge table and range model the copy inherits

Où la duplication s’insère dans une chaîne de reporting

Worksheet duplication is the natural companion to placeholder-driven generation. The token-anchored approach in the guide to template-driven report generation in Delphi solves the problem of writing data into a layout other people edit; duplication solves the problem of needing that layout many times in one workbook. Combine them and the pattern is clean: keep one pristine Template sheet with its tokens, merges, and print setup, then for each region or period call Duplicate, fill the clone's tokens with that slice of data, and move on. The pristine template never gets mutated, so it stays a reliable source for the next clone, and every output sheet starts from a byte-for-byte identical layout

One sequencing note saves a class of confusion. Duplicate the sheet before you pour data into it, not after. A template should hold structure and formatting, not last quarter's numbers, and cloning an empty styled sheet means each duplicate starts clean. If you duplicate a sheet that already carries data, that data comes along, because CopyFrom copie fidèlement la plage utilisée; c'est parfois ce que vous voulez, mais pour un rapport de diffusion, ce n'est généralement pas le cas

Une habitude de vérification rapide

Parce que la séparation entre copie profonde et copie superficielle est invisible tant que vous ne la cherchez pas, intégrez un contrôle en cinq lignes dans le travail plutôt que de faire confiance au fait que tout est bien passé. Après la duplication, relisez les signaux structurels que la copie est censée hériter et vérifiez qu'ils correspondent à la source

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.

Le nombre de fusions, une largeur de colonne, la ligne de figement et l'indicateur de couleur de l'onglet vous disent que la couche qui est copiée a bien été copiée. Par ailleurs, dans toute feuille qui portait un graphique, une table, des validations ou des mises en forme conditionnelles, traitez cela comme une liste à reconstruire sur la copie: leur absence est voulue, et la correction tient à quelques appels, pas à un rapport de bug. Ce modèle mental, en profondeur là où c'est sûr et en surface là où des références casseraient, résume entièrement la bonne façon d'utiliser cette fonctionnalité

La duplication de feuille et la CopyFrom copie de l'état de feuille décrite ici sont livrées en v2.91.0 du composant feuille de calcul Delphi natif HotXLS Delphi spreadsheet component, avec un exemple exécutable SheetCopy qui illustre le cycle de clonage et de mutation de bout en bout