Přejmenujte list z „Summary" na „Overview" ve vygenerovaném sešitu a každý interní hypertextový odkaz, který mířil na Summary!A1, přestane vést kamkoli. Žádná výjimka při ukládání, žádná při otevření. Odkaz se stále vykreslí, stále vypadá jako klikatelný a tiše se přeloží na nic. Stejný typ poškození se objeví i po konverzi typu save-as nebo po cyklu .xls/.xlsx tam a zpět, když se komentář posune o sloupec vedle nebo relativní odkaz ztratí svůj cíl. Obě funkce nesou revizní stav, na jehož základě jednají skuteční lidé, takže když se pokazí, selhání zůstane neviditelné, dokud recenzent na odkaz neklikne a nic se nestane
To je praktický důvod, proč si komentáře a hypertextové odkazy zaslouží více péče, než naznačuje jejich kosmetický vzhled. HotXLS dává kódu v Delphi a C++Builderu přímý zápisový přístup k oběma prvkům, v XLS i XLSX, bez jakékoli automatizace Excelu v řetězci. Odvrácenou stranou této kontroly je zodpovědnost: knihovna zapíše přesně ty cíle, které jí předáte, a žádný z nich neověřuje, takže udržet revizní workflow neporušený je úkolem vašeho kódu, ne Excelu
Komentáře buněk jako strojově psané revizní záznamy
V modelu tříd XLSX je komentář objekt na úrovni listu: zná svůj řádek, svůj sloupec, autora a textový obsah. Pole autora si své místo zaslouží. Když sešit vygenerovaný vaším kódem prochází revizním řetězcem, první otázka, kterou si auditor položí, zní, kdo danou poznámku napsal, a poznámka ponechaná bez autora na tuto otázku odpovídá prázdným polem. Označte generované komentáře servisní identitou, aby původ nebyl nikdy nejednoznačný
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Note: TXLSXComment;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('reconciliation.xlsx');
Sheet := Book.Sheets[0];
// Podepsaná poznámka k upravené hodnotě
Sheet.AddComment(14, 4, 'Manual adjustment: late FX rate, see ticket FIN-2214',
'recon-service');
// Aktualizace stávající poznámky místo přidání druhé
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;
Sonda FindAt má větší váhu, než se na první pohled zdá. Dávková úloha, která se po dočasném selhání zopakuje, klidně zavolá AddComment podruhé na buňku, kterou už okomentovala, a buňka pak skončí se dvěma poznámkami navrch sebe, o které nikdo nežádal. Nejprve prozkoumejte pomocí FindAt a aktualizujte vrácený objekt. Kolekce Comments také nabízí DeleteAt a DeleteInRange. K variantě pro rozsah sáhněte ve chvíli, kdy sešit před odesláním čistíte: vymazání interních QA poznámek z celé oblasti je jedno volání místo ručně psané smyčky přes buňky
Externí URL a skoky uvnitř sešitu jsou odlišná API
OOXML udržuje oba typy odkazů na různých místech. Externí URL se stane záznamem vztahu (relationship) v části .rels daného listu, přičemž buňka na tento vztah odkazuje přes id. Interní skok se vrstvy vztahů vůbec nedotkne; jde o obyčejný řetězec umístění, například Summary!A1, uložený přímo na odkazu. HotXLS udržuje tento rozdíl viditelný v API místo toho, aby přetěžoval jedinou metodu, což znamená, že správné volání vyberete podle toho, kde cíl žije:
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 výsledném objektu TXLSXHyperlink se Url a Location vzájemně vylučují a IsInternal vám řekne, která z těchto dvou vlastností je vyplněná. Tento příznak kontrolujete ve chvíli, kdy inventarizujete odkazy v otevřeném sešitu a potřebujete podle odlišných pravidel zacházet s tím, co „opouští soubor" a co „zůstává v souboru": externí hostitel může podléhat allowlistu, zatímco interní cíl musí jen pojmenovat list, který existuje. Interní odkazy za sebou nenesou žádné části vztahů, což je také činí levnějšími na hromadné přepsání
Poškození z úvodu žije celé na interní straně a plyne z jednoho faktu: řetězec umístění není naparsovaná reference. HotXLS zapíše přesně ten text, který mu předáte, a nic tento text znovu nepřesměruje, když je list později přejmenován. V praxi obstojí dvě obrany. První je disciplína v pořadí: přejmenujte každý list dřív, než vygenerujete jediný odkaz, a poté zacházejte s názvy listů jako se zmrazenými identifikátory. Druhá je odolnější a přežije i přejmenování provedená dodatečně. Nasměrujte odkaz na definovaný název na úrovni sešitu místo na syrovou adresu Sheet!Cell, protože Excel přepíše definici názvu, když se podkladový list změní, takže se odkaz automaticky sveze s ním. Tento druhý přístup se přirozeně pojí s technikami popsanými v článku o definovaných názvech a vzorcích napříč listy v HotXLS
Strana XLS: stejné koncepty, starší technické zázemí
Fasáda BIFF8 zavěšuje komentáře na rozsahy místo na kolekci na úrovni listu. Zavoláte AddComment na objektu IXLSRange a dostanete zpět TXLSComment; vlastnost Comment daného rozsahu čte existující poznámku a ClearComments je vymaže. Ostrá hrana je tu poziční. Objekt TXLSComment veřejně nezpřístupňuje svůj vlastní řádek a sloupec, takže přirozená smyčka „projdi každý komentář a nahlaš, kde sedí" jde proti srsti tomuto API. Musíte začít od buněk. Buď řiďte audit ze seznamu adres, které jste okomentovali, nebo si při zápisu veďte vlastní záznam pozic, protože objekt komentáře vám později neřekne, kde žije
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; // otevře poznámku hned při prvním zobrazení
Sheet.AddHyperlink(7, 2, 'https://intranet.example.com/signoff/4821',
'Sign-off form', 'Opens the controller queue');
Book.SaveAs('review.xls');
end;
Nastavení Visible na True je starší způsob, jak zajistit, že poznámka nejde přehlédnout: žlutý rámeček zůstane na listu otevřený místo toho, aby čekal na najetí myší. TXLSComment jde o krok dál než jeho protějšek v XLSX tím, že zpřístupňuje TextRuns, takže jediná poznámka může nést tučné varování vedle prostého vysvětlení, formátování, které API komentářů XLSX stejným způsobem nezpřístupňuje. Hypertextové odkazy na této straně přicházejí přes tři postupná přetížení (jen adresa, poté s zobrazovaným textem, poté s tipem obrazovky) a čtou se zpět přes kolekci HyperLinks listu, kde každý odkaz zpřístupňuje Address, SubAddress, DisplayText a ScreenTip
List s revizním indexem poráží roztroušené poznámky
Po zhruba tuctu poznámek přestává princip „najeď a přečti" tiše škálovat. Poznámky se hromadí na listech, které recenzent nikdy neotevře, a právě ty nejdůležitější jsou nejsnáze přehlédnutelné. Struktura, která se osvědčila nejlépe, je generovaný indexový list: jeden řádek na každé okomentované místo, s uvedením názvu listu, adresy buňky, autora a krátkého výtahu z poznámky. Poslední sloupec nese interní hypertextový odkaz vytvořený pomocí AddHyperlinkToCell, který skočí přímo na okomentovanou buňku. Recenzent pak čte seznam odshora dolů místo pátrání po mřížce, a počet řádků tohoto indexu zároveň slouží jako inventář vašich komentářů pro auditní krok níže
Index je levné sestavit, protože váš generátor už zná každou pozici, které se dotkl. Při zápisu každého komentáře přidejte do seznamu n-tici (list, řádek, sloupec, autor, shrnutí) a indexový list vydejte až jako poslední, aby byl počet jeho řádků před uložením konečný. Vyplatí se dvě vylepšení: seřaďte index podle závažnosti nebo podle listu místo podle pořadí vkládání a do záhlaví indexu vložte návratový odkaz, aby se recenzent mohl po každé položce vrátit nahoru. Protože interní odkazy jsou obyčejné řetězce umístění bez čehokoli ve vrstvě vztahů za sebou, i tisícařádkový index přidá k velikosti souboru nebo době ukládání téměř nic
Tentýž list se vyplatí znovu i na cestě zpět. Když se recenzovaný sešit vrátí, váš kód čte hodnoty stavu zapsané do buněk vedle řádků indexu, místo aby znovu procházel každý list a hledal komentáře, které se možná změnily. Sloupec strukturovaných stavových buněk se parsuje čistě; roztroušené poznámky ve volném textu ne
Auditní krok před předáním, který poškození skutečně odhalí
Žádné z těchto API cíl neověřuje. Odkaz na list, který jste smazali, překlepnutý název intranetového hostitele, sdílená složka zrušená minulé čtvrtletí: to vše se uloží bez jediného protestu. ECMA-376 specifikuje, jak je odkaz uložen, ne to, že se na něco přeloží. Sešit, který nese revizní metadata, si proto zaslouží vlastní krátký auditní krok, spuštěný těsně před SaveAs:
- Shromážděte každé interní umístění zapsané během generování a ověřte, že název listu před vykřičníkem stále existuje v kolekci listů sešitu
- Zkontrolujte externí URL proti allowlistu schémat a hostitelů. Holé cesty
file://a UNC prozrazují detaily prostředí a přestanou fungovat ve chvíli, kdy soubor opustí vaši síť - Spočítejte komentáře na list a porovnejte je s tím, co váš generátor zamýšlel zapsat. Opakování, které poznámky zdvojilo, se odhalí tady, a ne až v poštovní schránce recenzenta
- Odstraňte pouze interní poznámky pomocí
DeleteInRangevždy, když příjemce sedí mimo organizaci
Týmy, které staví sešity z datové vrstvy, mohou tento krok sloučit se stejným krokem pipeline, který už validuje data, takže se kontrola metadat sveze zdarma. Mechanika je stejná jako ta popsaná v článku o exportu výsledků databázových dotazů do reportů v Excelu, jen zaměřená na odkazy a komentáře místo na řádky
Jeden detail s uvozovkami lidem podráží nohy, když si řetězce umístění sestavují ručně. List, jehož název obsahuje mezeru, musí být uvnitř umístění uzavřen do uvozovek přesně tak, jak to dělá řádek vzorců: 'Quarterly Totals'!A1, ne Quarterly Totals!A1. HotXLS uplatňuje stejná pravidla, jaká pro odkazy napříč listy používá jádro vzorců, takže pokud odkaz funguje ve vzorci na listu, jeho uvozovkování bude fungovat i tady. Předejte mu neuzavřený název s mezerou a dostanete stejný tichý mrtvý odkaz, před kterým varoval úvod
Komentáře a hypertextové odkazy jsou části vygenerovaného sešitu, na které recenzenti jednají bez druhého pohledu, a přesně proto cíl, který nikam nemíří, napáchá reálnou škodu dřív, než si toho kdokoli všimne. Vytvořte validační krok jednou, spouštějte jej na každém sešitu před odesláním a revizní workflow zůstane neporušený napříč přejmenováními i konverzemi. Celý povrch API pro fasády XLS i XLSX je zdokumentován na stránce produktu HotXLS Delphi Component