Artículo técnico

Comentarios de celda e hipervínculos de Excel en Delphi con HotXLS

Un libro de conciliación sale del sistema financiero todas las noches con dos tipos de metadatos incorporados: comentarios de celda que explican por qué se ajustó una cifra e hipervínculos que apuntan a los revisores al registro fuente en la intranet. Después de un cambio rutinario de nombre de hoja, "Summary" pasó a llamarse "Overview", todos los saltos internos del libro dejaron de funcionar en silencio. Sin error al guardar, sin error al abrir; los vínculos simplemente ya no llevaban a ningún lado. Ese incidente es una lente útil para este tema, porque los comentarios y los hipervínculos son las dos funciones del libro que parecen cosméticas pero en realidad transportan estado de workflow. HotXLS da al código Delphi y C++Builder acceso directo de escritura a ambos, en XLS y XLSX, sin automatización de Excel, lo que también significa que su código, no Excel, es responsable de mantener válidos los destinos.

Comentarios de celda como registros de revisión escritos por máquina

En el modelo de clases XLSX, un comentario es un objeto de nivel hoja con fila, columna, autor y cuerpo de texto. El campo de autor no es decoración: cuando un libro generado circula por una cadena de revisión, "quién dijo esto" es lo primero que filtra un auditor, así que las notas generadas deben llevar una identidad de servicio en lugar de quedar vacías por defecto.

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;

El patrón FindAt importa más de lo que parece. Llamar AddComment dos veces en la misma celda es el bug clásico en trabajos batch propensos a reintentos; siempre consulten primero con FindAt y actualicen el objeto existente. La colección Comments también expone DeleteAt y DeleteInRange, y la variante por rango es la herramienta correcta para una pasada de sanitización: quitar anotaciones internas de QA de una región antes de enviar el libro fuera de la organización es una llamada, no un bucle.

URLs externos y saltos dentro del libro usan APIs distintas

OOXML almacena los dos tipos de vínculo de forma diferente: un URL externo se convierte en una entrada de relación en la parte .rels de la hoja, mientras que un salto interno es una cadena de ubicación simple como Summary!A1 almacenada en el propio vínculo. HotXLS refleja esa separación con dos métodos en vez de sobrecargar uno:

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');

En el objeto TXLSXHyperlink resultante, Url y Location son mutuamente excluyentes, y la bandera IsInternal indica cuál está poblado. Eso resulta útil cuando inventarían vínculos en un libro abierto y necesitan aplicar políticas distintas a "sale del archivo" frente a "permanece en el archivo". Como los vínculos internos nunca tocan las partes de relación, también son más baratos de reescribir en masa.

El modo de falla de la introducción vive por completo del lado interno. Una cadena de ubicación no es una referencia analizada; HotXLS escribe exactamente lo que ustedes pasan, y nada la redirige si luego se cambia el nombre de la hoja. En la práctica funcionan dos defensas. O renombran las hojas antes de generar cualquier vínculo y tratan los nombres de hoja como identificadores congelados desde ese punto, o apuntan a un nombre definido de nivel libro en lugar de a una dirección Sheet!Cell cruda: los nombres sobreviven a cambios de nombre porque Excel actualiza sus definiciones. El segundo enfoque combina bien con las técnicas de nombres definidos y fórmulas entre hojas en HotXLS.

El lado XLS: mismos conceptos, plomería más antigua

La fachada BIFF8 adjunta comentarios a rangos en lugar de hacerlo a una colección de hoja. IXLSRange.AddComment devuelve un TXLSComment, la propiedad Comment lee uno existente, y ClearComments los elimina. Hay un borde filoso: el objeto de comentario no expone públicamente su propia fila y columna, así que el código que recorre "todos los comentarios con sus posiciones" debe navegar desde rangos hacia comentarios, no al revés. Diseñen el bucle de auditoría alrededor de las celdas que anotaron, o mantengan su propio registro de posiciones al escribir.

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;

Establecer Visible en un comentario es el truco heredado para hacer que una nota sea imposible de ignorar: el cuadro amarillo se renderiza permanentemente en lugar de esperar al hover. TXLSComment también expone TextRuns, de modo que una nota puede mezclar advertencias en negrita con explicación normal, algo que la API de comentarios XLSX no ofrece de la misma manera. Los hipervínculos de este lado vienen en tres overloads progresivos (solo dirección, más texto visible, más screen tip) y se enumeran mediante la colección HyperLinks de la hoja, con Address, SubAddress, DisplayText y ScreenTip legibles por vínculo.

Una hoja índice de revisión supera a las notas dispersas

Cuando un libro lleva más de una docena de anotaciones, leer por hover deja de escalar y los revisores empiezan a perder notas en hojas que nunca visitan. El patrón que mejor se ha sostenido en producción es un índice generado: una hoja dedicada que lista cada ubicación anotada con su nombre de hoja, dirección de celda, autor y un extracto del texto de la nota, y en la última columna, un hipervínculo interno creado con AddHyperlinkToCell que salta directamente a la celda anotada. El revisor recorre el índice de arriba abajo en vez de buscar por la cuadrícula, y el número de filas de ese índice también sirve como inventario de comentarios para la auditoría posterior.

Construirlo cuesta poco porque el generador ya conoce cada posición que anotó. Agreguen cada tupla (hoja, fila, columna, autor, resumen) a una lista mientras escriben comentarios, y emitan la hoja índice al final para que su propio conteo de filas ya sea definitivo. Dos refinamientos prácticos: ordenar el índice por severidad o por hoja en lugar de por orden de inserción, y dar a la hoja índice un vínculo de retorno en su encabezado para que los usuarios puedan volver después de cada elemento. Como los vínculos internos son cadenas de ubicación simples sin partes de relación detrás, incluso un índice de mil filas casi no agrega tamaño de archivo ni tiempo de guardado.

El mismo índice abarata el viaje de vuelta posterior a la revisión: cuando regresa el libro, su código lee valores de estado escritos junto a las filas del índice en lugar de volver a escanear cada hoja en busca de comentarios modificados. Las celdas estructuradas son mucho más confiables de analizar que las notas de texto libre.

Una auditoría previa a la entrega que sí detecta la rotura

Ninguna de estas APIs valida destinos: un vínculo a una hoja eliminada, un host de intranet mal escrito o un recurso compartido retirado el trimestre pasado se guardan sin quejas. ECMA-376 solo especifica cómo se almacenan los vínculos, no que resuelvan. Por eso un libro que transporta metadatos de revisión merece una breve etapa de auditoría antes de SaveAs:

  • Recolectar cada ubicación interna escrita durante la generación y verificar que el nombre de hoja antes del signo de exclamación aún exista en la colección de hojas del libro.
  • Revisar URLs externos contra una allowlist de esquemas y hosts; file:// y las rutas UNC desnudas filtran detalles de entorno y se rompen fuera de la red.
  • Contar comentarios por hoja y compararlos contra el conteo que el generador pretendía escribir; un reintento que duplicó las notas aparece de inmediato.
  • Quitar anotaciones solo internas con DeleteInRange cuando el destinatario sea externo.

Los equipos que ya generan sus libros desde una capa de datos pueden incorporar esto en la misma etapa del pipeline que valida los datos; el patrón es el mismo que se describe en exportar resultados de consultas de base de datos a reportes de Excel, solo aplicado a metadatos en vez de filas.

FAQ: comentarios e hipervínculos

¿HotXLS verifica que exista el destino de un hipervínculo? No, por diseño. La biblioteca escribe las estructuras; la alcanzabilidad es responsabilidad de la aplicación. Validen URLs y ubicaciones internas en su propio código antes de guardar.

¿Cómo enlazo a una hoja cuyo nombre contiene espacios? Pongan el nombre de la hoja entre comillas simples dentro de la cadena de ubicación, exactamente como lo hace Excel en fórmulas: 'Quarterly Totals'!A1. Las reglas de comillas son las mismas que acepta el motor de fórmulas para referencias entre hojas.

¿Puedo hacer visible un comentario sin que el usuario pase el mouse? En XLS, sí: establezcan Visible := True en el TXLSComment. En XLSX el comentario se renderiza con hover; si la nota debe ser imposible de pasar por alto, pónganla en una celda o en un cuadro de texto.

Los comentarios y los vínculos son la parte de un libro que los usuarios juzgan por confianza más que por apariencia, así que el estándar de ingeniería es "nunca equivocado" y no "normalmente bien". Toda la superficie de API para ambas fachadas está documentada en la página de producto HotXLS Component.