Tehnični članak

Preglednica po meri v Delphiju s HotXLS

HotXLS vključuje TXLSWorkbookViewer, izvorni kontrolnik VCL, ki delovne zvezke XLS, XLSX, XLSM in ODS upodablja kot interaktivno mrežo preglednice v obrazcu Delphi ali C++Builder, brez namestitve Excela ali upravljanja z njim prek avtomatizacije OLE. Kakovostna izdelava takega kontrolnika zahteva rešitev treh določenih težav: preslikavo klika miške, ki pristane znotraj združene celice, na pravilno logično celico, ohranjanje skladnosti položaja drsenja, naslovnih pasov in izbire celice, ko uporabnik premika list, ki je veliko večji od vidnega okna, ter odločitev, kaj naj se v resnici zgodi ob kliku označevalnika komentarja ali celice s hiperpovezavo

Večina ekip Delphi poseže po pregledovalniku preglednic iz razlogov, ki nimajo nič skupnega z urejanjem: revizijska postaja, ki pred vstopom v cevovod predogleda naložene delovne zvezke, kiosk ali pregledovalnik poročil, kjer Microsoft Office ni del namestitvene slike, ali orodje za zagotavljanje kakovosti, ki mora prikazati vsebino delovnega zvezka brez nepredvidljivosti upravljanja pravega procesa Excel prek COM. Preprosta mreža nizov hitro prikaže besedilo v celicah, vendar datoteka preglednice ni preprosta mreža: celice se združujejo v bloke, ki v osnovnem modelu obstajajo šele po združitvi, listi vsebujejo stalne naslovne pasove ter neodvisna vodoravna in navpična položaja drsenja, posamezne celice pa vsebujejo komentarje in hiperpovezave, ki potrebujejo lasten model interakcije. TXLSWorkbookViewer je odgovor HotXLS na to vrzel, njegova notranja zasnova pa je razumna predloga za vsakogar, ki podoben kontrolnik izdeluje od začetka

Kako se pregledovalnik delovnega zvezka izogne odvisnosti od Excela

TXLSWorkbookViewer se Excelu popolnoma izogne tako, da bere prek lastnega razčlenjenega objektnega modela HotXLS, namesto da bi dokument odprl v Excelu in ga upravljal. Lastnost Workbook poveže obstoječi TXLSWorkbook za klasične datoteke XLS, XlsxWorkbook pa poveže TXLSXWorkbook za XLSX, XLSM in različice predlog; katera koli od njiju je lahko že odprta drugje v aplikaciji, pregledovalnik pa jo samo bere. Ko mora kontrolnik sam upravljati datoteko, LoadFromFile pregleda pripono, usmeri XLSX, XLSM, XLTX, XLTM in ODS prek sodobnega mehanizma, vse drugo pa prek klasičnega, nato pa sprosti delovni zvezek, ki ga je ustvaril, ko kontrolnik počistimo ali uničimo

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;

Iskanje prave celice znotraj združenega obsega

Razreševanje klika na pravilno celico v TXLSWorkbookViewer je dvostopenjsko iskanje, ta razdelitev pa je pomembna, ker sta geometrija slikovnih pik in semantika preglednice resnično različni težavi. Prva stopnja je čista geometrija: zasebna metoda CellAtPoint od trenutnega položaja drsenja prehaja čez širine stolpcev in višine vrstic, dokler ne najde pasu, ki vsebuje kliknjeno koordinato X in Y, pri tem pa sploh ne pozna združenih celic. Druga stopnja je semantična: vsaka pot, ki spremeni izbiro, klik miške, puščična tipka, Tab ali neposreden klic GoToCell, gre skozi eno notranjo rutino ChangeSelection, ki surovo vrstico in stolpec normalizira glede na morebitno združitev ter ju pred dejansko spremembo izbire postavi na sidrno celico združitve

Sidrna celica je zgornja leva celica združenega obsega in je edina celica v tem bloku, ki v osnovnem modelu delovnega zvezka dejansko vsebuje vrednost, oblikovanje, komentar ali hiperpovezavo; vsaka druga celica, ki jo združitev vizualno pokriva, je v samih podatkih prazna. Pri klasičnih delovnih zvezkih XLS sidro izhaja iz Cell.MergeArea, IXLSRange, katerega Row in Column kažeta na lastniško celico; pri delovnih zvezkih XLSX in ODS MergedCells.FindAt vrne TXLSXMergedRange, ki isto sidro izpostavlja kot Row1 in Col1. Risanje neodvisno rešuje enakovredno težavo, tako da pravokotnik združene celice razširi čez celoten razpon vrstic in stolpcev ter preskoči celice znotraj tega razpona, zato se oris izbire ovije okoli celotnega združenega bloka in ne le okoli njegovega sidrnega kota, zapisovanje združenih postavitev namesto njihovega samega branja pa je sorodna, vendar ločena težava, obravnavana v spremnem članku o postavitvi združenih celic za predloge poročil

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;

Kaj ohranja drsenje, glave in izbiro v sinhronizaciji

TXLSWorkbookViewer ohranja usklajene tri ločene dele stanja: logični položaj drsenja v TopRow in LeftCol, izvorna drsna trakova Windows, ki ju kontrolnik zahteva prek WS_HSCROLL in WS_VSCROLL v CreateParams, ter trenutno izbiro v SelectedRow in SelectedCol. Vlečenje drsnega traku ali vrtenje koleščka miške sproži WM_HSCROLL, WM_VSCROLL ali WM_MOUSEWHEEL, kar posodobi TopRow ali LeftCol in ponovno nariše vsebino; izbira se ne premakne, kar ustreza načinu, kako Excel sam ločuje premikanje pogleda od izbire. Po vsaki od teh posodobitev UpdateScrollBars potisne nov položaj nazaj v izvorni drsni trak prek SetScrollInfo, zato drsnik nikoli ne izgubi skladnosti s tem, kar mreža dejansko prikazuje

Navigacija s tipkovnico izvaja isto sinhronizacijo v nasprotni smeri: ko izbiro premaknete čez rob vidne mreže, EnsureSelectionVisible premakne TopRow ali LeftCol tako, da sešteva dejanske širine stolpcev in višine vrstic, namesto da bi jih preprosto povečal za ena, saj imajo vrstice in stolpci lahko prilagojene velikosti, nato pa pokliče UpdateScrollBars, da drsnik odraža mesto, kamor je tipkovnica pravkar premaknila pogled. Pasova z glavo številk vrstic in črk stolpcev, katerih velikost določata RowHeaderWidth in ColumnHeaderHeight, sta del tega kontrolnika, ki ostaja pritrjen na zaslonu, medtem ko TopRow in LeftCol pod njima premikata podatke, in to je celoten obseg zamrznitve, ki jo kontrolnik izvaja sam: to ni Excelova funkcija zamrznjenih podoken in ni vgrajenega načina za pripenjanje poljubne podatkovne vrstice ali stolpca, medtem ko preostali list drsi mimo. Ena meja, ki jo je vredno preizkusiti pred izdajo pregledovalnika za datoteke, nad katerimi nimate popolnega nadzora, je ta, da TopRow in LeftCol nista omejena glede na dejansko uporabljeni obseg delovnega lista, zato lahko drsnik, povlečen do strukturne meje, pristane v vrstici 1,048,576 ali stolpcu 16,384 in prikaže prazno mrežo namesto zadnje vrstice ali stolpca, ki dejansko vsebuje podatke; delovni zvezki, ki so dovolj veliki, da to postane opazno, so običajno dovolj veliki tudi za potrebo po pozornosti pri nalaganju, opisani v članku o zmogljivosti pri velikih delovnih zvezkih

Povezovanje komentarjev in hiperpovezav z dogodki miške in izbire

TXLSWorkbookViewer obravnava komentarje in hiperpovezave kot lastnosti trenutno izbrane celice, ne kot cilje ob prehodu miške, zato se SelectedCellCommentText, SelectedCellCommentAuthor in SelectedCellHyperlink posodobijo vsakič, ko se sproži OnSelectionChange, ne glede na to, ali se je izbira premaknila s klikom miške, puščično tipko ali klicem GoToCell. Komentirana celica dobi v zgornjem desnem kotu majhen rdeč trikotnik kot vizualni namig, podobno kot Excelova lastna zastavica komentarja, vendar je ta označevalnik zgolj vizualen; kontrolnik nima vgrajenega pojavnega opisa, sproženega ob prehodu miške, zato mora aplikacija, ki želi pojavno okno ob prehodu namesto ob izbiri, to plast izdelati sama. Aktiviranje hiperpovezave deluje na enak način, ki najprej izbere celico: dvojni klik celice pokliče ActivateSelectedCell, ki prebere SelectedCellHyperlink in, če ni prazna, sproži OnHyperlinkClick s ciljnim naslovom ter parametrom var Handled: Boolean, ki ga mora obravnavalnik nastaviti

Prav tako je pomembno, česa OnHyperlinkClick ne počne: TXLSWorkbookViewer nikoli ne pokliče ShellExecute in sam ne odpre brskalnika, ne glede na to, ali obravnavalnik nastavi Handled na true ali ga pusti na false. Navigacija in vsaka odločitev o tem, kaj šteje kot varen cilj, sta v celoti odgovornost gostiteljske aplikacije, kar je prava privzeta nastavitev za komponento, ki ne more vedeti, ali je vdelana v zaupanja vredno notranje orodje ali v pregledovalnik datotek, ki jih je pravkar naložil uporabnik

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;

Obseg izbire in omejitve navigacije s tipkovnico

Izbira v TXLSWorkbookViewer je vedno ena sama logična celica, sledita pa ji SelectedRow in SelectedCol; osnovni kontrolnik nima pravokotne izbire več celic, zato je treba vsako funkcijo, ki mora delovati na bloku celic, zgraditi nad njim in je ni mogoče prebrati iz objekta izbire. Podpora tipkovnici je namerno osnovna: puščične tipke premaknejo pogled za eno celico, Home se vrne na začetek vrstice ali s Ctrl v celico A1, Page Up in Page Down preskočita deset vrstic, Tab in Shift+Tab pa se premikata čez stolpce; skoka Ctrl+Arrow na rob podatkovnega območja in izbire razpona z razširitvijo Shift ni, zato bodo uporabniki, ki prihajajo neposredno iz Excela, vrzel opazili na gosti preglednici

Omejitve stolpcev se uveljavljajo na isti točki ChangeSelection, ki obravnava normalizacijo združitev, namenoma pa se med mehanizmoma razlikujejo: pregledovalnik, povezan s klasičnim TXLSWorkbook, se omeji pri stolpcu 256, strukturnem zgornjem robu formata BIFF8, medtem ko pregledovalnik, povezan s TXLSXWorkbook, upošteva sodobno omejitev 16,384 stolpcev, ki jo je XLSX podedoval od Excela 2007 naprej. Vrstice so v obeh primerih omejene na 1,048,576, zato je praktična razlika med odpiranjem starejše datoteke XLS in datoteke XLSX v istem pregledovalniku v celoti vprašanje, kako daleč v desno vam je mreža pripravljena dovoliti

Nič od tega ni nenavadno, ko se razčleni na iskanje po slikovnih pikah, normalizacijo sidra in nekaj obravnavalnikov sporočil, vendar je doseganje njihovega soglasja pri resničnih datotekah z resničnimi združitvami, komentarji in hiperpovezavami večina dela pri komponenti, kot je ta. TXLSWorkbookViewer je del standardne komponente HotXLS Excel Component za Delphi in C++Builder, skupaj s klasičnimi in XLSX objektnimi modeli, iz katerih upodablja