Artikel Teknis

Sel Tergabung HotXLS dan Templat Laporan Berbasis Tata Letak di Delphi

Telusuri cell-cell sebuah template laporan yang baru dibuka, dan sebuah judul gabungan berperilaku seperti sebuah kebocoran. Anda membaca A1 dan mendapatkan "Quarterly Statement"; Anda membaca B1 hingga F1, yang secara visual berada di bawah spanduk yang sama, dan Anda tidak mendapatkan apa-apa. Tulis sebuah nilai ke dalam C1 untuk menambal header itu, dan nilai itu tidak pernah muncul di layar. Grid-nya tidak kehilangan data Anda. Ia sedang melakukan persis apa yang dimaksud sebuah merge: baik dalam XLS maupun XLSX, sebuah persegi panjang gabungan merender konten dari satu cell saja, anchor kiri-atas, dan memperlakukan sisanya sebagai ruang tertutup yang menyimpan nilai namun tidak pernah menampilkannya. Pengguna Excel menyerap ini lewat coba-coba. Sebuah generator laporan harus mengodekannya sebagai sebuah aturan, karena dalam kode yang dihasilkan, gejalanya adalah sebuah region kosong tanpa exception untuk ditelusuri penyebabnya. HotXLS, sebuah library Object Pascal native yang membaca dan menulis kedua format Excel dari Delphi dan C++Builder, menampilkan tabel merge secara cukup eksplisit sehingga Anda bisa memprogram sesuai aturan itu, bukan menemukannya kembali lewat sebuah tiket dukungan

Satu nilai, satu anchor

Sebuah merge adalah sebuah instruksi tampilan yang dilapiskan di atas sebuah grid yang tidak berubah bentuk. Setiap cell yang tertutup tetap ada dalam file sebagai slotnya sendiri; rekaman merge itu hanya memberi tahu sebuah konsumen untuk melukis konten anchor-nya melintasi persegi panjang tersebut. Perbedaan itu mendorong tiga perilaku yang layak diresapi sebelum Anda menulis kode layout apa pun. Membaca sebuah cell yang tertutup mengembalikan nilai tersimpannya sendiri, yang untuk sebuah spanduk yang Anda bangun biasanya kosong, sehingga kode apa pun yang memeriksa sebuah judul gabungan harus menyelesaikan dan membaca anchor-nya. Menulis ke sebuah cell yang tertutup berhasil pada level file dan tidak muncul di mana pun, yang menjadi jebakan header-tak-terlihat dari bagian pembuka. Dan melepas gabungan sebuah region mengungkap apa pun yang berada di bawahnya sepanjang waktu, sehingga sebuah nilai nyasar yang ditulis ke dalam ruang tertutup berubah menjadi sebuah cacat yang terlihat pada hari seseorang membubarkan merge tersebut

Diagram spanduk tergabung HotXLS di mana sel tertutup mempertahankan slot mereka sendiri sementara pembacaan beralih ke jangkar A1 dalam spreadsheet Delphi
HotXLS mempertahankan setiap sel tertutup sebagai slot nyata dan hanya melukis ulang anchor, sehingga pembacaan diselesaikan melalui A1 sementara penulisan ke ruang tertutup tetap tak terlihat sampai unmerge

Pada sisi XLSX, tabel itu adalah sebuah objek kelas-satu. Sheet.MergedCells membawa Add('A1:C1'), FindAt(Row, Col), DeleteAt, dan Items, dan satu pemanggilan yang paling sering Anda gunakan adalah FindAt: berikan koordinat apa pun kepadanya, dan ia mengembalikan region gabungan yang menutupi cell tersebut, atau nil ketika cell itu berdiri sendiri. Satu lookup tunggal itu menjadi fondasi bagi kedua separuh penanganan merge yang benar, pembacaan yang aman dan penjaga penulisan, dan keduanya muncul belakangan

Dua facade, dua idiom merge

HotXLS mempertahankan engine BIFF8 .xls klasik dan engine OOXML .xlsx sebagai model objek yang terpisah, dan keduanya mengeja merge secara berbeda karena turun dari konvensi yang berbeda. Facade XLS mengikuti idiom COM Excel: Anda mengambil sebuah range dari sebuah properti terindeks dua-argumen dan memanggil Merge dengan sebuah OleVariant yang nilainya menentukan geometri yang Anda dapatkan

var
  Book: IXLSWorkbook;   // dihitung lewat interface: tanpa Free manual
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // koleksi sheet XLS berbasis 1
  Sh.Range['A1', 'F1'].Merge(False);    // False = satu blok gabungan
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = merge across: satu merge per baris
  Book.SaveAs('layout.xls');
end;

Argumen untuk Merge adalah bagian yang paling sering disalahpahami orang. Pada sebuah range dua-baris, Merge(True) menghasilkan dua merge satu-baris yang independen, yang merupakan "Merge Across" milik Excel dan justru yang Anda inginkan untuk sebuah pita header bertingkat yang barisnya harus tetap bisa dipisahkan. Merge(False) melebur seluruh persegi panjang menjadi satu blok tunggal. Range itu juga melaporkan MergeCells sebagai sebuah flag status, mengembalikan region yang mengandungnya lewat MergeArea, dan membubarkan dirinya sendiri dengan Unmerge. Facade XLSX mengekspos operasi yang sama dengan nama berbeda: Sheet.MergeCells(Row1, Col1, Row2, Col2) menerima batas integer, TXLSXRange.Merge menerima varian Across yang setara, dan koleksi MergedCells menyimpan hasilnya

Sebuah template yang bertumbuh bersama datanya

Sebuah template laporan sungguhan bukanlah sebuah grid tetap. Header dan totalnya tetap, tetapi bagian detail di antara keduanya meregang mengikuti apa pun yang dikembalikan query. Pola yang terbukti tahan mempertahankan satu baris detail yang sudah berstyle penuh dalam template, mengkloningnya sekali per record, lalu membuka sebuah celah di depan blok totals sehingga segala sesuatu yang berlabuh di bawahnya bergeser turun tanpa kehilangan formatting-nya

Templat laporan HotXLS yang bertumbuh di Delphi: baris detail bergaya dikloning per rekaman dan InsertRows membuka celah sehingga blok totals bergeser turun dengan merge tetap utuh
Mengkloning baris detail berstyling membawa style dan formulanya ke setiap salinan, dan InsertRows lalu menggeser pita totals ke bawah dengan merge dan format yang utuh
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // nilai menuju anchor, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1;        // indeks pool berbasis 0, sisi cell berbasis 1

// baris 5 adalah baris template detail yang sudah berstyle
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // style dan formula ikut terbawa

// buka sebuah celah di atas blok totals; konten di bawah bergeser turun
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

Dua baris layak dilihat sekali lagi. Penetapan font itu membawa sebuah off-by-one yang menggigit diam-diam: Fonts.Add mengembalikan sebuah posisi pool berbasis 0, sementara sebuah cell menyimpan sebuah referensi font berbasis 1 di mana 0 berarti font default, sehingga menghilangkan + 1 tidak memicu apa pun, ia hanya membuat style judul Anda salah jenis huruf. Baris lainnya adalah CopyRange, yang memindahkan formatting dan formula bersama nilainya. Itulah keseluruhan alasan untuk mengkloning sebuah baris template buatan tangan alih-alih merekonstruksi tampilannya dalam kode. Seorang desainer memiliki tampilan itu sekali saja, dalam template; generator hanya pernah menuangkan data ke dalam salinan-salinannya

Pemisahan itu semakin berskala ketika layout yang bisa digunakan kembali itu hidup di dalam workbook-nya sendiri, katakanlah sebuah sheet berisi pita header dan footer yang dipakai bersama di banyak laporan. CopyRangeTo menjalankan kloning yang sama melintasi batas worksheet, menerima sebuah sheet target plus koordinat tujuan, sehingga sebuah generator bisa mempertahankan satu sheet template yang murni dan mencapkan region-nya ke sebanyak apa pun sheet output yang dibutuhkan sebuah job. Alternatifnya, memutasi template di tempat lalu mencoba memulihkannya belakangan, adalah jenis hal yang berfungsi sampai hari sebuah run terhenti di tengah jalan

Apa yang dipindahkan InsertRows, dan apa yang tidak

Pola bertumbuh-template ini hanya berfungsi karena InsertRows XLSX adalah sebuah edit struktural, bukan sekadar pengocokan cell. Ketika ia membuka sebuah celah, ia memindahkan region gabungan, tinggi baris, hyperlink, comment, frozen pane, range autofilter, conditional format, data validation, table, defined name, anchor gambar, dan anchor chart yang berada di bawah titik penyisipan, bukan hanya nilai cell-nya. Itulah yang membuat blok totals tiba di baris barunya dengan merge dan format angkanya tetap utuh, bukan tiba dalam keadaan terlucuti

Dua batasan terdokumentasinya adalah yang perlu dirancang di sekitarnya. Penyesuaian formula ter-scope pada sheet yang sedang diedit: referensi di dalam sheet itu ditulis ulang, dan sebuah formula pada sheet lain yang menunjuk ke area yang bergeser juga ditulis ulang, tetapi penyesuaian itu hanya mengikuti referensi yang menyasar sheet yang diedit, sehingga skema referensi lintas-workbook apa pun layak mendapat audit tersendiri, bukan kepercayaan membabi buta. Batasan kedua lebih tajam, dan itu ada di sisi XLS. Pivot table bertahan melewati siklus open-save sebagai rekaman mentah yang dipertahankan, bukan sebagai objek termodelkan yang bisa dipindahkan HotXLS, sehingga menyisipkan baris tidak memindahkan jejak sebuah pivot. Template apa pun yang Anda bangun untuk format .xls sebaiknya menempatkan region pivot-nya jauh dari pita mana pun yang bertumbuh

Menolak menulis data ke dalam ruang layout

Kegagalan merged-cell yang sungguhan sampai ke produksi bukanlah yang kosmetik. Ia bersifat struktural: sebuah baris detail bergeser masuk ke dalam sebuah pita layout gabungan, nilai-nilainya mendarat di cell yang tertutup dan menjadi tak terlihat, dan total kolom diam-diam berhenti cocok dengan apa yang dilihat siapa pun yang membaca sheet itu. Karena FindAt menjawab pertanyaan region-penutup untuk koordinat apa pun, generator bisa menolak penulisan itu pada saat ia akan terjadi, alih-alih mengirimkan sebuah laporan yang diam-diam kehitungannya kurang

// tolak menulis data detail ke dalam sebuah region layout gabungan
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
  raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;

Pemeriksaan batas yang sama berlaku di mana pun seorang pengguna belakangan akan mengurutkan atau memfilter outputnya. Sebuah range yang di dalamnya ada merge tidak bisa diurutkan dengan bersih, karena pengurutan memindahkan baris secara independen dan sebuah merge yang membentang beberapa baris tidak punya satu baris tunggal untuk ikut berpindah; Excel merespons dengan sebuah error atau sebuah layout yang berantakan. Disiplin yang menjaga laporan tetap benar bersifat geografis. Batasi merge pada pita judul, pemisah bagian, dan blok tanda tangan, dan jaga bagian tengah tabular sheet tetap datar. Artikel tentang pembuatan laporan berbasis template mengembangkan pemisahan layout-versus-data ini menjadi sebuah workflow lengkap berbasis placeholder, dan artikel tentang conditional formatting dan rich text membahas styling pita data yang datar itu

Bagaimana merge terdegradasi saat keluar

Sebuah merge adalah sebuah konsep workbook, dan setiap format ekspor berorientasi-teks menghormatinya pada tingkat yang berbeda-beda. Mengetahui ketiga perilaku ini lebih dulu menghemat satu siklus QA. Ekspor HTML mereproduksi merge dengan setia, memancarkan colspan dan rowspan pada satu table, sehingga sebuah laporan berbasis browser mempertahankan tampilan berpitanya. Ekspor RTF sama sekali tidak membentang kolom: teks anchor mendarat di cell-nya sendiri dan sisa lebar merge itu keluar sebagai cell kosong, yang meninggalkan sebuah judul lebar yang secara visual terdorong ke kiri dalam sebuah word processor. CSV tidak punya konsep merge sama sekali, sehingga nilai anchor menempati satu field dan setiap cell yang tertutup terpancar sebagai sebuah field kosong. Kesimpulannya untuk sebuah workbook yang juga memberi makan ekspor berpembatas adalah menjaga apa pun yang penting tetap berada di luar geometri gabungan; artikel tentang ekspor CSV, TSV, dan HTML membahas setiap format secara rinci

Judul tergabung HotXLS yang diekspor dari Delphi ke HTML dengan colspan dan rowspan, RTF tanpa span, dan CSV sebagai bidang yang diratakan
Judul ter-merge yang sama selamat melalui ekspor HTML lewat colspan dan rowspan, menurun di RTF menjadi satu sel terkunci-kiri yang sendirian, dan merata menjadi satu nilai plus field kosong di CSV

Satu kepastian bagi siapa pun yang menimbang hal ini terhadap ukuran file: merge nyaris tidak berbiaya apa pun pada skala laporan. Tabel merge-nya kecil sekali dibanding data cell, dan membaca sebuah cell yang tertutup tetap melewati FindAt, bukan pemindaian. Tekanan performa pada workbook besar datang dari tempat lain, terutama pertumbuhan style-pool dan memori yang ditahan jalur penyimpanan, yang dibahas langsung oleh artikel tentang performa workbook besar. Kedua API merge, operasi editing struktural, dan demo template-nya tersedia dalam paket HotXLS Delphi Component