Artikel Teknis

Komentar Sel dan Tautan Excel di Delphi dengan HotXLS

Ganti nama sebuah sheet dari "Summary" menjadi "Overview" pada workbook yang dihasilkan, dan setiap hyperlink internal yang menunjuk ke Summary!A1 berhenti mengarah ke mana pun. Tidak ada exception saat disimpan, tidak ada saat dibuka. Link tersebut masih tampil, masih terlihat bisa diklik, dan diam-diam mengarah ke ketiadaan. Kerusakan serupa muncul setelah konversi save-as atau round-trip .xls/.xlsx, ketika sebuah comment mendarat satu kolom bergeser atau sebuah link relatif kehilangan targetnya. Kedua fitur ini membawa status review yang ditindaklanjuti orang sungguhan, sehingga saat keduanya rusak, kegagalannya tidak terlihat sampai seorang reviewer mengklik dan tidak terjadi apa-apa

Itulah alasan praktis mengapa comment dan hyperlink layak mendapat perhatian lebih besar daripada yang tersirat dari tampilan kosmetiknya. HotXLS memberi kode Delphi dan C++Builder akses tulis langsung ke keduanya, pada XLS maupun XLSX, tanpa Excel automation yang terlibat dalam prosesnya. Sisi lain dari kendali tersebut adalah tanggung jawab: library ini menulis persis target yang Anda berikan dan tidak memvalidasi satu pun darinya, sehingga menjaga review workflow tetap utuh adalah tugas kode Anda, bukan tugas Excel

Cell comment sebagai catatan review yang ditulis mesin

Dalam model class XLSX, sebuah comment adalah objek pada level worksheet: ia mengetahui row-nya, column-nya, seorang author, dan sebuah text body. Field author ini memang layak ada. Ketika workbook yang dihasilkan kode Anda melewati sebuah rantai review, pertanyaan pertama yang diajukan seorang auditor adalah siapa yang menulis catatan tertentu, dan sebuah catatan yang ditinggalkan tanpa author menjawab pertanyaan itu dengan kekosongan. Bubuhkan identitas layanan pada comment yang dihasilkan sehingga asal-usulnya tidak pernah ambigu

Diagram percobaan ulang komentar HotXLS Delphi di mana probe FindAt memperbarui catatan sel yang ada sementara percobaan ulang AddComment yang buta menumpuk duplikat
Percobaan ulang yang memanggil AddComment secara buta menumpuk catatan kedua di sel yang sama, sementara probe FindAt menyunting catatan yang sudah ada di sana
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Note: TXLSXComment;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('reconciliation.xlsx');
    Sheet := Book.Sheets[0];

    // Catatan beratribusi pada angka yang disesuaikan
    Sheet.AddComment(14, 4, 'Manual adjustment: late FX rate, see ticket FIN-2214',
      'recon-service');

    // Perbarui catatan yang sudah ada, bukan menumpuk catatan kedua
    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;

Pemeriksaan FindAt punya bobot lebih besar daripada kelihatannya. Sebuah batch job yang melakukan retry setelah kegagalan sementara akan dengan mudah memanggil AddComment untuk kedua kalinya pada cell yang sudah diberi anotasi, dan cell tersebut berakhir dengan dua catatan bertumpuk yang tidak diminta siapa pun. Periksa dulu dengan FindAt, lalu perbarui objek yang dikembalikannya. Koleksi Comments juga menyediakan DeleteAt dan DeleteInRange. Varian range itulah yang perlu digunakan saat Anda membersihkan sebuah workbook sebelum meninggalkan gedung: menghapus anotasi QA internal dari seluruh region cukup dengan satu panggilan, bukan loop manual atas cell satu per satu

URL eksternal dan lompatan di dalam workbook adalah API yang berbeda

OOXML menyimpan kedua jenis link ini di tempat yang berbeda. URL eksternal menjadi entri relationship dalam bagian .rels milik sheet, dengan cell menunjuk ke relationship tersebut melalui id. Lompatan internal sama sekali tidak menyentuh relationship layer; ia hanyalah string lokasi biasa seperti Summary!A1 yang disimpan langsung pada link tersebut. HotXLS menjaga perbedaan itu tetap terlihat dalam API, alih-alih membebankan satu method untuk keduanya, yang berarti Anda memilih panggilan yang tepat dengan mengetahui di mana target itu berada:

Diagram yang mengontraskan bagaimana HotXLS menyimpan URL eksternal sebagai relationship di bagian rels dan lompatan internal sebagai string lokasi biasa dalam workbook buatan Delphi
URL eksternal bepergian melalui layer relationship sementara lompatan internal adalah teks polos, sehingga tiap jenis gagal dengan caranya sendiri dan butuh aturan auditnya sendiri
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');

Pada objek TXLSXHyperlink yang dihasilkan, Url dan Location saling eksklusif, dan IsInternal memberi tahu Anda mana di antara keduanya yang terisi. Flag itulah yang Anda periksa ketika menginventarisasi link dalam sebuah workbook yang dibuka dan perlu memperlakukan "keluar dari file" dan "tetap di dalam file" dengan aturan berbeda: sebuah host eksternal mungkin harus melewati allowlist, sementara target internal hanya perlu menyebut nama sheet yang benar-benar ada. Link internal tidak membawa bagian relationship di baliknya, yang juga membuatnya lebih murah untuk ditulis ulang secara massal

Kerusakan yang diceritakan di bagian pembuka sepenuhnya berada di sisi internal, dan itu bersumber dari satu fakta: string lokasi bukan referensi yang sudah diuraikan. HotXLS menulis persis teks yang Anda berikan, dan tidak ada yang mengarahkan ulang teks tersebut ketika sebuah sheet diganti namanya di kemudian hari. Dua bentuk pertahanan ini terbukti ampuh dalam praktik. Yang pertama adalah disiplin soal urutan: ganti nama semua sheet sebelum Anda membuat satu pun link, lalu perlakukan nama sheet sebagai identifier yang baku. Yang kedua lebih kokoh dan tetap bertahan meski penggantian nama dilakukan belakangan. Arahkan link ke sebuah defined name pada level workbook, bukan ke alamat mentah Sheet!Cell, karena Excel menulis ulang definisi sebuah name ketika sheet yang mendasarinya berubah, sehingga link ikut terbawa secara otomatis. Pendekatan kedua ini berpadu secara alami dengan teknik-teknik pada defined names dan formula lintas-sheet di HotXLS

Sisi XLS: konsep yang sama, mekanisme yang lebih lama

Facade BIFF8 menggantungkan comment pada range, bukan pada koleksi level worksheet. Anda memanggil AddComment pada sebuah IXLSRange dan mendapatkan kembali sebuah TXLSComment; properti Comment milik range membaca catatan yang sudah ada, dan ClearComments menghapusnya. Sisi tajam di sini adalah soal posisi. Sebuah TXLSComment tidak mengekspos row dan column-nya sendiri secara publik, sehingga loop yang wajar, "telusuri setiap comment dan laporkan di mana ia berada," berjalan terbalik terhadap API ini. Anda harus memulai dari cell-nya. Jalankan audit dari daftar alamat yang Anda beri anotasi, atau simpan log posisi Anda sendiri saat menulis, karena objek comment tidak akan memberi tahu Anda kemudian di mana ia berada

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;   // buka catatan secara langsung pada tampilan pertama

  Sheet.AddHyperlink(7, 2, 'https://intranet.example.com/signoff/4821',
    'Sign-off form', 'Opens the controller queue');
  Book.SaveAs('review.xls');
end;

Menyetel Visible menjadi True adalah cara lawas untuk membuat sebuah catatan mustahil terlewat: kotak kuningnya tetap terbuka pada sheet, alih-alih menunggu hover. TXLSComment melangkah lebih jauh dibanding rekannya di XLSX dengan mengekspos TextRuns, sehingga satu catatan bisa membawa peringatan tebal di samping penjelasan biasa, sebuah pemformatan yang tidak diekspos dengan cara yang sama oleh API comment XLSX. Hyperlink pada sisi ini hadir lewat tiga overload yang bertahap (hanya alamat, lalu dengan display text, lalu dengan sebuah screen tip) dan dibaca kembali lewat koleksi HyperLinks milik worksheet, tempat setiap link menampilkan Address, SubAddress, DisplayText, dan ScreenTip

Sheet indeks review mengungguli catatan yang berserakan

Melewati sekitar selusin anotasi, cara hover-untuk-membaca diam-diam berhenti mampu mengimbangi jumlahnya. Catatan menumpuk pada sheet yang tidak pernah dibuka seorang reviewer, dan yang paling penting justru yang paling mudah terlewat. Struktur yang paling terbukti tahan adalah sebuah sheet indeks yang dihasilkan: satu baris per lokasi yang diberi anotasi, mencantumkan nama sheet-nya, alamat cell, author, dan sebuah kutipan singkat dari catatan tersebut. Kolom terakhir membawa sebuah hyperlink internal yang dibangun dengan AddHyperlinkToCell yang melompat langsung ke cell yang dianotasi. Kini reviewer membaca ke bawah sebuah daftar alih-alih memburu di seluruh grid, dan jumlah baris indeks tersebut sekaligus menjadi inventaris comment Anda untuk tahap audit di bawah ini

Indeks ini murah untuk dibangun karena generator Anda sudah mengetahui setiap posisi yang ia sentuh. Tambahkan sebuah tuple (sheet, row, column, author, summary) ke sebuah daftar setiap kali Anda menulis comment, lalu hasilkan sheet indeks terakhir sehingga jumlah barisnya sudah final sebelum Anda menyimpan. Dua penyempurnaan yang terbukti berguna: urutkan indeks berdasarkan tingkat keparahan atau berdasarkan sheet, bukan berdasarkan urutan penyisipan, dan tempatkan sebuah link kembali pada header indeks sehingga reviewer bisa melompat balik ke atas setelah setiap item. Karena link internal hanyalah string lokasi biasa tanpa apa pun di relationship layer di baliknya, bahkan indeks seribu baris pun nyaris tidak menambah apa-apa pada ukuran file atau waktu penyimpanan

Sheet yang sama itu kembali berguna pada perjalanan pulang. Ketika workbook yang sudah direview kembali, kode Anda membaca nilai status yang diketik ke dalam cell di samping baris indeks, alih-alih memindai ulang setiap sheet untuk mencari comment yang mungkin sudah berubah. Sebuah kolom cell status yang terstruktur bisa diparse dengan bersih; sebaran catatan teks bebas tidak bisa

Tahap audit pra-pengiriman yang benar-benar menangkap kerusakan

Tidak satu pun dari API ini memvalidasi sebuah target. Sebuah link ke sheet yang sudah Anda hapus, sebuah host intranet yang salah eja, sebuah file share yang sudah dinonaktifkan kuartal lalu: semuanya tersimpan tanpa keluhan sedikit pun. ECMA-376 menetapkan bagaimana sebuah link disimpan, bukan bahwa link itu mengarah ke sesuatu. Karena itu, sebuah workbook yang membawa metadata review layak mendapat tahap audit singkat buatan Anda sendiri, dijalankan tepat sebelum SaveAs:

Diagram lintasan audit pra-pengiriman HotXLS yang memeriksa target internal, allowlist URL, jumlah komentar, dan pembersihan penerima sebelum SaveAs di Delphi
Empat pemeriksaan berjalan tepat sebelum SaveAs dan setiap satu darinya menangkap kegagalan yang tak akan pernah dilempar library itu sendiri
  • Kumpulkan setiap lokasi internal yang ditulis selama generasi dan pastikan nama sheet sebelum tanda seru masih ada dalam koleksi sheet milik workbook
  • Periksa URL eksternal terhadap sebuah allowlist scheme dan host. Path file:// polos dan path UNC membocorkan detail environment dan langsung rusak begitu file meninggalkan jaringan Anda
  • Hitung jumlah comment per sheet dan bandingkan dengan apa yang dimaksudkan generator Anda untuk ditulis. Sebuah retry yang menggandakan catatan akan muncul di sini, bukan di kotak masuk reviewer
  • Hapus anotasi khusus-internal dengan DeleteInRange setiap kali penerima berada di luar organisasi

Tim yang membangun workbook mereka dari sebuah data layer bisa melipat tahap ini ke dalam langkah pipeline yang sama yang sudah memvalidasi data, sehingga pemeriksaan metadata ikut berjalan tanpa biaya tambahan. Mekanismenya sama seperti yang dijelaskan pada mengekspor hasil query database ke laporan Excel, hanya diarahkan ke link dan comment, bukan ke baris

Satu detail penguotean membuat orang tersandung ketika mereka membangun string lokasi secara manual. Sebuah sheet yang namanya mengandung spasi harus diberi tanda kutip di dalam lokasi tersebut, persis seperti cara formula bar mengutipnya: 'Quarterly Totals'!A1, bukan Quarterly Totals!A1. HotXLS menerapkan aturan yang sama yang digunakan formula engine untuk referensi lintas-sheet, sehingga jika sebuah link berfungsi dalam sebuah formula worksheet, cara pengutipannya juga akan berfungsi di sini. Berikan nama tanpa tanda kutip yang mengandung spasi, dan Anda mendapatkan link mati yang diam-diam sama seperti yang diperingatkan di bagian pembuka

Comment dan hyperlink adalah bagian dari workbook yang dihasilkan yang langsung ditindaklanjuti reviewer tanpa berpikir dua kali, dan itulah tepatnya mengapa sebuah target yang mengarah ke ketiadaan menimbulkan kerugian nyata sebelum ada yang menyadarinya. Bangun tahap validasi ini sekali, jalankan pada setiap workbook sebelum dikirim, dan review workflow tetap utuh melewati penggantian nama dan konversi. Seluruh permukaan API untuk facade XLS maupun XLSX didokumentasikan pada halaman produk HotXLS Delphi Component