HotXLS menyertakan TXLSWorkbookViewer, sebuah kontrol VCL native yang merender workbook XLS, XLSX, XLSM, dan ODS sebagai sebuah grid spreadsheet interaktif di dalam sebuah form Delphi atau C++Builder, tanpa menginstal Excel atau menjalankannya lewat OLE automation. Membangun kontrol semacam itu dengan baik berarti menyelesaikan tiga masalah spesifik: memetakan sebuah klik mouse yang mendarat di dalam sebuah sel gabungan ke sel logis yang benar, menjaga posisi scroll, pita header, dan seleksi sel tetap konsisten saat pengguna menggeser sebuah sheet yang jauh lebih besar dari window yang terlihat, dan memutuskan apa yang seharusnya sebenarnya dilakukan sebuah klik pada marker komentar atau sel hyperlink
Kebanyakan perusahaan Delphi menjangkau sebuah spreadsheet viewer karena alasan yang tidak berkaitan dengan editing: sebuah stasiun audit yang mempratinjau workbook yang diunggah sebelum masuk ke sebuah pipeline, sebuah kios atau report viewer di mana Microsoft Office bukan bagian dari image deployment, atau sebuah tool QA yang perlu menampilkan konten sebuah workbook tanpa ketidakpastian mengotomasi sebuah proses Excel sungguhan lewat COM. Sebuah string grid polos memberikan Anda teks dalam sel dengan cepat, tetapi sebuah file spreadsheet bukan sebuah grid polos: sel-sel bergabung menjadi blok yang hanya ada sekali dalam model yang mendasarinya, sheet membawa pita header tetap dan posisi scroll horizontal dan vertikal independen, dan sel individual membawa komentar dan hyperlink yang membutuhkan model interaksinya sendiri. TXLSWorkbookViewer adalah jawaban HotXLS atas celah itu, dan desain internalnya adalah sebuah cetak biru yang masuk akal bagi siapa pun yang membangun kontrol serupa dari nol
Bagaimana sebuah workbook viewer menghindari ketergantungan pada Excel?
TXLSWorkbookViewer sepenuhnya menghindari Excel dengan membaca lewat model objek ter-parse milik HotXLS sendiri alih-alih membuka sebuah dokumen lewat Excel dan mengendalikannya seperti boneka. Properti Workbook mengikat sebuah TXLSWorkbook yang sudah ada untuk file XLS klasik, dan XlsxWorkbook mengikat sebuah TXLSXWorkbook untuk varian XLSX, XLSM, dan template; salah satunya bisa saja sudah terbuka di tempat lain dalam aplikasi, dan viewer hanya membaca darinya. Ketika kontrol seharusnya memiliki file itu sendiri, LoadFromFile memeriksa ekstensinya, mengarahkan XLSX, XLSM, XLTX, XLTM, dan ODS lewat mesin modern dan sisanya lewat yang klasik, dan membebaskan workbook mana pun yang dibuatnya begitu kontrol dikosongkan atau dihancurkan
var
Viewer: TXLSWorkbookViewer;
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
if Book.Open('quarterly-report.xlsx') <> 1 then
raise Exception.Create('Could not open workbook');
Viewer := TXLSWorkbookViewer.Create(Self);
Viewer.Parent := Self;
Viewer.Align := alClient;
Viewer.XlsxWorkbook := Book; // the viewer does not take ownership
Viewer.GoToCell(1, 1);
Caption := Viewer.WorksheetName + ': ' + Viewer.SelectedCellText;
end;
Menemukan sel yang benar di dalam sebuah rentang gabungan
Menyelesaikan sebuah klik ke sel yang benar di TXLSWorkbookViewer adalah sebuah lookup dua-tahap, dan pemisahan itu penting karena geometri piksel dan semantik spreadsheet benar-benar masalah yang berbeda. Tahap pertama adalah geometri murni: sebuah metode privat CellAtPoint menelusuri lebar kolom dan tinggi baris dari posisi scroll saat ini sampai menemukan pita yang mengandung koordinat X dan Y yang diklik, sama sekali tanpa kesadaran akan sel gabungan. Tahap kedua adalah semantik: setiap jalur yang mengubah seleksi, sebuah klik mouse, sebuah tombol panah, Tab, atau sebuah pemanggilan langsung ke GoToCell, menyalur lewat satu rutin internal ChangeSelection, yang menormalisasi baris dan kolom mentah terhadap gabungan apa pun dan menjepretkannya ke sel anchor gabungan sebelum seleksi benar-benar berubah
Anchor itu adalah sel kiri-atas dari rentang gabungan, dan itu satu-satunya sel dalam blok itu yang benar-benar memegang sebuah nilai, sebuah format, sebuah komentar, atau sebuah hyperlink dalam model workbook yang mendasarinya; setiap sel lain yang dicakup gabungan itu secara visual kosong dalam data itu sendiri. Untuk workbook XLS klasik, anchor itu berasal dari Cell.MergeArea, sebuah IXLSRange yang Row dan Column-nya menunjuk ke sel pemilik; untuk workbook XLSX dan ODS, MergedCells.FindAt mengembalikan sebuah TXLSXMergedRange yang mengekspos anchor yang sama sebagai Row1 dan Col1. Penggambaran menyelesaikan masalah setara secara independen, memperluas rectangle sebuah sel gabungan ke rentang baris dan kolom penuhnya dan melewati sel-sel di dalam rentang itu, sehingga outline seleksi membungkus seluruh blok gabungan bukan hanya sudut anchor-nya, dan menulis layout gabungan alih-alih hanya membacanya kembali adalah sebuah masalah yang berkaitan tetapi berbeda yang dibahas artikel pendamping tentang layout sel gabungan untuk template laporan
var
Sheet: TXLSXWorksheet;
begin
Sheet := Book.Sheets.Add('Summary');
Sheet.MergeCells(2, 2, 3, 4); // B2:D3
Sheet.Cells[2, 2].Value := 'Region totals';
Viewer.XlsxWorkbook := Book;
Viewer.GoToCell(3, 4); // targets the bottom-right corner of the merge
// SelectedRow is now 2 and SelectedCol is now 2: normalized to the anchor cell
end;
Apa yang menjaga scrolling, header, dan seleksi tetap sinkron?
TXLSWorkbookViewer menjaga tiga bagian state terpisah tetap koheren: posisi scroll logis yang dipegang di TopRow dan LeftCol, scrollbar Windows native yang diminta kontrol lewat WS_HSCROLL dan WS_VSCROLL dalam CreateParams, dan seleksi saat ini di SelectedRow dan SelectedCol. Menyeret sebuah scrollbar atau memutar roda mouse memicu WM_HSCROLL, WM_VSCROLL, atau WM_MOUSEWHEEL, yang memperbarui TopRow atau LeftCol dan menggambar ulang; seleksinya tidak berpindah, yang cocok dengan bagaimana Excel sendiri memisahkan panning dari selecting. Setelah salah satu dari pembaruan itu, UpdateScrollBars mendorong posisi baru kembali ke scrollbar native lewat SetScrollInfo, sehingga thumb tidak pernah melenceng dari kecocokan dengan apa yang sebenarnya ditampilkan grid
Navigasi keyboard menjalankan sinkronisasi yang sama dalam arah berlawanan: memindahkan seleksi melewati tepi grid yang terlihat memanggil EnsureSelectionVisible, yang menggeser TopRow atau LeftCol dengan mengakumulasi lebar kolom dan tinggi baris sesungguhnya alih-alih sekadar menambah satu, karena baris dan kolom bisa membawa ukuran kustom, lalu memanggil UpdateScrollBars sehingga thumb mencerminkan ke mana pun keyboard baru saja membawa tampilan. Pita header nomor-baris dan huruf-kolom, diukur lewat RowHeaderWidth dan ColumnHeaderHeight, adalah bagian dari kontrol ini yang tetap tetap di layar sementara TopRow dan LeftCol menggulirkan data di bawahnya, dan itulah sejauh apa freezing yang dilakukan kontrol ini sendiri: ini bukan fitur Freeze Panes milik Excel, dan tidak ada cara bawaan untuk menyematkan sebuah baris atau kolom data sembarang sementara sisa sheet menggulir melewatinya. Satu batas yang layak diuji sebelum merilis sebuah viewer atas file yang tidak sepenuhnya Anda kendalikan adalah bahwa TopRow dan LeftCol tidak dijepit terhadap rentang terpakai sesungguhnya milik worksheet, sehingga sebuah thumb yang diseret ke batas strukturalnya bisa mendarat pada baris 1.048.576 atau kolom 16.384 dan menampilkan sebuah grid kosong alih-alih baris atau kolom terakhir yang benar-benar memegang data; workbook yang cukup besar untuk membuat itu terlihat biasanya juga cukup besar untuk membutuhkan perhatian sisi-pemuatan yang dibahas artikel performa workbook besar
Menghubungkan komentar dan hyperlink ke event mouse dan seleksi
TXLSWorkbookViewer memperlakukan komentar dan hyperlink sebagai atribut sel mana pun yang sedang terpilih alih-alih sebagai target hover, sehingga SelectedCellCommentText, SelectedCellCommentAuthor, dan SelectedCellHyperlink diperbarui setiap kali OnSelectionChange terpicu, baik seleksinya berpindah lewat klik mouse, tombol panah, atau sebuah pemanggilan ke GoToCell. Sebuah sel berkomentar mendapat sebuah segitiga merah kecil digambar di sudut kanan-atasnya sebagai isyarat visual, mirip dengan flag komentar milik Excel sendiri, tetapi marker itu murni visual; tidak ada tooltip terpicu-hover bawaan dalam kontrol ini, sehingga sebuah aplikasi yang menginginkan sebuah popup saat mouse-over alih-alih saat seleksi harus membangun lapisan itu sendiri. Aktivasi hyperlink bekerja dengan cara seleksi-lebih-dulu yang sama: double-click sebuah sel memanggil ActivateSelectedCell, yang membaca SelectedCellHyperlink dan, jika tidak kosong, memicu OnHyperlinkClick dengan alamat target dan sebuah parameter var Handled: Boolean untuk diatur handler
Apa yang tidak dilakukan OnHyperlinkClick sama pentingnya: TXLSWorkbookViewer tidak pernah memanggil ShellExecute atau membuka sebuah browser dengan sendirinya, terlepas dari apakah handler mengatur Handled menjadi true atau membiarkannya false. Navigasi, dan keputusan apa pun tentang apa yang dianggap sebagai target aman, sepenuhnya menjadi tanggung jawab aplikasi host, yang merupakan default yang tepat untuk sebuah komponen yang tidak tahu apakah dirinya disematkan dalam sebuah tool internal terpercaya atau sebuah viewer untuk file yang baru saja diunggah seorang pelanggan
procedure TMainForm.ViewerSelectionChange(Sender: TObject; Row, Col: Integer);
begin
if Viewer.SelectedCellCommentText <> '' then
StatusBar.SimpleText := Viewer.SelectedCellCommentAuthor + ': ' +
Viewer.SelectedCellCommentText
else
StatusBar.SimpleText := Viewer.SelectedCellHyperlink;
end;
procedure TMainForm.ViewerHyperlinkClick(Sender: TObject;
const Target: WideString; var Handled: Boolean);
begin
ShellExecute(0, 'open', PWideChar(Target), nil, nil, SW_SHOWNORMAL);
Handled := True;
end;
Cakupan seleksi dan batas navigasi keyboard
Seleksi di TXLSWorkbookViewer selalu satu sel logis tunggal, dilacak sebagai SelectedRow dan SelectedCol; tidak ada seleksi rentang multi-sel persegi panjang dalam kontrol dasar, sehingga fitur apa pun yang perlu beroperasi pada sebuah blok sel harus dibangun di atasnya alih-alih dibaca dari sebuah objek seleksi. Cakupan keyboard sengaja dibuat dasar: tombol panah berpindah satu sel pada satu waktu, Home kembali ke awal baris atau, dengan Ctrl, ke sel A1, Page Up dan Page Down melompat sepuluh baris, dan Tab serta Shift+Tab melangkah melintasi kolom; tidak ada lompatan Ctrl+Panah ke tepi sebuah region data dan tidak ada seleksi rentang terperluas-Shift, sehingga pengguna yang langsung datang dari Excel akan memperhatikan celah itu pada sebuah sheet yang padat
Batas kolom diterapkan pada titik cekik ChangeSelection yang sama yang menangani normalisasi gabungan, dan berbeda berdasarkan mesin dengan sengaja: sebuah viewer yang terikat ke TXLSWorkbook klasik menjepit pada kolom 256, batas atas struktural format BIFF8, sementara yang terikat ke TXLSXWorkbook menghormati batas 16.384-kolom modern yang diwarisi XLSX dari Excel 2007 dan seterusnya. Baris dibatasi pada 1.048.576 dalam kedua kasus, sehingga perbedaan praktis antara membuka sebuah file XLS lawas dan sebuah file XLSX dalam viewer yang sama sepenuhnya tentang seberapa jauh ke kanan grid itu bersedia membiarkan Anda pergi
Tidak satu pun dari ini eksotis begitu dipecah menjadi lookup piksel, normalisasi anchor, dan segelintir message handler, tetapi membuat ketiganya sepakat di bawah file sungguhan, dengan gabungan, komentar, dan hyperlink sungguhan, adalah sebagian besar pekerjaan dalam sebuah komponen seperti ini. TXLSWorkbookViewer disertakan sebagai bagian dari HotXLS Excel Component standar untuk Delphi dan C++Builder, berdampingan dengan model objek klasik dan XLSX yang menjadi sumber render-nya