Le rapport de bug disait que le titre de la facture avait disparu. Le générateur avait fusionné A1:F1 correctement, l'avait stylé correctement — puis avait écrit le titre en B1. Dans XLS comme dans XLSX, une plage fusionnée affiche le contenu d'une seule cellule : l'ancre en haut à gauche. Ce qui se retrouve dans les cellules couvertes ne se rend pas, et le contenu écrit là avant la fusion cesse d'être visible au moment où la fusion est appliquée. Les utilisateurs d'Excel l'apprennent par essais et erreurs ; les générateurs de rapports doivent l'apprendre comme une règle, car dans du code généré le symptôme est une région vide sans erreur nulle part. HotXLS — une bibliothèque native Object Pascal qui lit et écrit les deux formats Excel depuis Delphi et C++Builder — modélise les fusions assez explicitement pour bâtir sur la règle au lieu de trébucher dessus.
Une valeur, une ancre
Pensez à une fusion comme à une instruction d'affichage posée par-dessus une grille inchangée. Les cellules existent toujours individuellement dans le fichier ; l'enregistrement de fusion dit aux consommateurs de rendre le contenu de l'ancre sur tout le rectangle. Trois conséquences pratiques suivent. Lire une cellule couverte autre que l'ancre ne donne rien d'utile, donc le code qui inspecte un titre fusionné doit adresser l'ancre. Écrire ailleurs que dans l'ancre produit du contenu invisible — le « titre disparu » ci-dessus. Et défusionner fait réapparaître ce qui était stocké dessous, raison pour laquelle des valeurs parasites sous des régions de mise en page finissent par devenir une surprise visible chez le client.
Côté XLSX, HotXLS expose directement la table des fusions : Sheet.MergedCells est une collection avec Add('A1:C1'), FindAt(Row, Col), DeleteAt et Items, et FindAt répond à la question essentielle — étant donnée une cellule arbitraire, quelle région fusionnée, s'il y en a une, la couvre ? Cette seule recherche alimente à la fois les lectures sûres (trouver la région, puis lire son ancre) et le garde de région de données montré plus bas.
Les appels de fusion sur chaque façade
La façade XLS suit l'idiome COM d'Excel : les plages viennent d'une propriété indexée à deux arguments, et Merge prend un OleVariant dont la valeur change la géométrie obtenue.
var
Book: IXLSWorkbook; // interface-counted: no manual Free
Sh: IXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
Sh := Book.Sheets[1]; // XLS sheet collection is 1-based
Sh.Range['A1', 'F1'].Merge(False); // False = one merged block
Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
Sh.Range['A3', 'F4'].Merge(True); // True = merge across: one merge per row
Book.SaveAs('layout.xls');
end;
La distinction True/False compte pour les bandes d'en-tête : Merge(True) sur une plage de deux lignes produit deux fusions indépendantes d'une ligne (le « Merge Across » d'Excel), tandis que Merge(False) produit un seul rectangle. La plage expose aussi MergeCells comme indicateur d'état lisible, MergeArea pour retrouver la région contenante, et Unmerge pour la dissoudre. Côté XLSX, les mêmes opérations existent sous forme de Sheet.MergeCells(Row1, Col1, Row2, Col2), TXLSXRange.Merge avec une variante Across équivalente, et la collection MergedCells.
Un modèle qui grandit : CopyRange plus InsertRows
Les vrais modèles de rapport ne sont pas statiques — une section de détail s'étend selon les données. Le modèle praticable garde une ligne stylée comme gabarit dans la mise en page, la clone pour chaque ligne de données, puis ouvre un espace avant le bloc de totaux afin que tout ce qui est ancré plus bas se décale intact :
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611'; // value goes to the anchor, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1; // pool index 0-based, cell side 1-based
// row 5 is the styled detail template line
for I := 0 to ItemCount - 1 do
Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1); // styles and formulas travel with it
// open a gap above the totals block; content below shifts down
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);
Deux lignes méritent un examen. L'affectation de police porte le décalage d'index de pool — Fonts.Add renvoie une position de pool 0-based tandis que les cellules stockent des références 1-based avec 0 signifiant police par défaut, donc omettre + 1 applique silencieusement la mauvaise police au titre. Et CopyRange copie formats et formules avec les valeurs, ce qui explique précisément pourquoi cloner une ligne de modèle stylée à la main vaut mieux que reconstruire son formatage dans le code : le concepteur du modèle possède l'apparence, le générateur possède les données.
Lorsque la bibliothèque de mise en page vit dans un classeur séparé — une feuille de bandes d'en-tête réutilisables, par exemple — CopyRangeTo étend la même opération entre feuilles, en prenant une feuille cible et des coordonnées de destination. Cela permet à un générateur de garder une feuille de modèle intacte et d'estampiller ses régions dans autant de feuilles de sortie que le rapport le demande, plutôt que de muter le modèle en place en espérant le restaurer.
Ce que InsertRows décale — et ce qu'il laisse derrière
XLSX InsertRows est une édition structurelle, pas un simple déplacement de cellules. Il relocalise les régions fusionnées, hauteurs de lignes, liens hypertexte, commentaires, volets figés, plages AutoFilter, formats conditionnels, validations de données, tables, noms définis, ancres d'images et ancres de graphiques avec les cellules. C'est cette ampleur qui rend sûr le modèle de gabarit qui grandit : le bloc de totaux situé sous le point d'insertion arrive à sa nouvelle ligne avec ses fusions et formats intacts.
Deux limites sont documentées et méritent d'être intégrées à la conception. L'ajustement des formules couvre les références vers la feuille en cours d'édition — une formule sur une autre feuille pointant dans la zone décalée est aussi réécrite, mais seules les références ciblant la même feuille participent ; auditez donc séparément les modèles de références interclasseur. Et côté XLS, les tableaux croisés survivent aux cycles ouverture-enregistrement comme enregistrements bruts préservés plutôt que comme objets modélisés, ce qui signifie que l'insertion de lignes ne relocalise pas l'empreinte d'un tableau croisé. Les modèles destinés au format XLS doivent garder les régions de tableaux croisés loin de toute bande qui grandit.
Protéger les régions de données contre les régions de mise en page
La panne classique de cellules fusionnées en production n'est pas visuelle — elle est structurelle : une ligne de données atterrit dans une bande de mise en page fusionnée, ses valeurs deviennent invisibles, et les totaux ne correspondent plus à la feuille visible. Comme MergedCells.FindAt peut interroger la table des fusions pour toute coordonnée, le générateur peut imposer la frontière au lieu d'espérer :
// refuse to write detail data into a merged layout region
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;
La même discipline s'applique aux fonctionnalités interactives : les plages que les utilisateurs trieront ou filtreront ne doivent contenir aucune fusion, car les cellules fusionnées dans une plage de tri provoquent des erreurs ou une mise en page mélangée dans Excel. Gardez les fusions dans les bandes de titre, séparateurs de section et blocs de signature ; gardez le milieu tabulaire de la feuille plat. L'article sur la génération de rapports depuis des modèles étend cette séparation mise en page contre données en un flux complet de placeholders, et l'article sur le formatage conditionnel et le texte riche couvre le stylage de la bande de données plate.
Comment les fusions survivent à l'export
La mise en page fusionnée est un concept de classeur, et chaque autre format de sortie la dégrade différemment — le savoir évite une série de surprises QA. L'export HTML rend fidèlement les fusions comme attributs colspan et rowspan sur une table unique, de sorte que les rapports destinés au navigateur conservent leur apparence en bandes. L'export RTF ne fusionne pas les colonnes : le texte de l'ancre atterrit dans sa propre colonne et le reste de la largeur fusionnée ressort comme cellules vides, donc les grands titres fusionnés semblent décalés à gauche dans les traitements de texte. CSV n'a aucune représentation des fusions — la valeur d'ancre occupe un champ et les cellules couvertes sortent comme champs vides. Si un classeur sert aussi de source à des flux délimités, concevez la région de données de façon qu'aucune information significative ne dépende de la géométrie de fusion ; l'article sur l'export CSV, TSV et HTML détaille le comportement de chaque format.
Questions de fusion issues du support
Comment lire la valeur d'une région fusionnée quand je n'ai qu'une cellule couverte ?
Résolvez d'abord la région, puis lisez son ancre. Sur XLSX, MergedCells.FindAt(Row, Col) renvoie la plage fusionnée couvrante ou nil ; sur XLS, Range.MergeArea remet la région complète pour toute cellule située dedans. Lire directement la coordonnée couverte renvoie un contenu vide même lorsque la région affiche visiblement du texte.
Pourquoi le tri a-t-il mélangé la mise en page de mon rapport ?
La plage de tri intersectait presque certainement une région fusionnée. Le tri réorganise les lignes indépendamment, et une fusion qui s'étend sur plusieurs lignes ne peut pas se déplacer avec une seule d'entre elles. Gardez les fusions hors des plages triables, ou défusionnez, triez puis refusionnez autour de l'opération.
Les cellules fusionnées ralentissent-elles les grands classeurs ?
Pas de façon significative à l'échelle d'un rapport — la table de fusions est petite par rapport aux données de cellules. Les préoccupations de performance des gros fichiers se trouvent ailleurs : croissance des pools de styles et mémoire du chemin d'enregistrement, couvertes dans l'article sur les performances des grands classeurs.
Les deux API de fusion, les opérations d'édition structurelle et les démos de modèles sont incluses avec HotXLS Component.