Wygeneruj skoroszyt z poziomu Delphi, dodaj adnotację do komórki za pomocą klasycznego API komentarzy, a program Excel 365 zapisze Twój tekst jako notatkę (Note): staromodne żółte pole bez pola odpowiedzi, bez awatara autora i bez przycisku rozwiązania wątku. Biblioteka HotXLS zapisuje prawdziwe wątkowane komentarze (threaded comments) programu Excel 365 za pomocą metod AddThreadedComment oraz AddThreadedReply w silniku XLSX. Tworzy ona dedykowany plik xl/threadedComments/threadedCommentN.xml dla każdego arkusza i rejestruje każdego autora w globalnej strukturze uczestników (person) skoroszytu, dzięki czemu konwersacja utworzona przez Twój kod staje się tym samym obiektem, na który recenzent odpowiada w programie Excel
Różnica ta jest kluczowa, ponieważ obie te funkcje są podobne tylko z wyglądu. Notatka to po prostu ruchome pole tekstowe; wątkowany komentarz to natomiast zapis konwersacji z trwałymi identyfikatorami, drzewem odpowiedzi, rejestrem autorów i flagą rozwiązania wątku — czyli elementami, na których opiera się rzeczywisty proces weryfikacji. W tym artykule najpierw przyjrzymy się strukturze OOXML, ponieważ zestawienie obu tych modeli zapisu wyjaśnia wszelkie różnice w zachowaniu programu Excel
Dlaczego moje komentarze wyświetlają się jako notatki w programie Excel 365?
Model tradycyjny — na który wskazuje metoda AddComment — przechowuje dane tekstowe w pliku xl/comments1.xml (typ zawartości application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml), a dymek pozycjonuje przy użyciu pomocniczego pliku rysunku VML xl/drawings/vmlDrawing1.vml. To rozwiązanie powstało na długo przed wprowadzeniem paska wstążki. Program Excel 365 nadal poprawnie odczytuje te dane, jednak renderuje je jako „notatkę” (note) i nie wyświetla dla nich interfejsu odpowiedzi, ponieważ struktura pliku nie daje możliwości tworzenia wątków. Autor to zwykły ciąg tekstowy, pozycja to kształt VML — i to cały model
Wątkowane komentarze, wprowadzone w programie Excel 365, całkowicie pomijają warstwę VML. Każdy arkusz z konwersacją posiada własny plik xl/threadedComments/threadedCommentN.xml o typie zawartości application/vnd.openxmlformats-officedocument.spreadsheetml.threadedComments+xml, a skoroszyt zawiera pojedynczy plik xl/person/person.xml (typ zawartości ...spreadsheetml.people+xml) z listą wszystkich uczestników. HotXLS udostępnia oba te modele w ramach jednego arkusza: AddComment nadal zapisuje tradycyjną parę plików, natomiast AddThreadedComment zapisuje nową strukturę. Jeśli Twój wynik wyświetla się jako notatka, oznacza to, że wywołałeś pierwszą funkcję API zamiast drugiej. Model tradycyjny wciąż jest odpowiednim narzędziem do niektórych zadań — co szczegółowo omawia wcześniejszy artykuł o komentarzach komórek i hiperłączach w procesach recenzji (w tym po stronie formatu XLS) — lecz żadne z jego ustawień nie przekształci go w wątek
Co HotXLS zapisuje w strukturze archiwum
HotXLS modeluje każdą odpowiedź jako obiekt TXLSXThreadedComment zawierający zakotwiczenie komórki (Ref), identyfikator typu GUID (Id), opcjonalny identyfikator rodzica ParentId, identyfikator autora AuthorId powiązany z listą osób, nazwę wyświetlaną DisplayName, treść komentarza, flagę ukończenia Done oraz znacznik czasu ISO-8601 DateTime. Podczas zapisu każdy wpis staje się elementem <threadedComment>, którego atrybut t przechowuje identyfikator, atrybut dT przechowuje znacznik czasu, a atrybut parentT — obecny wyłącznie w odpowiedziach podrzędnych — wskazuje na identyfikator rodzica. Excel odtwarza drzewo konwersacji poprzez dopasowanie parentT to t; struktura XML nie zawiera zagnieżdżeń, jest to po prostu płaska lista z odnośnikami
Plik rejestru osób (person part) to element, o którym zapomina większość samodzielnie pisanych bibliotek. Każdy identyfikator authorId w wątkowanym komentarzu musi odpowiadać pozycji <person> w pliku xl/person/person.xml, powiązanej z relacjami skoroszytu i posiadającej własny typ zawartości — w przeciwnym razie Excel nie wyświetli nazwy autora. HotXLS obsługuje tę rejestrację automatycznie: metoda AddThreadedComment wyszukuje autora na liście Persons skoroszytu po nazwie wyświetlanej i w razie jego braku tworzy nową pozycję z nowym kluczem GUID. Dzięki temu kolejne komentarze tego samego autora współdzielą jedną tożsamość. Relacje arkusza otrzymują powiązanie z threadedComments, relacje skoroszytu powiązanie z rejestrem osób, a plik typów zawartości (content-types) obie nowe definicje — wszystko to bez ingerencji w Twój kod
Tworzenie łańcucha odpowiedzi za pomocą AddThreadedComment i AddThreadedReply
Metoda AddThreadedComment tworzy korzeń konwersacji: jej identyfikator ParentId pozostaje pusty, co oznacza szczyt wątku. Z kolei AddThreadedReply przyjmuje parametr ParentRef — referencję komórki, w której znajduje się korzeń wątku — odnajduje go pod tym adresem i wpisuje identyfikator Id korzenia do pola ParentId nowej odpowiedzi. Obie funkcje zwracają nowy obiekt TXLSXThreadedComment, co pozwala od razu ustawić dla niego znacznik czasu lub status rozwiązania wątku:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Root: TXLSXThreadedComment;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('forecast.xlsx');
Sheet := Book.Sheets[0];
// Korzeń konwersacji; ParentId pozostaje pusty
Root := Sheet.AddThreadedComment(8, 3,
'Q3 figure looks low against the pipeline export', 'Maria Ortiz');
Root.DateTime := '2026-07-06T09:12:00Z';
// Odpowiedzi są zakotworzone w tej samej komórce; ParentRef wskazuje komórkę korzenia
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;
Dwa szczegóły w tym przykładzie zasługują na uwagę. Po pierwsze, przekazujemy Root.Ref zamiast ręcznie budowanego ciągu tekstowego: wygenerowana przez bibliotekę referencja gwarantuje zgodność z tym, czego będzie szukać metoda AddThreadedReply, co eliminuje błędy przesunięcia adresów zakotwiczenia. Po drugie, obaj autorzy zostali podani jako zwykłe nazwy wyświetlane — rejestracja w bazie osób, przypisanie identyfikatorów GUID oraz powiązania authorId odbyły się automatycznie pod maską wywołań. Otwórz zapisany plik w Excelu 365, a zakotwiczenie komórek, kolejność odpowiedzi i dwaj różni uczestnicy będą w pełni aktywni: kliknij Odpowiedz, a Excel doda wpis do tego samego wątku, który zapoczątkował Twój kod
Odczytywanie konwersacji: zachowanie podczas zapisu i odczytu
Biblioteka HotXLS obsługuje pełny zapis i odczyt wątkowanych komentarzy od wersji v2.122.0: ponowne otwarcie zapisanego pakietu odtwarza listę konwersacji każdego arkusza oraz listę uczestników skoroszytu, dzięki czemu identyfikatory, powiązania odpowiedzi i nazwy wyświetlane przechodzą nienaruszone przez cały proces odczytu i zapisu. Czytnik analizuje plik person.xml przed wczytaniem zawartości arkuszy, co pozwala na dopasowanie każdego authorId do nazwy wyświetlanej już w trakcie ładowania. Wczytane identyfikatory osób są zachowywane bez ich ponownego generowania, więc skoroszyt przekazywany na przemian między Twoim kodem a programem Excel zachowuje spójne tożsamości autorów podczas dowolnej liczby cykli odczytu i zapisu
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('Korzeń pod adresem ', Cmt.Ref, ' by ', Cmt.DisplayName, ': ', Cmt.Text)
else
Writeln(' odpowiedź autora ', Cmt.DisplayName, ': ', Cmt.Text);
end;
Writeln('Participants: ', Book.Persons.Count);
end;
Płaska struktura zapisu z odnośnikami jest tu widoczna celowo: kolekcja ThreadedComments listuje odpowiedzi w kolejności zapisu w pliku XML, a pusty parametr ParentId wskazuje na korzenie wątków. Jeśli potrzebujesz struktury drzewiastej (na przykład do eksportu rejestru weryfikacji), pogrupuj wpisy najpierw po polu Ref, a następnie połącz ParentId z Id w ramach danej komórki. To zachowanie opierające się na zasadzie „zapisz to, co wczytałeś” stanowi przykład szerszej strategii biblioteki; artykuł o bezstratnym zapisie motywów, list rozszerzeń i łańcucha obliczeń (calc chain) opisuje, jak ta sama zasada dotyczy pozostałych części pliku
Status rozwiązania, znaczniki czasu i tożsamość autorów
Trzy atrybuty niosą ze sobą semantykę weryfikacji i wszystkie są zapisywalne w zwracanych obiektach. Właściwość Done odpowiada atrybutowi done w pliku XML — Excel wyświetla rozwiązany wątek jako zwinięty z odnośnikiem do jego ponownego otwarcia. DateTime to znacznik czasu utworzenia według standardu ISO-8601 UTC (atrybut dT); HotXLS wstawia stały znacznik zastępczy, jeśli pole pozostanie puste, dzięki czemu plik przechodzi walidację, lecz rzeczywisty znacznik czasu ułatwia analizę historii wątku, stąd warto go ustawiać. Po stronie tożsamości każdy obiekt TXLSXPerson udostępnia właściwość ProviderId (zazwyczaj o wartości None dla wpisów tworzonych lokalnie) oraz UserId, którą można uzupełnić adresem email lub nazwą UPN, jeśli aplikacja nimi dysponuje:
var
Root: TXLSXThreadedComment;
Person: TXLSXPerson;
begin
Root := Sheet.ThreadedComments.FindAt('D9');
if Root <> nil then
Root.Done := True; // zapisywane jako done="1"; Excel wyświetli wątek jako rozwiązany
Person := Book.Persons.FindByDisplayName('Maria Ortiz');
if Person <> nil then
Person.UserId := 'maria.ortiz@example.com';
end;
Jedno ograniczenie, o którym należy wspomnieć wprost: HotXLS zapisuje element <mentions> jako pusty. Oznaczenia typu @-mention — mechanizm podświetlający imię współpracownika w treści komentarza i wyzwalający powiadomienie w usłudze Microsoft 365 — nie są modelowane, stąd tekst zawierający znak @ jest zapisywany jako zwykły tekst. W przypadku generowanych skoroszytów weryfikacyjnych rzadko stanowi to problem, lecz jeśli proces wymaga powiadomień o wzmiankach, należy założyć, że doda je użytkownik w programie Excel. Tożsamość autorów w rejestrze osób naturalnie uzupełnia się z polami autorskimi na poziomie skoroszytu, opisanymi w artykule o metadanych skoroszytów i właściwościach dokumentów, gdzie zapisywana jest pozostała ścieżka audytu generowanego pliku
Jak starsze wersje programu Excel traktują wątkowane komentarze?
Program Excel 2016 i starsze wersje powstały przed opracowaniem modelu wątkowanego i po prostu ignorują elementy, których nie rozumieją. Gdy sam program Excel 365 zapisuje wątkowaną konwersację, tworzy dodatkowo cień w postaci tradycyjnego komentarza — notatkę zastępczą z tekstem „[Threaded comment]…” — właśnie po to, by starsze wersje wyświetliły cokolwiek w zakotwiczonej komórce. HotXLS generuje wyłącznie elementy wątkowane bez tego tradycyjnego cienia, więc plik zapisany przez Twój kod po otwarciu w Excelu 2016 nie pokaże żadnego znacznika komentarza. Jeśli docelowi użytkownicy korzystają ze starszych wersji Excela (przed wersją 365) i komentarz musi być dla nich widoczny, zgodnym kanałem komunikacji pozostaje tradycyjne API AddComment. Należy jednak unikać nakładania obu tych modeli na tę samą komórkę bez przetestowania zachowania we wszystkich docelowych wersjach Excela, ponieważ sposób pogodzenia notatki i wątku w jednej komórce zależy wyłącznie od danej przeglądarki