HotXLS leverer TXLSWorkbookViewer, en native VCL-kontrol der gengiver XLS-, XLSX-, XLSM- og ODS-projektmapper som et interaktivt regnearksgrid inde i en Delphi- eller C++Builder-formular, uden at installere Excel eller drive det gennem OLE-automatisering. At bygge den slags kontrol godt betyder at løse tre specifikke problemer: at mappe et museklik, der lander inde i en sammenlagt celle, til den korrekte logiske celle, at holde scrollposition, header-bånd og cellemarkering konsistente, mens en bruger panorerer et ark langt større end det synlige vindue, og at afgøre, hvad et klik på en kommentarmarkør eller en hyperlink-celle rent faktisk skal gøre
De fleste Delphi-shops griber til en regnearksfremviser af årsager, der intet har at gøre med redigering: en audit-station der forhåndsviser uploadede projektmapper, før de kommer ind i en pipeline, en kiosk- eller rapportfremviser hvor Microsoft Office ikke er en del af udrulningsimaget, eller et QA-værktøj der har brug for at vise en projektmappes indhold uden uforudsigeligheden ved at automatisere en rigtig Excel-proces over COM. Et almindeligt strenggrid giver dig tekst i celler hurtigt, men en regnearksfil er ikke et almindeligt grid: celler sammenlægges til blokke, der kun findes én gang i den underliggende model, ark bærer faste header-bånd og uafhængige vandrette og lodrette scrollpositioner, og individuelle celler bærer kommentarer og hyperlinks, der har brug for deres egen interaktionsmodel. TXLSWorkbookViewer er HotXLS' svar på det hul, og dens interne design er en fornuftig blueprint for alle, der bygger en lignende kontrol fra bunden
Hvordan undgår en projektmappe-fremviser at være afhængig af Excel?
TXLSWorkbookViewer undgår helt Excel ved at læse gennem HotXLS' egen parsede objektmodel frem for at åbne et dokument gennem Excel og styre det som en marionet. Workbook-egenskaben binder en eksisterende TXLSWorkbook til klassiske XLS-filer, og XlsxWorkbook binder en TXLSXWorkbook til XLSX-, XLSM- og skabelon-varianter; hvilken som helst kan allerede være åben andre steder i applikationen, og fremviseren læser kun fra den. Når kontrollen selv skal eje filen, undersøger LoadFromFile filtypen, dirigerer XLSX, XLSM, XLTX, XLTM og ODS gennem den moderne motor og alt andet gennem den klassiske en, og frigiver hvilken som helst projektmappe, den oprettede, når kontrollen ryddes eller destrueres
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;
At finde den rigtige celle inde i et sammenlagt interval
At løse et klik til den korrekte celle i TXLSWorkbookViewer er et to-trins-opslag, og opdelingen betyder noget, fordi pixel-geometri og regneark-semantik reelt er forskellige problemer. Det første trin er ren geometri: en privat CellAtPoint-metode gennemgår kolonnebredder og rækkehøjder fra den aktuelle scrollposition, indtil den finder det bånd, der indeholder den klikkede X- og Y-koordinat, uden nogen bevidsthed om sammenlagte celler overhovedet. Det andet trin er semantisk: hver vej der ændrer markeringen, et museklik, en piletast, Tab, eller et direkte kald til GoToCell, render gennem én intern ChangeSelection-rutine, som normaliserer den rå række og kolonne mod enhver sammenlægning og snapper dem til sammenlægningens ankercelle, før markeringen rent faktisk ændres
Ankeret er den øverste venstre celle af det sammenlagte interval, og det er den eneste celle i den blok, der reelt holder en værdi, et format, en kommentar eller et hyperlink i den underliggende projektmappe-model; hver anden celle, sammenlægningen visuelt dækker, er tom i selve dataene. For klassiske XLS-projektmapper kommer ankeret fra Cell.MergeArea, en IXLSRange hvis Row og Column peger på den ejende celle; for XLSX- og ODS-projektmapper returnerer MergedCells.FindAt en TXLSXMergedRange, der eksponerer det samme anker som Row1 og Col1. Tegning løser et tilsvarende problem uafhængigt, udvider en sammenlagt celles rektangel til sit fulde række- og kolonnespænd og springer cellerne inden i det spænd over, så markeringsomridset omslutter hele den sammenlagte blok frem for kun dens ankerhjørne, og at skrive sammenlagte layouts frem for bare at læse dem tilbage er et beslægtet, men distinkt problem dækket i følgeartiklen om sammenlagt-celle-layout til rapportskabeloner
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;
Hvad holder scrolling, headers og markering synkroniseret?
TXLSWorkbookViewer holder tre separate stykker tilstand koherente: den logiske scrollposition holdt i TopRow og LeftCol, de native Windows-scrollbars kontrollen anmoder om gennem WS_HSCROLL og WS_VSCROLL i CreateParams, og den aktuelle markering i SelectedRow og SelectedCol. At trække en scrollbar eller dreje musehjulet udløser WM_HSCROLL, WM_VSCROLL eller WM_MOUSEWHEEL, som opdaterer TopRow eller LeftCol og gentegner; markeringen flytter sig ikke, hvilket matcher, hvordan Excel selv adskiller panorering fra markering. Efter enhver af de opdateringer skubber UpdateScrollBars den nye position tilbage ind i den native scrollbar gennem SetScrollInfo, så tommelfingeren aldrig driver ud af overensstemmelse med, hvad gridet rent faktisk viser
Tastaturnavigation kører den samme synkronisering i den modsatte retning: at flytte markeringen forbi kanten af det synlige grid kalder EnsureSelectionVisible, som skubber TopRow eller LeftCol ved at akkumulere faktiske kolonnebredder og rækkehøjder frem for simpelthen at øge med én, da rækker og kolonner kan bære brugerdefinerede størrelser, og derefter kalder UpdateScrollBars, så tommelfingeren afspejler, hvor tastaturet lige har taget visningen hen. Række-nummer- og kolonne-bogstav-header-båndene, dimensioneret gennem RowHeaderWidth og ColumnHeaderHeight, er den del af denne kontrol, der forbliver fast på skærmen, mens TopRow og LeftCol scroller dataene under, og det er omfanget af fastfrysning, denne kontrol gør på egen hånd: det er ikke Excels Frys Ruder-funktion, og der er ingen indbygget måde at fastgøre en vilkårlig datarække eller -kolonne, mens resten af arket scroller forbi den. Én grænse værd at teste, før man udruller en fremviser over filer, man ikke fuldt ud kontrollerer, er, at TopRow og LeftCol ikke er begrænset mod regnearkets faktiske brugte interval, så en tommelfinger trukket til sin strukturelle grænse kan lande på række 1.048.576 eller kolonne 16.384 og vise et tomt grid i stedet for den sidste række eller kolonne, der rent faktisk holder data; projektmapper store nok til at gøre det bemærkelsesværdigt er sædvanligvis også store nok til at have brug for den indlæsnings-side-opmærksomhed dækket i artiklen om ydeevne ved store projektmapper
At koble kommentarer og hyperlinks til muse- og markeringshændelser
TXLSWorkbookViewer behandler kommentarer og hyperlinks som egenskaber ved uanset hvilken celle der aktuelt er markeret, frem for som hover-mål, så SelectedCellCommentText, SelectedCellCommentAuthor og SelectedCellHyperlink opdateres hver gang OnSelectionChange udløses, uanset om markeringen flyttede sig ved museklik, piletast eller et kald til GoToCell. En kommenteret celle får en lille rød trekant tegnet i sit øverste højre hjørne som et visuelt cue, ligesom Excels eget kommentarflag, men den markør er rent visuel; der er ingen hover-udløst tooltip indbygget i kontrollen, så en applikation der vil have en popup ved museover frem for ved markering, må bygge det lag selv. Hyperlink-aktivering fungerer på samme markerings-først-måde: dobbeltklik på en celle kalder ActivateSelectedCell, som læser SelectedCellHyperlink og, hvis den ikke er tom, udløser OnHyperlinkClick med målets adresse og en var Handled: Boolean-parameter, handleren skal sætte
Hvad OnHyperlinkClick ikke gør, er lige så vigtigt: TXLSWorkbookViewer kalder aldrig ShellExecute eller åbner en browser på egen hånd, uanset om handleren sætter Handled til true eller lader den være false. Navigation, og enhver beslutning om, hvad der tæller som et sikkert mål, er helt værtsapplikationens ansvar, hvilket er den rigtige standard for en komponent, der ikke har nogen anelse om, hvorvidt den er indlejret i et betroet internt værktøj eller en fremviser til filer, en kunde lige har uploadet
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;
Markeringsomfang og grænser for tastaturnavigation
Markering i TXLSWorkbookViewer er altid én enkelt logisk celle, sporet som SelectedRow og SelectedCol; der er ingen rektangulær multi-celle-intervalmarkering i basiskontrollen, så enhver funktion der har brug for at handle på en blok af celler, må bygges oven på den frem for at læses fra et markeringsobjekt. Tastaturdækning er bevidst basal: piletaster flytter én celle ad gangen, Home returnerer til starten af rækken eller, med Ctrl, til celle A1, Page Up og Page Down hopper ti rækker, og Tab og Shift+Tab trin på tværs af kolonner; der er intet Ctrl+Pil-hop til kanten af en dataregion og ingen Shift-udvidet intervalmarkering, så brugere der kommer direkte fra Excel, vil bemærke hullet på et tæt ark
Kolonnegrænser håndhæves ved det samme ChangeSelection-flaskehalspunkt, der håndterer sammenlægnings-normalisering, og de adskiller sig efter motor med vilje: en fremviser bundet til en klassisk TXLSWorkbook begrænser ved kolonne 256, det strukturelle loft for BIFF8-formatet, mens en bundet til TXLSXWorkbook respekterer den moderne 16.384-kolonne-grænse, XLSX arvede fra Excel 2007 og fremefter. Rækker er begrænset til 1.048.576 begge veje, så den praktiske forskel mellem at åbne en legacy-XLS-fil og en XLSX-fil i den samme fremviser handler helt om, hvor langt til højre gridet er villig til at lade dig gå
Intet af dette er eksotisk, når det først er brudt ned til pixel-opslag, anker-normalisering og en håndfuld beskedhandlere, men at få de tre til at være enige under rigtige filer, med rigtige sammenlægninger, kommentarer og hyperlinks, er det meste af arbejdet i en komponent som denne. TXLSWorkbookViewer leveres som en del af standard-HotXLS-Excel-komponenten til Delphi og C++Builder, sammen med de klassiske og XLSX-objektmodeller, den gengiver fra