Tehnički članak

Excel komentari i hiperveze u Delphiju uz HotXLS

Preimenujte list iz 'Summary' u 'Overview' u generisanoj radnoj svesci i svaka interna hiperveza koja je pokazivala na Summary!A1 prestaće da vodi bilo gde. Bez izuzetka pri čuvanju, bez izuzetka pri otvaranju. Veza se i dalje prikazuje, i dalje izgleda kao da se na nju može kliknuti i tiho se razrešava u ništa. Isti se problem javlja nakon konverzije pri čuvanju (save-as) ili povratnog putovanja između .xls/.xlsx formata, kada komentar završi kolonu dalje ili relativna veza izgubi svoje odredište. Obe funkcije nose status pregleda (review state) na osnovu kojeg stvarni ljudi deluju, pa kada se pokvare, neuspeh 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 sugeriše. HotXLS daje kodu u Delphiju i C++Builderu direktan pristup pisanju za oboje, u XLS-u i XLSX-u, bez Excel automatizacije u petlji. Druga strana te kontrole je odgovornost: biblioteka zapisuje tačno ona odredišta koja joj predate i ne proverava nijedno od njih, pa je održavanje toka rada pregleda zadatak vašeg koda, a ne Excela

Komentari ćelija kao mašinski zapisani zapisi pregleda

U XLSX klasnom modelu komentar je objekat na nivou radnog lista: on poznaje svoj red, svoju kolonu, autora i telo teksta. Polje autora zaslužuje svoje mesto. Kada radna sveska koju je generisao vaš kod putuje kroz lanac pregleda, prvo pitanje koje revizor postavlja jeste ko je napisao određenu zabelešku, a zabeleška ostavljena bez autora na to pitanje odgovara praznim poljem. Označite generisane komentare identitetom servisa kako poreklo 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 ponovo pokreće nakon prolaznog neuspeha rado će pozvati AddComment po drugi put na ćeliji koju je već označio, pa ćelija završi sa dvema poslaganim zabeleškama koje niko nije tražio. Najpre proverite sa FindAt i ažurirajte objekat koji vraća. Kolekcija Comments takođe izlaže metode DeleteAt i DeleteInRange. Ta varijanta raspona je ona za kojom trebate posegnuti kada čistite radnu svesku pre nego što napusti zgradu: brisanje internih zabeleški osiguranja kvaliteta (QA annotations) iz cele regije je jedan poziv, a ne ručno napisana petlja kroz ćelije

Spoljni URL-ovi i skokovi unutar radne sveske su različiti API-ji

OOXML drži ove dve vrste veza na različitim mestima. Spoljni URL postaje unos odnosa (relationship entry) u .rels delu lista, pri čemu ćelija pokazuje na taj odnos pomoću ID-a. Interni skok uopšte ne dotiče sloj odnosa; to je običan niz lokacije kao što je Summary!A1 sačuvan direktno u vezi. HotXLS drži tu razliku vidljivom u API-ju umesto da preoptereti jednu metodu, što znači da birate ispravan poziv znajući gde 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 proveravate kada popisujete veze u otvorenoj radnoj svesci i trebate tretirati 'odlazak iz datoteke' i 'ostajanje u datoteci' prema različitim pravilima: spoljni server mogao bi se suočiti sa spiskom dopuštenih (allowlist), dok interni cilj samo mora da imenuje list koji postoji. Interne veze ne nose delove odnosa iza sebe, što ih takođe čini jeftinijima za masovno prepisivanje

Problem sa lomljenjem iz uvoda živi u potpunosti na internoj strani, a proizlazi iz jedne činjenice: niz lokacije nije parsirana referenca. HotXLS zapisuje tačan tekst koji mu predate i ništa ne usmerava taj tekst kada se list kasnije preimenuje. Dve odbrane funkcionišu u praksi. Prva je disciplina oko redosleda: preimenujte svaki list pre nego što generišete ijednu vezu, a zatim tretirajte nazive listova kao zamrznute identifikatore. Druga je čvršća i preživljava preimenovanja napravljena nakon toga. Usmerite vezu na definisani naziv na nivou radne sveske (defined name) umesto na sirovu adresu Sheet!Cell, jer Excel prepisuje definiciju naziva kada se temeljni list promeni, pa veza automatski putuje sa njim. Taj se drugi pristup prirodno povezuje sa tehnikama u članku definisani nazivi i formule između listova u HotXLS-u

XLS strana: isti koncepti, starija arhitektura

BIFF8 fasada veže komentare za raspone (ranges) umesto za kolekciju na nivou radnog lista. Pozivate AddComment na IXLSRange i dobijate nazad TXLSComment; svojstvo Comment raspona čita postojeću zabelešku, a ClearComments ih briše. Ovde je oštar rub položajni. TXLSComment javno ne izlaže sopstveni red i kolonu, pa prirodna petlja, 'prođi kroz svaki komentar i prijava gde se nalazi', radi unazad u odnosu na API. Morate krenuti od ćelija. Ili pokrenite reviziju sa spiska adresa koje ste označili ili vodite sopstveni dnevnik položaja dok pišete, jer vam objekat komentara kasnije neće reći gde ž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 nasleđeni način da se zabeleška učini nemogućom za prevideti: žuti okvir ostaje otvoren na listu umesto da čeka prelaz mišem (hover). TXLSComment ide korak dalje od svog XLSX pandana izlažući TextRuns, pa jedna zabeleška može nositi podebljano upozorenje pored 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 sa tekstom prikaza, pa sa savetom na ekranu) i čitaju se nazad kroz kolekciju radnog lista HyperLinks, gde svaka veza prikazuje Address, SubAddress, DisplayText i ScreenTip

Indeksni list pregleda je bolji od razbacanih zabeleški

Nakon desetak zabeleški, čitanje prelazom miša (hover-to-read) tiho prestaje biti praktično. Zabeleške se gomilaju na listovima koje recenzent nikada ne otvara, a one koje su najvažnije upravo su one koje je najlakše prevideti. Struktura koja se najbolje pokazala je generisani indeksni list: jedan red po označenoj lokaciji, sa spiskom naziva lista, adrese ćelije, autora i kratkim izvodom zabeleške. Poslednja kolona nosi internu hipervezu izgrađenu pomoću AddHyperlinkToCell koja skače direktno na označenu ćeliju. Sada recenzent čita spisak umesto da lovi po mreži, a broj redova 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, red, kolona, autor, rezime) na spisak kako pišete svaki komentar, a zatim indeksni list emitujte na kraju kako bi njegov broj redova bio konačan pre spremanja. Dve se pojedinosti isplate: poredajte indeks po ozbiljnosti ili po listu, a ne po redosledu 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 hiljadu redova ne dodaje gotovo ništa veličini datu ili vremenu spremanja

Taj se isti list isplati i na povratnom putu. Kada se pregledana radna sveska vrati, vaš kod čita vrednosti statusa upisane u ćelije pored redova indeksa umesto da ponovo skenira svaki list u potrazi za komentarima koji su se možda promenili. Kolona strukturiranih ćelija statusa parsira se čisto, dok se razbacane zabeleške sa slobodnim tekstom ne parsiraju tako jednostavno

Provera pre isporuke koja doista otkriva kvarove

Nijedan od ovih API-ja ne proverava odredište. Veza na list koji ste izbrisali, pogrešno napisan host intraneta, mrežni udeo stavljen van pogona prošlog tromesečja: sve se to čuva bez ikakvog upozorenja. Standard ECMA-376 definiše kako se veza čuva, a ne da se ona stvarno razrešava. Radna sveska koja nosi revizijske metapodatke stoga zaslužuje kratku proveru pre samog poziva SaveAs:

  • prikupite svaku internu lokaciju zapisanu tokom generisanja i potvrdite da naziv lista ispred znaka uzvika i dalje postoji u kolekciji listova radne sveske
  • proverite spoljne URL-ove prema spisku dopuštenih shema i servera. 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 uporedite ih sa onim što je vaš generator nameravao da zapiše. Ponovljeni pokušaj koji je udvostručio zabeleške izbiće na površinu ovde, a ne u recenzentovom sandučetu
  • uklonite interne zabeleške pomoću DeleteInRange kad god se primalac nalazi van organizacije

Timovi koji grade svoje radne sveske iz podatkovnog sloja mogu integrisati ovu fazu u isti korak pipeline-a koji već proverava podatke, pa provera metapodataka ide besplatno. Mehanizmi su oni opisani u članku izvoz rezultata upita baze podataka u Excel izveštaje, usmereni na veze i komentare radije nego na redove

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, tačno onako kako ga navodi traka formule: 'Quarterly Totals'!A1, a ne Quarterly Totals!A1. HotXLS primenjuje ista pravila koja mehanizam formule koristi za reference među listovima, pa ako veza radi u formuli radnog lista, njeni će navodnici raditi i ovde. Predajte mu necitirani naziv sa razmakom i dobićete istu tihu neaktivnu vezu o kojoj je uvod upozorio

Komentari i hiperveze su delovi generisane radne sveske na koje recenzenti reaguju bez drugog pogleda, zbog čega odredište koje ne pokazuje ni na što uzrokuje stvarnu štetu pre nego što iko primeti. Izgradite proveru jednom, pokrenite je na svakoj radnoj svesci pre isporuke, i tok rada pregleda ostaje netaknut kroz preimenovanja i konverzije. Celokupna API površina za XLS i XLSX fasade dokumentovana je na stranici proizvoda HotXLS Component