Article technique

Commentaires de cellules et hyperliens Excel en Delphi avec HotXLS

Chaque nuit, un classeur de rapprochement sort du système financier avec deux types de métadonnées intégrés : des commentaires de cellules qui expliquent pourquoi un montant a été ajusté, et des hyperliens qui orientent les réviseurs vers l’enregistrement source dans l’intranet. Après un renommage ordinaire de feuille — "Summary" est devenu "Overview" — tous les sauts internes du classeur ont cessé de fonctionner sans bruit. Aucune erreur à l’enregistrement, aucune erreur à l’ouverture ; les liens ne menaient simplement plus nulle part. Cet incident est un bon angle pour aborder le sujet, car commentaires et hyperliens sont deux fonctions de classeur qui paraissent cosmétiques mais portent en réalité l’état du workflow. HotXLS donne au code Delphi et C++Builder un accès direct en écriture aux deux, en XLS comme en XLSX, sans automatisation Excel — ce qui signifie aussi que votre code, et non Excel, devient responsable de la validité des cibles.

Commentaires de cellules comme traces de revue écrites par machine

Dans le modèle de classes XLSX, un commentaire est un objet au niveau de la feuille, avec une ligne, une colonne, un auteur et un corps de texte. Le champ auteur n’est pas décoratif : lorsqu’un classeur généré circule dans une chaîne de revue, "qui a dit cela" est la première information qu’un auditeur filtre ; les notes générées doivent donc porter une identité de service au lieu de rester vides par défaut.

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Note: TXLSXComment;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('reconciliation.xlsx');
    Sheet := Book.Sheets[0];

    // Authored note on the adjusted figure
    Sheet.AddComment(14, 4, 'Manual adjustment: late FX rate, see ticket FIN-2214',
      'recon-service');

    // Update an existing note instead of stacking a second one
    Note := Sheet.Comments.FindAt(14, 4);
    if Note <> nil then
      Note.Text := Note.Text + ' [verified 2026-06-11]';

    Book.SaveAs('reconciliation-reviewed.xlsx');
  finally
    Book.Free;
  end;
end;

Le motif FindAt compte davantage qu’il n’y paraît. Appeler AddComment deux fois sur la même cellule est le bogue classique des traitements batch soumis aux relances ; interrogez toujours avec FindAt d’abord, puis mettez à jour l’objet existant. La collection Comments expose aussi DeleteAt et DeleteInRange, et la variante par plage est l’outil adapté à une passe de nettoyage : supprimer les annotations QA internes d’une zone avant d’envoyer le classeur hors de l’organisation tient en un appel, pas en une boucle.

Les URL externes et les sauts internes utilisent des API différentes

OOXML stocke les deux types de liens différemment : une URL externe devient une entrée de relation dans la partie .rels de la feuille, tandis qu’un saut interne est une simple chaîne de localisation comme Summary!A1 portée par le lien lui-même. HotXLS reflète cette séparation avec deux méthodes au lieu d’en surcharger une seule :

Sheet.Cells[2, 1].Value := 'Source record';
Sheet.AddHyperlink(2, 1, 'https://intranet.example.com/records/2214',
  'Open record 2214', 'ERP source entry');

Sheet.Cells[3, 1].Value := 'Totals';
Sheet.AddHyperlinkToCell(3, 1, 'Overview!B12', 'Jump to totals');

Sur l’objet TXLSXHyperlink obtenu, Url et Location sont mutuellement exclusifs, et l’indicateur IsInternal vous dit lequel est renseigné — utile lorsque vous inventoriez les liens d’un classeur ouvert et devez appliquer des règles différentes à ce qui "quitte le fichier" et à ce qui "reste dans le fichier". Comme les liens internes ne touchent jamais aux parties de relations, ils sont aussi moins coûteux à réécrire en masse.

Le mode d’échec de l’introduction se situe entièrement du côté interne. Une chaîne de localisation n’est pas une référence analysée ; HotXLS écrit exactement ce que vous fournissez, et rien ne la recible lorsqu’une feuille est renommée ensuite. Deux défenses fonctionnent en pratique. Soit vous renommez les feuilles avant de générer le moindre lien et vous traitez leurs noms comme des identifiants figés à partir de ce moment, soit vous ciblez un nom défini au niveau du classeur plutôt qu’une adresse brute Sheet!Cell — les noms survivent aux renommages parce qu’Excel met à jour leurs définitions. La seconde approche s’accorde bien avec les techniques décrites dans les noms définis et les formules inter-feuilles dans HotXLS.

Côté XLS : mêmes concepts, tuyauterie plus ancienne

La façade BIFF8 attache les commentaires aux plages plutôt qu’à une collection de feuille. IXLSRange.AddComment renvoie un TXLSComment, la propriété Comment relit celui qui existe déjà, et ClearComments les supprime. Une arête vive : l’objet commentaire n’expose pas publiquement sa propre ligne ni sa propre colonne ; le code qui parcourt "tous les commentaires avec leur position" doit donc naviguer des plages vers les commentaires, et non l’inverse. Concevez votre boucle d’audit autour des cellules que vous avez annotées, ou conservez votre propre journal des positions pendant l’écriture.

var
  Book: IXLSWorkbook;
  Sheet: IXLSWorksheet;
  Remark: TXLSComment;
begin
  Book := TXLSWorkbook.Create;
  Sheet := Book.Sheets.Add;
  Sheet.Name := 'Review';
  Sheet.Cells.Item[5, 2].Value := 4821.50;

  Remark := Sheet.Cells.Item[5, 2].AddComment('Awaiting sign-off from controller');
  Remark.Visible := True;   // pop the note open on first view

  Sheet.AddHyperlink(7, 2, 'https://intranet.example.com/signoff/4821',
    'Sign-off form', 'Opens the controller queue');
  Book.SaveAs('review.xls');
end;

Définir Visible sur un commentaire est l’ancienne astuce pour rendre une note impossible à manquer : la boîte jaune reste affichée au lieu d’attendre le survol. TXLSComment expose aussi TextRuns, ce qui permet de mêler avertissements en gras et explication ordinaire dans une note, chose que l’API de commentaires XLSX n’offre pas de la même manière. De ce côté, les hyperliens existent en trois surcharges progressives (adresse seule, puis texte affiché, puis info-bulle) et sont énumérables via la collection HyperLinks de la feuille, avec Address, SubAddress, DisplayText et ScreenTip lisibles pour chaque lien.

Une feuille d’index de revue vaut mieux que des notes dispersées

Dès qu’un classeur porte plus d’une douzaine d’annotations, la lecture au survol cesse de monter en charge et les réviseurs commencent à manquer les notes placées sur des feuilles qu’ils n’ouvrent jamais. Le motif qui résiste le mieux en production est un index généré : une feuille dédiée qui liste chaque emplacement annoté avec son nom de feuille, son adresse de cellule, son auteur et un extrait du texte de la note — et, dans la dernière colonne, un hyperlien interne construit avec AddHyperlinkToCell qui saute directement vers la cellule annotée. Le réviseur descend l’index du haut vers le bas au lieu de fouiller la grille, et le nombre de lignes de cet index devient aussi votre inventaire de commentaires pour la passe d’audit ci-dessous.

Le coût de construction est faible, car votre générateur connaît déjà chaque position qu’il a annotée. Ajoutez chaque tuple (feuille, ligne, colonne, auteur, résumé) à une liste pendant l’écriture des commentaires, puis émettez la feuille d’index en dernier afin que son propre nombre de lignes soit définitif. Deux raffinements pratiques : triez l’index par gravité ou par feuille plutôt que par ordre d’insertion, et donnez à la feuille d’index un lien de retour dans son en-tête pour que les utilisateurs puissent revenir après chaque élément. Comme les liens internes sont de simples chaînes de localisation sans parties de relation derrière elles, même un index de mille lignes n’ajoute presque rien à la taille du fichier ni au temps d’enregistrement.

Le même index rend le retour post-revue moins coûteux dans l’autre sens : lorsque le classeur revient, votre code lit les valeurs de statut saisies à côté des lignes d’index au lieu de rescanner chaque feuille pour trouver les commentaires modifiés — des cellules structurées se parsant bien plus sûrement que des notes en texte libre.

Une passe d’audit avant livraison qui détecte vraiment la casse

Aucune de ces API ne valide les cibles — un lien vers une feuille supprimée, un hôte intranet mal orthographié ou un partage de fichiers retiré le trimestre dernier s’enregistre sans plainte. ECMA-376 spécifie seulement comment les liens sont stockés, pas qu’ils se résolvent. Un classeur qui transporte des métadonnées de revue mérite donc une courte étape d’audit avant SaveAs :

  • Collectez chaque localisation interne écrite pendant la génération et vérifiez que le nom de feuille avant le point d’exclamation existe encore dans la collection de feuilles du classeur.
  • Contrôlez les URL externes avec une liste autorisée de schémas et d’hôtes ; file:// et les chemins UNC nus divulguent des détails d’environnement et cassent hors réseau.
  • Comptez les commentaires par feuille et comparez avec le nombre que votre générateur devait produire — une relance qui a doublé les notes apparaît immédiatement.
  • Supprimez les annotations internes avec DeleteInRange lorsque le destinataire est externe.

Les équipes qui génèrent déjà leurs classeurs depuis une couche de données peuvent intégrer cela à la même étape de pipeline qui valide les données elles-mêmes ; le motif est celui décrit dans l’export des résultats de requêtes de base de données vers des rapports Excel, appliqué aux métadonnées plutôt qu’aux lignes.

FAQ : commentaires et hyperliens

HotXLS vérifie-t-il qu’une cible d’hyperlien existe ? Non, par conception. La bibliothèque écrit les structures ; l’accessibilité relève de l’application. Validez les URL et les localisations internes dans votre propre code avant l’enregistrement.

Comment créer un lien vers une feuille dont le nom contient des espaces ? Encadrez le nom de feuille dans la chaîne de localisation, exactement comme Excel le fait dans les formules : 'Quarterly Totals'!A1. Les règles de guillemetage sont les mêmes que celles acceptées par le moteur de formules pour les références inter-feuilles.

Puis-je rendre un commentaire visible sans attendre le survol de l’utilisateur ? En XLS, oui — définissez Visible := True sur le TXLSComment. En XLSX, le commentaire s’affiche au survol ; si la note doit être impossible à manquer, placez-la plutôt dans une cellule ou une zone de texte.

Les commentaires et les liens sont la partie d’un classeur que vos utilisateurs jugent sur la confiance plutôt que sur l’apparence ; le niveau d’exigence d’ingénierie est donc "jamais faux" plutôt que "généralement correct". La surface API complète des deux façades est documentée sur la page produit HotXLS Component.