Техническа статия

Коментари на клетки и хипервръзки в Excel от Delphi с HotXLS

Преименувайте лист от „Summary - на „Overview - в генерирана работна книга и всяка вътрешна връзка, която сочи към Summary!A1, ще спре да работи. Без изключение при запис, без съобщение при отваряне. Връзката продължава да се показва и изглежда активна, но не води до никъде. Подобни проблеми възникват и след конвертиране чрез „запиши като“ или при преобразуване между .xls и .xlsx, когато коментар се измести с една колона или относителна връзка загуби целта си. И двете функции съдържат състояние за преглед, от което зависят потребителите, така че когато се повредят, грешката остава скрита, докато някой не кликне върху тях

Това е практическата причина, поради която коментарите и хипервръзките изискват повече внимание, отколкото подсказва външният им вид. HotXLS предоставя на Delphi и C++Builder разработчиците директен достъп за запис и на двата обекта в XLS и XLSX без необходимост от Excel автоматизация. Обратната страна на този контрол е отговорността: библиотеката записва точните цели, които й подадете, без да ги валидира, така че поддържането на коректен работен процес за преглед е задача на вашия код, а не на Excel

Коментари в клетките като автоматизирани записи за преглед

В класовия модел на XLSX коментарът е обект на ниво работен лист: неговият автор е налице, той знае своя ред, своята колона и има текст. Полето за автор има важно значение. Когато генерираната книга преминава през верига за одит, първият въпрос е кой е написал дадена бележка, а бележка без автор оставя това поле празно. Винаги маркирайте автоматично генерираните коментари с името на съответната услуга, за да бъде произходът им ясен

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;

Методът FindAt е по-полезен, отколкото изглежда. Автоматична задача, която се рестартира след временна грешка, би извикала AddComment втори път за същата клетка, което води до два насложени коментара. Винаги проверявайте първо с FindAt и обновявайте върнатия обект. Колекцията Comments излага също DeleteAt и DeleteInRange. Вариантът за диапазон е изключително удобен за изчистване на вътрешни QA анотации от цяла област с едно извикване, преди файлът да напусне организацията

Външните URL адреси и вътрешните връзки в книгата ползват различни API

OOXML съхранява двата вида връзки на различни места. Външният URL адрес се записва като запис за връзка в .rels частта на листа, а клетката сочи към него чрез идентификатор. Вътрешният преход изобщо не използва слоя за връзки; той е обикновен низ за местоположение, например Summary!A1, съхранен директно върху връзката. HotXLS запазва това разграничение видимо в API, вместо да претоварва един метод, така че правилното извикване се избира според местоположението на целта:

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

В обекта TXLSXHyperlink свойствата Url и Location се изключват взаимно, а IsInternal указва кой от двата е попълнен. Този флаг се проверява при опис на връзките в отворен документ, когато трябва да се приложат различни правила за външни и вътрешни цели: външните хостове могат да се проверяват спрямо списък с разрешени адреси, докато за вътрешните е достатъчно да сочат към съществуващ лист. Вътрешните връзки не изискват генериране на релационни части, което ги прави по-бързи за масова промяна

Проблемът от началото засяга изцяло вътрешните връзки и следва от един факт: низът за местоположение не е анализирана препратка. HotXLS записва точния текст, който му подадете, и нищо не пренасочва този текст, ако листът бъде преименуван по-късно. Две защити работят надеждно. Първата е дисциплина в реда на операциите: преименувайте всички листове, преди да генерирате дори една връзка, и след това третирайте имената им като фиксирани идентификатори. Втората е по-устойчива и преживява по-късно преименуване. Насочете връзката към дефинирано име на ниво работна книга вместо към необработен адрес Sheet!Cell, защото Excel пренаписва дефиницията на името при промяна на основния лист и връзката се актуализира автоматично. Този подход се съчетава естествено с техниките в дефинирани имена и формули между листове в HotXLS

От страна на XLS: същите концепции, по-стара архитектура

Интерфейсът на BIFF8 прикрепя коментарите към диапазони, а не към колекция на ниво работен лист. Извиквате AddComment за IXLSRange и получавате TXLSComment; свойството Comment на диапазона чете съществуваща бележка, а ClearComments ги изтрива. Позиционирането тук има особености. Обектът TXLSComment не показва публично своя ред и колона, така че стандартният цикъл за обхождане и отчитане на позицията на коментарите изисква да започнете от клетките. Записвайте координатите на коментарите при генерирането им, тъй като самият обект няма да ви ги съобщи по-късно

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;

Задаването на Visible на True е наследеният начин бележката да не може да бъде пропусната: жълтото поле остава отворено върху листа, вместо да чака посочване с мишката. TXLSComment отива по-далеч от XLSX еквивалента чрез TextRuns, така че една бележка може да съдържа удебелено предупреждение до обикновено обяснение, форматиране, което API за XLSX коментари не предоставя по същия начин. Хипервръзките тук се създават чрез три последователни претоварвания, само с адрес, след това с показван текст и накрая с подсказка, и се прочитат през колекцията HyperLinks на работния лист, където всяка връзка предоставя Address, SubAddress, DisplayText и ScreenTip

Създаване на индекс с препратки вместо пръснати бележки

При наличие на повече от десетина анотации прегледът чрез посочване с мишката става неефективен. Бележките се натрупват на листове, които никой не отваря, и най-важните от тях лесно се пропускат. Най-доброто решение е генериране на индексен лист за преглед: по един ред за всяко анотирано местоположение, съдържащ името на листа, адреса на клетката, автора и резюме на бележката. Последната колона съдържа вътрешна връзка, създадена с AddHyperlinkToCell, която води директно до анотираната клетка. Така рецензентът чете структуриран списък, вместо да претърсва цялата таблица

Този индекс се генерира лесно, тъй като приложението знае всяка позиция, която е променило. Добавяйте запис (лист, ред, колона, автор, резюме) към списък при запис на всеки коментар, след което генерирайте индексния лист последен. Две подобрения са полезни: сортирайте индекса по важност или по лист, и поставете връзка за връщане обратно в заглавието на индекса. Тъй като вътрешните връзки са обикновени текстови местоположения без релационни части, голям индекс не увеличава размера на файла или времето за запис

Същият лист е полезен и при обработка на върнатия файл. Вашето приложение може да чете клетките със статуси до индексните редове, вместо да претърсва отново цялата работна книга за променени коментари. Структурирана колона със статуси се чете лесно, докато разпръснатите коментари са трудни за обработка

Проверка преди изпращане за предотвратяване на грешки

Нито едно от тези API не валидира целта. Връзка към изтрит лист, грешен вътрешен хост или несъществуващ файлов споделен ресурс се записват без предупреждение. ECMA-376 определя как се съхранява връзката, а не дали тя води до реално местоположение. Поради това е препоръчително да добавите кратка стъпка за одит преди извикването на SaveAs:

  • Съберете всички вътрешни местоположения и потвърдете, че името на листа преди знака - - съществува в колекцията от листове на книгата
  • Проверете външните URL адреси спрямо списък с разрешени протоколи и хостове. Пътища от тип file:// и UNC адреси могат да разкрият системна информация и спират да работят извън мрежата
  • Пребройте коментарите на всеки лист и ги сравнете с очаквания брой, за да откриете евентуални дублирания при повторни опити
  • Премахнете вътрешните анотации с DeleteInRange, когато файлът е предназначен за външни получатели

Тази стъпка може да се автоматизира. Механиката е сходна с описаното в експортиране на резултати от бази данни в Excel отчети, но е насочена към връзки и коментари

Ена подробност при кавичките често води до грешки при ръчно сглобяване на местоположения. Лист, чието име съдържа интервал, трябва да бъде ограден с единични кавички: 'Quarterly Totals'!A1, а не Quarterly Totals!A1. HotXLS прилага същите правила за формули, така че ако връзката работи във формула, тя ще работи и тук. Нецитирано име с интервал води до неработеща връзка

Коментарите и хипервръзките са елементите, на които потребителите разчитат, поради което неработещите връзки могат да доведат до сериозни проблеми. Изградете автоматична валидация и връзките ви ще останат стабилни при всякакви промени. Пълното API описание е достъпно на продуктовата страница за HotXLS Component