Technical Article

Excel komentari i hiperveze u Delphiju uz HotXLS

Preimenujte list iz 'Summary' u 'Overview' u generiranoj radnoj knjizi i svaka interna hiperveza koja je pokazivala na Summary!A1 prestat će voditi bilo kamo. Bez iznimke pri spremanju, bez iznimke pri otvaranju. Veza se i dalje prikazuje, i dalje izgleda kao da se na nju može kliknuti i tiho se razrješava u ništa. Isti se problem javlja nakon konverzije pri spremanju (save-as) ili povratnog putovanja između .xls/.xlsx formata, kada komentar završi stupac dalje ili relativna veza izgubi svoje odredište. Obje značajke nose status pregleda (review state) na temelju kojeg stvarni ljudi djeluju, pa kada se pokvare, neuspjeh je nevidljiv sve dok recenzent ne klikne i ne dogodi se ništa.

To je praktičan razlog zašto komentari i hiperveze zaslužuju više pažnje nego što njihov kozmetički izgled sugerira. HotXLS daje kodu u Delphiju i C++Builderu izravan pristup pisanju za oboje, u XLS-u i XLSX-u, bez Excel automatizacije u petlji. Druga strana te kontrole je odgovornost: knjižnica zapisuje točno ona odredišta koja joj predate i ne provjerava nijedno od njih, pa je održavanje tijeka rada pregleda zadatak vašeg koda, a ne Excela.

Komentari ćelija kao strojno zapisani zapisi pregleda

U XLSX klasnom modelu komentar je objekt na razini radnog lista: on poznaje svoj redak, svoj stupac, autora i tijelo teksta. Polje autora zaslužuje svoje mjesto. Kada radna knjiga koju je generirao vaš kôd putuje kroz lanac pregleda, prvo pitanje koje revizor postavlja jest tko je napisao određenu bilješku, a bilješka ostavljena bez autora na to pitanje odgovara praznim poljem. Označite generirane komentare identitetom servisa kako podrijetlo nikada ne bi bilo dvosmisleno.

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;

Provjera pomoću FindAt nosi veću težinu nego što se čini. Serijski posao koji se ponovno pokreće nakon prolaznog neuspjeha rado će pozvati AddComment po drugi put na ćeliji koju je već označio, pa ćelija završi s dvjema poslaganim bilješkama koje nitko nije tražio. Najprije provjerite s FindAt i ažurirajte objekt koji vraća. Kolekcija Comments također izlaže metode DeleteAt i DeleteInRange. Ta varijanta raspona je ona za kojom trebate posegnuti kada čistite radnu knjigu prije nego što napusti zgradu: brisanje internih bilješki osiguranja kvalitete (QA annotations) iz cijele regije je jedan poziv, a ne ručno napisana petlja kroz ćelije.

Vanjski URL-ovi i skokovi unutar radne knjige su različiti API-ji

OOXML drži ove dvije vrste veza na različitim mjestima. Vanjski URL postaje unos odnosa (relationship entry) u .rels dijelu lista, pri čemu ćelija pokazuje na taj odnos pomoću ID-a. Interni skok uopće ne dotiče sloj odnosa; to je običan niz lokacije kao što je Summary!A1 pohranjen izravno u vezi. HotXLS drži tu razliku vidljivom u API-ju umjesto da preoptereti jednu metodu, što znači da birate ispravan poziv znajući gdje odredište živi:

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

Na rezultirajućem objektu TXLSXHyperlink, svojstva Url i Location se međusobno isključuju, a IsInternal vam govori koje je od ta dva popunjeno. Ta zastavica je ono što provjeravate kada popisujete veze u otvorenoj radnoj knjizi i trebate tretirati 'odlazak iz datoteke' i 'ostajanje u datoteci' prema različitim pravilima: vanjski poslužitelj mogao bi se suočiti s popisom dopuštenih (allowlist), dok interni cilj samo mora imenovati list koji postoji. Interne veze ne nose dijelove odnosa iza sebe, što ih također čini jeftinijima za masovno prepisivanje.

Problem s lomljenjem iz uvoda živi u potpunosti na internoj strani, a proizlazi iz jedne činjenice: niz lokacije nije parsirana referenca. HotXLS zapisuje točan tekst koji mu predate i ništa ne usmjerava taj tekst kada se list kasnije preimenuje. Dvije obrane funkcioniraju u praksi. Prva je disciplina oko redoslijeda: preimenujte svaki list prije nego što generirate ijednu vezu, a zatim tretirajte nazive listova kao zamrznute identifikatore. Druga je čvršća i preživljava preimenovanja napravljena nakon toga. Usmjerite vezu na definirani naziv na razini radne knjige (defined name) umjesto na sirovu adresu Sheet!Cell, jer Excel prepisuje definiciju naziva kada se temeljni list promijeni, pa veza automatski putuje s njim. Taj se drugi pristup prirodno povezuje s tehnikama u članku definirani nazivi i formule između listova u HotXLS-u.

XLS strana: isti koncepti, starija arhitektura

BIFF8 fasada veže komentare za raspone (ranges) umjesto za kolekciju na razini radnog lista. Pozivate AddComment na IXLSRange i dobivate natrag TXLSComment; svojstvo Comment raspona čita postojeću bilješku, a ClearComments ih briše. Ovdje je oštar rub položajni. TXLSComment javno ne izlaže vlastiti redak i stupac, pa prirodna petlja, 'prođi kroz svaki komentar i prijavi gdje se nalazi', radi unatrag u odnosu na API. Morate krenuti od ćelija. Ili pokrenite reviziju s popisa adresa koje ste označili ili vodite vlastiti dnevnik položaja dok pišete, jer vam objekt komentara kasnije neće reći gdje živi.

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;

Postavljanje Visible na True je naslijeđeni način da se bilješka učini nemogućom za previdjeti: žuti okvir ostaje otvoren na listu umjesto da čeka prijelaz mišem (hover). TXLSComment ide korak dalje od svog XLSX pandana izlažući TextRuns, pa jedna bilješka može nositi podebljano upozorenje pokraj običnog objašnjenja, što je oblikovanje koje XLSX API komentara ne izlaže na isti način. Hiperveze na ovoj strani dolaze kroz tri progresivna preopterećenja (samo adresa, zatim s tekstom prikaza, pa sa savjetom na zaslonu) i čitaju se natrag kroz kolekciju radnog lista HyperLinks, gdje svaka veza prikazuje Address, SubAddress, DisplayText i ScreenTip.

Indeksni list pregleda je bolji od razbacanih bilješki

Nakon desetak bilješki, čitanje prelazom miša (hover-to-read) tiho prestaje biti praktično. Bilješke se gomilaju na listovima koje recenzent nikada ne otvara, a one koje su najvažnije upravo su one koje je najlakše previdjeti. Struktura koja se najbolje pokazala je generirani indeksni list: jedan redak po označenoj lokaciji, s popisom naziva lista, adrese ćelije, autora i kratkim izvatkom bilješke. Posljednji stupac nosi internu hipervezu izgrađenu pomoću AddHyperlinkToCell koja skače izravno na označenu ćeliju. Sada recenzent čita popis umjesto da lovi po mreži, a broj redaka tog indeksa služi i kao popis komentara za revizijski korak u nastavku.

Indeks je jeftin za izgradnju jer vaš generator već zna svaki položaj koji je dotaknuo. Dodajte torku (list, redak, stupac, autor, sažetak) na popis kako pišete svaki komentar, a zatim indeksni list emitirajte na kraju kako bi njegov broj redaka bio konačan prije spremanja. Dvije se pojedinosti isplate: poredajte indeks po ozbiljnosti ili po listu, a ne po redoslijedu umetanja, i postavite povratnu vezu u zaglavlje indeksa kako bi se recenzent mogao vratiti na vrh nakon svakog predmeta. Budući da su interne veze obični nizovi lokacije bez ičega u sloju odnosa iza sebe, čak i indeks od tisuću redaka ne dodaje gotovo ništa veličini datoteke ili vremenu spremanja.

Taj se isti list isplati i na povratnom putu. Kada se pregledana radna knjiga vrati, vaš kôd čita vrijednosti statusa upisane u ćelije pokraj redaka indeksa umjesto da ponovno skenira svaki list u potrazi za komentarima koji su se možda promijenili. Stupac strukturiranih ćelija statusa parsira se čisto, dok se razbacane bilješke s slobodnim tekstom ne parsiraju tako jednostavno.

Provjera prije isporuke koja doista otkriva kvarove

Nijedan od ovih API-ja ne provjerava odredište. Veza na list koji ste izbrisali, pogrešno napisan host intraneta, mrežni udjel stavljen izvan pogona prošlog tromjesečja: sve se to sprema bez ikakvog upozorenja. Standard ECMA-376 definira kako se veza pohranjuje, a ne da se ona stvarno razrješava. Radna knjiga koja nosi revizijske metapodatke stoga zaslužuje kratku provjeru prije samog poziva SaveAs:

  • prikupite svaku internu lokaciju zapisanu tokom generiranja i potvrdite da naziv lista ispred znaka uskličnika i dalje postoji u kolekciji listova radne knjige.
  • provjerite vanjske URL-ove prema popisu dopuštenih shema i poslužitelja. Obične file:// i UNC putanje otkrivaju pojedinosti o okruženju i pucaju čim datoteka napusti vašu mrežu.
  • prebrojite komentare po listu i usporedite ih s onim što je vaš generator namjeravao zapisati. Ponovljeni pokušaj koji je udvostručio bilješke izbit će na površinu ovdje, a ne u recenzentovom sandučiću.
  • uklonite interne bilješke pomoću DeleteInRange kad god se primatelj nalazi izvan organizacije.

Timovi koji grade svoje radne knjige iz podatkovnog sloja mogu integrirati ovu fazu u isti korak cjevovoda koji već provjerava podatke, pa provjera metapodataka ide besplatno. Mehanizmi su oni opisani u članku izvoz rezultata upita baze podataka u Excel izvješća, usmjereni na veze i komentare radije nego na retke.

Jedan detalj oko navodnika stvara probleme ljudima kada ručno grade nizove lokacije. List čiji naziv sadrži razmak mora biti pod navodnicima unutar lokacije, točno onako kako ga navodi traka formule: 'Quarterly Totals'!A1, a ne Quarterly Totals!A1. HotXLS primjenjuje ista pravila koja mehanizam formule koristi za reference među listovima, pa ako veza radi u formuli radnog lista, njezini će navodnici raditi i ovdje. Predajte mu necitirani naziv s razmakom i dobit ćete istu tihu neaktivnu vezu o kojoj je uvod upozorio.

Komentari i hiperveze su dijelovi generirane radne knjige na koje recenzenti reagiraju bez drugog pogleda, zbog čega odredište koje ne pokazuje ni na što uzrokuje stvarnu štetu prije nego što itko primijeti. Izgradite provjeru jednom, pokrenite je na svakoj radnoj knjizi prije isporuke, i tijek rada pregleda ostaje netaknut kroz preimenovanja i konverzije. Cjelokupna API površina za XLS i XLSX fasade dokumentirana je na stranici proizvoda HotXLS Component.