Műszaki cikk

Egyéni táblázat-grid Delphiben a HotXLS-szel

A HotXLS szállítja a TXLSWorkbookViewer-t, egy natív VCL-vezérlőt, amely XLS-, XLSX-, XLSM-, és ODS-munkafüzeteket rendel interaktív táblázat-griddé egy Delphi vagy C++Builder formon belül, Excel telepítése vagy OLE-automatizáción keresztüli vezérlése nélkül. Egy ilyen vezérlő jó felépítése három konkrét probléma megoldását jelenti: egy olyan egérkattintás leképezését, amely egy összevont cellán belül landol, a helyes logikai cellára, a görgetési pozíció, fejlécsávok, és cellakijelölés konzisztensen tartását, miközben egy felhasználó egy, a látható ablaknál sokkal nagyobb lapot pásztáz, és annak eldöntését, mit is kellene ténylegesen tennie egy kattintásnak egy megjegyzésjelölőn vagy egy hivatkozás-cellán

A legtöbb Delphi-műhely olyan okokból nyúl egy táblázat-megjelenítőhöz, amelyeknek semmi közük a szerkesztéshez: egy audit-állomás, amely feltöltött munkafüzeteket néz meg előzetesen, mielőtt azok belépnének egy csővezetékbe, egy kiosk vagy jelentés-megjelenítő, ahol a Microsoft Office nem része a telepítési képnek, vagy egy minőségbiztosítási eszköz, amelynek meg kell mutatnia egy munkafüzet tartalmát egy valódi Excel-folyamat COM-on keresztüli automatizálásának kiszámíthatatlansága nélkül. Egy egyszerű sztring-grid gyorsan szöveget ad neked cellákban, de egy táblázatfájl nem egyszerű grid: a cellák blokkokká olvadnak össze, amelyek csak egyszer léteznek az alapul szolgáló modellben, a lapok fix fejlécsávokat és független vízszintes és függőleges görgetési pozíciókat hordoznak, és az egyes cellák megjegyzéseket és hivatkozásokat hordoznak, amelyeknek saját interakciós modellre van szükségük. A TXLSWorkbookViewer a HotXLS válasza erre a résre, és belső felépítése ésszerű tervrajz bárkinek, aki egy hasonló vezérlőt épít nulláról

Hogyan kerüli el egy munkafüzet-megjelenítő az Excel-függőséget?

A TXLSWorkbookViewer teljesen elkerüli az Excelt azáltal, hogy a HotXLS saját elemzett objektummodelljén keresztül olvas, ahelyett hogy egy dokumentumot az Excelen keresztül nyitna meg, és bábuként vezérelné. A Workbook tulajdonság egy meglévő TXLSWorkbook-ot köt a klasszikus XLS-fájlokhoz, és az XlsxWorkbook egy TXLSXWorkbook-ot köt az XLSX-, XLSM-, és sablon-variánsokhoz; bármelyik már nyitva lehet máshol az alkalmazásban, és a megjelenítő csak olvas belőle. Amikor a vezérlőnek magának kellene birtokolnia a fájlt, a LoadFromFile megvizsgálja a kiterjesztést, az XLSX-et, XLSM-et, XLTX-et, XLTM-et, és ODS-t a modern motoron keresztül irányítja, mindent mást a klasszikuson, és felszabadítja, bármelyik munkafüzetet is hozta létre, amint a vezérlő törlődik vagy megsemmisül

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;

A helyes cella megkeresése egy összevont tartományon belül

Egy kattintás helyes cellára feloldása a TXLSWorkbookViewer-ben egy kétlépcsős keresés, és a felosztás azért számít, mert a pixel-geometria és a táblázat-szemantika valóban különböző problémák. Az első lépcső tiszta geometria: egy privát CellAtPoint metódus bejárja az oszlopszélességeket és sormagasságokat az aktuális görgetési pozíciótól, amíg megtalálja azt a sávot, amely tartalmazza a kattintott X és Y koordinátát, teljesen az összevont cellák ismerete nélkül. A második lépcső szemantikai: minden útvonal, amely megváltoztatja a kijelölést, egy egérkattintás, egy nyílbillentyű, a Tab, vagy egy közvetlen hívás a GoToCell-ra, egyetlen belső ChangeSelection rutinon keresztül tölcsérezik, amely normalizálja a nyers sort és oszlopot bármely összevonás ellen, és a kijelölés tényleges megváltozása előtt az összevonás horgonycellájára kattintja azokat

A horgony az összevont tartomány bal felső cellája, és ez az egyetlen cella abban a blokkban, amely ténylegesen tart egy értéket, egy formátumot, egy megjegyzést, vagy egy hivatkozást az alapul szolgáló munkafüzet-modellben; minden más cella, amelyet az összevonás vizuálisan lefed, üres magában az adatban. Klasszikus XLS-munkafüzeteknél a horgony a Cell.MergeArea-ból származik, egy IXLSRange-ből, amelynek Row-ja és Column-ja a birtokló cellára mutat; XLSX- és ODS-munkafüzeteknél a MergedCells.FindAt ad vissza egy TXLSXMergedRange-et, amely ugyanazt a horgonyt teszi elérhetővé Row1-ként és Col1-ként. A festés egy ezzel egyenértékű problémát old meg függetlenül, egy összevont cella téglalapját a teljes sor- és oszlopkiterjedésére bővítve, és kihagyva a cellákat azon a kiterjedésen belül, így a kijelölési körvonal a teljes összevont blokkot körülöleli, nem csak a horgonysarkát, és az összevont elrendezések írása, nem csak azok visszaolvasása, egy rokon, de különálló probléma, amelyet a jelentéssablonok összevont-cella elrendezéséről szóló kísérőcikk tárgyal

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;

Mi tartja szinkronban a görgetést, a fejléceket, és a kijelölést?

A TXLSWorkbookViewer három különálló állapotdarabot tart koherensen: a logikai görgetési pozíciót a TopRow-ban és LeftCol-ban tartva, a natív Windows görgetősávokat, amelyeket a vezérlő a WS_HSCROLL-on és WS_VSCROLL-on keresztül kér a CreateParams-ban, és az aktuális kijelölést a SelectedRow-ban és SelectedCol-ban. Egy görgetősáv húzása vagy az egérgörgő pörgetése kiváltja a WM_HSCROLL-t, a WM_VSCROLL-t, vagy a WM_MOUSEWHEEL-t, amelyek frissítik a TopRow-t vagy a LeftCol-t, és újrarajzolnak; a kijelölés nem mozdul, ami illeszkedik ahhoz, ahogyan maga az Excel is elkülöníti a pásztázást a kijelöléstől. Bármelyik ilyen frissítés után az UpdateScrollBars visszatolja az új pozíciót a natív görgetősávba a SetScrollInfo-n keresztül, így a jelző soha nem sodródik szét attól, amit a grid ténylegesen mutat

A billentyűzet-navigáció ugyanazt a szinkronizálást futtatja ellentétes irányban: a kijelölés mozgatása a látható grid szélén túlra meghívja az EnsureSelectionVisible-t, amely eltolja a TopRow-t vagy a LeftCol-t a tényleges oszlopszélességek és sormagasságok összegzésével, nem pedig egyszerűen eggyel növelve, mivel sorok és oszlopok egyéni méreteket hordozhatnak, majd meghívja az UpdateScrollBars-t, így a jelző tükrözi, ahová a billentyűzet éppen vitte a nézetet. A sorszám- és oszlopbetű-fejlécsávok, a RowHeaderWidth és ColumnHeaderHeight révén méretezve, azok a részei ennek a vezérlőnek, amelyek fixen maradnak a képernyőn, miközben a TopRow és a LeftCol görgeti alattuk az adatot, és ez az a mérték, ameddig ez a vezérlő önmagában "rögzít": ez nem az Excel Ablaktábla rögzítése funkciója, és nincs beépített mód egy tetszőleges adatsor vagy oszlop rögzítésére, miközben a lap többi része elgördül mellette. Egy határ, amit érdemes tesztelni, mielőtt egy megjelenítőt olyan fájlok fölé szállítunk, amelyeket nem teljesen kontrollálunk, az, hogy a TopRow és a LeftCol nincs a munkalap tényleges használt tartományához szorítva, így egy jelző, amelyet a strukturális határáig húznak, landolhat az 1 048 576. soron vagy a 16 384. oszlopon, és üres gridet mutathat az utolsó sor vagy oszlop helyett, amely ténylegesen adatot tart; azok a munkafüzetek, amelyek elég nagyok ahhoz, hogy ez észrevehető legyen, általában elég nagyok ahhoz is, hogy szükségük legyen a nagy munkafüzet teljesítményéről szóló cikkben tárgyalt betöltési-oldali figyelemre

Megjegyzések és hivatkozások bekötése egér- és kijelölési eseményekbe

A TXLSWorkbookViewer a megjegyzéseket és hivatkozásokat annak a cellának a tulajdonságaiként kezeli, amely éppen ki van jelölve, nem pedig hover-célként, így a SelectedCellCommentText, SelectedCellCommentAuthor, és SelectedCellHyperlink minden alkalommal frissül, amikor az OnSelectionChange kiváltódik, akár egérkattintással, nyílbillentyűvel, vagy a GoToCell hívásával mozdult el a kijelölés. Egy megjegyzéssel ellátott cella egy kis piros háromszöget kap a jobb felső sarkába vizuális jelzésként, hasonlóan az Excel saját megjegyzés-jelzőjéhez, de ez a jelölő tisztán vizuális; nincs beépített, hover-kiváltott tooltip a vezérlőben, így egy alkalmazásnak, amely felugró ablakot akar egér-fölé-vitelre, nem kijelölésre, magának kell felépítenie azt a réteget. A hivatkozás-aktiválás ugyanúgy kijelölés-elsőbbséges módon működik: egy cellára való dupla kattintás meghívja az ActivateSelectedCell-t, amely beolvassa a SelectedCellHyperlink-et, és ha az nem üres, kiváltja az OnHyperlinkClick-ot a célcímmel és egy var Handled: Boolean paraméterrel a kezelő beállításához

Amit az OnHyperlinkClick nem tesz, éppolyan fontos: a TXLSWorkbookViewer soha nem hívja meg a ShellExecute-t, és nem nyit meg egy böngészőt önmagában, függetlenül attól, hogy a kezelő true-ra állítja-e a Handled-et, vagy false-on hagyja. A navigáció, és bármely döntés arról, mi számít biztonságos célnak, teljesen a gazdaalkalmazás felelőssége, ami a helyes alapértelmezés egy olyan komponens számára, amelynek fogalma sincs arról, egy megbízható belső eszközbe van-e beágyazva, vagy egy megjelenítőbe olyan fájlokhoz, amelyeket egy ügyfél épp feltöltött

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;

Kijelölési hatókör és billentyűzet-navigációs korlátok

A kijelölés a TXLSWorkbookViewer-ben mindig egyetlen logikai cella, SelectedRow-ként és SelectedCol-ként nyomon követve; nincs téglalap alakú, több cellás tartománykijelölés az alapvezérlőben, így bármely funkciónak, amelynek egy cellablokkon kell működnie, arra kell épülnie, nem egy kijelölésobjektumból kiolvasnia. A billentyűzet-lefedettség szándékosan alapszintű: a nyílbillentyűk egyszerre egy cellát mozgatnak, a Home visszatér a sor elejére, vagy Ctrl-lal az A1 cellához, a Page Up és Page Down tíz sort ugrik, és a Tab és Shift+Tab oszlopok között lépked; nincs Ctrl+Nyíl ugrás egy adatterület széléhez, és nincs Shift-tel kiterjesztett tartománykijelölés, így az Excelből egyenesen érkező felhasználók észreveszik a rést egy sűrű lapon

Az oszlopkorlátok ugyanazon a ChangeSelection szűkületnél kényszerítve vannak, amely az összevonás-normalizálást kezeli, és szándékosan eltérnek motoronként: egy klasszikus TXLSWorkbook-hoz kötött megjelenítő a 256. oszlopnál szorít, a BIFF8-formátum strukturális plafonjánál, míg egy TXLSXWorkbook-hoz kötött tiszteletben tartja a modern, 16 384 oszlopos korlátot, amelyet az XLSX örökölt az Excel 2007-től kezdve. A sorok mindkét esetben 1 048 576-nál vannak korlátozva, így a gyakorlati különbség egy örökölt XLS-fájl és egy XLSX-fájl ugyanabban a megjelenítőben történő megnyitása között teljes egészében arról szól, milyen messzire jobbra hajlandó engedni a grid

Ebből semmi nem egzotikus, amint pixel-keresésre, horgony-normalizálásra, és néhány üzenetkezelőre bontjuk, de azt, hogy a három egyetértsen valódi fájlok, valódi összevonások, megjegyzések, és hivatkozások mellett, az egy ilyen komponens munkájának nagy része. A TXLSWorkbookViewer a Delphihez és C++Builderhez készült szabványos HotXLS Excel Komponens részeként érkezik, a klasszikus és XLSX objektummodellek mellett, amelyekből rendel