Artículo técnico

Comentarios en hilo en archivos de Excel con Delphi y HotXLS

Genere un libro de trabajo desde Delphi, anote una celda con la API clásica de comentarios y Excel 365 archivará su texto bajo Notas: un cuadro amarillo de estilo antiguo sin campo Responder, sin avatar de autor y sin botón de resolución. HotXLS escribe comentarios en hilo genuinos de Excel 365 a través de AddThreadedComment y AddThreadedReply en el motor XLSX, emitiendo la parte dedicada xl/threadedComments/threadedCommentN.xml por hoja y registrando a cada autor en la sección person a nivel de libro de trabajo, de modo que la conversación que escribe su código es el mismo objeto al que responde un revisor en Excel

La distinción es importante porque ambas características solo se parecen en la superficie. Una nota es un cuadro de texto flotante; un comentario en hilo es un registro de conversación con identificadores estables, un árbol de respuestas, un registro de autores y un indicador de resuelto: los elementos sobre los que realmente se ejecuta un flujo de trabajo de revisión. Este artículo recorre primero la estructura OOXML, porque una vez que se observan ambos modelos de almacenamiento frente a frente, cualquier diferencia de comportamiento en Excel deja de ser un misterio

¿Por qué mis comentarios se muestran como notas en Excel 365?

La respuesta corta: dos generaciones de comentarios comparten una misma cuadrícula y residen en partes del paquete completamente distintas. El modelo heredado (al que apunta AddComment) almacena el texto en xl/comments1.xml (tipo de contenido application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml) y posiciona el cuadro emergente a través de una parte de dibujo VML complementaria, xl/drawings/vmlDrawing1.vml. Ese par es anterior a la cinta de opciones; Excel 365 lo sigue leyendo fielmente, pero renderiza el resultado como una "nota" y no ofrece una interfaz de usuario para hilos sobre él, porque no hay nada en el almacenamiento que se pueda organizar en hilos. El autor es una cadena de visualización, la posición es una forma VML y ese es todo el modelo

Los comentarios en hilo, introducidos con Excel 365, omiten la capa VML por completo. Cada hoja que contiene una conversación lleva su propia parte xl/threadedComments/threadedCommentN.xml bajo el tipo de contenido application/vnd.openxmlformats-officedocument.spreadsheetml.threadedComments+xml, y el libro de trabajo contiene una única parte xl/person/person.xml (tipo de contenido ...spreadsheetml.people+xml) que enumera a cada participante una vez. HotXLS expone ambos modelos en la misma hoja de cálculo: AddComment sigue escribiendo el par heredado, mientras que AddThreadedComment escribe las partes nuevas. Si el resultado se muestra en forma de notas, significa que invocó la primera API cuando deseaba utilizar la segunda. El modelo heredado sigue siendo la herramienta adecuada para ciertas tareas (el artículo anterior sobre comentarios de celdas e hipervínculos en flujos de trabajo de revisión lo cubre en profundidad, incluyendo el lado de XLS), pero nada en él se actualiza a un hilo

Qué escribe HotXLS en el paquete

HotXLS modela cada respuesta como un objeto TXLSXThreadedComment que contiene un anclaje de celda (Ref), un identificador de tipo GUID (Id), un ParentId opcional, un AuthorId que se resuelve a través de la lista de personas, un espejo de DisplayName, el cuerpo del texto, un indicador de resuelto Done y una marca de fecha y hora ISO-8601 DateTime. Al guardar, cada entrada se convierte en un elemento <threadedComment> cuyo atributo t contiene el identificador, su atributo dT contiene la marca de tiempo y su atributo parentT (presente solo en respuestas hijas) nombra al identificador del padre. Excel reconstruye el árbol de la conversación asociando parentT con t; no hay anidamiento en el XML, solo una lista plana más referencias

La parte de personas es la pieza que la mayoría de las implementaciones manuales olvidan. Cada authorId en un comentario en hilo debe resolverse en una entrada <person> en xl/person/person.xml, vinculada con las relaciones del libro de trabajo y con su propia anulación de tipo de contenido; de lo contrario, Excel no dispondrá de ningún nombre de visualización para mostrar. HotXLS gestiona el registro automáticamente: AddThreadedComment busca al autor en la lista Persons del libro de trabajo por su nombre de visualización y crea una entrada con un GUID nuevo cuando no existe ninguna, de modo que los comentarios consecutivos de un mismo autor compartan una sola identidad. Las relaciones de la hoja de cálculo obtienen la relación threadedComments, las del libro de trabajo obtienen la de personas y el manifiesto de tipos de contenido obtiene ambas anulaciones, sin que nada de esto tenga que figurar en su código

Construir una cadena de respuestas con AddThreadedComment y AddThreadedReply

AddThreadedComment crea la raíz de la conversación: su ParentId permanece vacío, que es lo que la marca como el inicio del hilo. AddThreadedReply toma un parámetro ParentRef (la referencia de celda donde reside la raíz), localiza la raíz en ese anclaje y graba el ParentId de la nueva respuesta con el Id de la raíz. Ambos devuelven el nuevo TXLSXThreadedComment, por lo que puede establecer la marca de tiempo o el estado de resuelto en el objeto que acaba de crear:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Root: TXLSXThreadedComment;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('forecast.xlsx');
    Sheet := Book.Sheets[0];

    // Raíz de la conversación; ParentId permanece vacío
    Root := Sheet.AddThreadedComment(8, 3,
      'Q3 figure looks low against the pipeline export', 'Maria Ortiz');
    Root.DateTime := '2026-07-06T09:12:00Z';

    // Las respuestas se anclan en la misma celda; ParentRef indica la celda raíz
    Sheet.AddThreadedReply(8, 3, Root.Ref,
      'Pipeline export missed the EMEA renewals, re-running', 'Jan Kowalski');
    Sheet.AddThreadedReply(8, 3, Root.Ref,
      'Confirmed, refreshed figure lands tomorrow', 'Maria Ortiz');

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

Conviene prestar atención a dos detalles de ese listado. En primer lugar, se pasa Root.Ref en lugar de una cadena construida a mano: la referencia que generó la biblioteca tiene la garantía de coincidir con lo que buscará AddThreadedReply, lo que elimina una categoría entera de errores de anclaje por diferencia de uno. En segundo lugar, ambos autores se pasaron como nombres de visualización simples: el registro de personas, las identidades de GUID y la conexión de authorId sucedieron en segundo plano tras las llamadas. Abra el archivo guardado en Excel 365 y el anclaje de celdas, el orden de las respuestas y los dos participantes distintos estarán activos: haga clic en Responder y Excel lo añadirá al mismo hilo que inició su código

Leer una conversación de vuelta: comportamiento de viaje de ida y vuelta

HotXLS realiza el viaje de ida y vuelta de comentarios en hilo a partir de la versión 2.122.0: reabrir un paquete guardado reconstruye la lista de conversaciones de cada hoja y la lista de participantes del libro de trabajo a partir de las secciones, por lo que los identificadores, enlaces de respuesta y nombres de visualización sobreviven a un ciclo completo de lectura→escritura. El lector analiza person.xml antes que cualquier contenido de hoja, lo que permite que cada authorId se resuelva en un nombre de visualización en el momento de la carga; los identificadores de personas analizados se conservan tal cual en lugar de regenerarse, por lo que un libro de trabajo que alterna entre su código y Excel mantiene identidades de autor estables en cualquier cantidad de viajes de ida y vuelta

var
  i: Integer;
  Cmt: TXLSXThreadedComment;
begin
  Book.Open('forecast-reviewed.xlsx');
  Sheet := Book.Sheets[0];
  for i := 0 to Sheet.ThreadedComments.Count - 1 do
  begin
    Cmt := Sheet.ThreadedComments[i];
    if Cmt.ParentId = '' then
      Writeln('Raíz en ', Cmt.Ref, ' de ', Cmt.DisplayName, ': ', Cmt.Text)
    else
      Writeln('  respuesta de ', Cmt.DisplayName, ': ', Cmt.Text);
  end;
  Writeln('Participantes: ', Book.Persons.Count);
end;

El almacenamiento en forma de lista plana más referencias se muestra aquí deliberadamente: ThreadedComments enumera las respuestas en el orden de las partes, y un ParentId vacío identifica a cada raíz. Si necesita la estructura de árbol (por ejemplo, para exportar un registro de revisión), agrupe primero por Ref y luego encadene ParentId con Id dentro del grupo de cada celda. Este comportamiento de conservar lo analizado es un ejemplo de una política más amplia en la biblioteca; el artículo sobre el viaje de ida y vuelta sin pérdidas de temas, listas de extensiones y la cadena de cálculo cubre cómo se aplica este mismo principio al resto del paquete

Estado de resuelto, marcas de tiempo e identidad de autor

Tres atributos portan la semántica de la revisión y los tres se pueden escribir en los objetos devueltos. Done se asocia con el atributo done: Excel muestra un hilo resuelto colapsado con un enlace Volver a abrir. DateTime es la marca de creación UTC ISO-8601 (dT); HotXLS coloca un marcador de posición fijo cuando se deja vacío para que la sección siempre se valide, pero una marca de tiempo real es lo que hace legible el historial del hilo, por lo que conviene establecerla. En el aspecto de la identidad, cada TXLSXPerson expone ProviderId (típicamente None para entradas creadas localmente) y un UserId que puede rellenar con un UPN o correo electrónico cuando su aplicación disponga de él:

var
  Root: TXLSXThreadedComment;
  Person: TXLSXPerson;
begin
  Root := Sheet.ThreadedComments.FindAt('D9');
  if Root <> nil then
    Root.Done := True;   // guardado como done="1"; Excel muestra el hilo resuelto

  Person := Book.Persons.FindByDisplayName('Maria Ortiz');
  if Person <> nil then
    Person.UserId := 'maria.ortiz@example.com';
end;

Un límite que cabe mencionar claramente: HotXLS escribe el elemento <mentions> vacío. Los registros de menciones con @ (el mecanismo que resalta el nombre de un compañero dentro del texto del comentario y activa una notificación en Microsoft 365) no están modelados, por lo que el texto que contiene un símbolo @ se almacena como texto plano. Para un libro de trabajo de revisión generado, esto rara vez supone una pérdida, pero si su flujo de trabajo depende de las notificaciones de mención, prevea que un humano las añada dentro de Excel. La identidad del autor en la sección person se asocia de forma natural con los campos de procedencia a nivel de libro de trabajo cubiertos en metadatos del libro de trabajo y propiedades del documento, que es donde reside el resto del registro de auditoría de un archivo generado

¿Qué hacen las versiones antiguas de Excel con los comentarios en hilo?

Excel 2016 y versiones anteriores son previos al modelo en hilo y simplemente omiten las partes que no comprenden. Cuando el propio Excel 365 guarda una conversación en hilo, también escribe una sombra de comentario heredado (una nota de marcador de posición que dice "[Threaded comment]...") precisamente para que las versiones anteriores muestren algo en la celda anclada. HotXLS emite únicamente las partes en hilo, sin sombra heredada, por lo que un archivo escrito por su código no muestra ningún indicador de comentario cuando se abre en Excel 2016. Si su audiencia incluye instalaciones anteriores a Office 365 y la anotación debe ser visible allí, la API heredada AddComment sigue siendo el canal compatible; evite superponer ambos modelos en la misma celda sin realizar pruebas en cada versión de Excel a la que distribuya, porque la manera en que un lector concilia una nota y un hilo en una celda es decisión del lector, no suya

El otro límite estricto es el propio formato del archivo. Los comentarios en hilo son una estructura OOXML sin contrapartida en BIFF8, por lo que el lado de .xls de HotXLS no dispone de API en hilo: un libro de trabajo que deba permanecer en el formato binario heredado se limita a las notas clásicas. En la práctica, la tabla de decisiones es corta: si escribe .xlsx para Excel 365 o lectores de Excel web/móvil, utilice AddThreadedComment y obtenga conversaciones reales; si apunta a Excel 2016 o .xls, utilice AddComment y acepte el modelo de nota; si audita un archivo que ha recibido, compruebe tanto ThreadedComments como Comments, porque un libro de trabajo que ha vivido en ambos mundos puede contener legítimamente ambos. Todo el abanico de la API para ambas generaciones está documentado en la página del producto Componente de Hoja de Cálculo Delphi HotXLS