HotXLS distribuisce TXLSWorkbookViewer, un controllo VCL nativo che renderizza cartelle di lavoro XLS, XLSX, XLSM e ODS come una griglia di foglio di calcolo interattiva dentro una form Delphi o C++Builder, senza installare Excel o pilotarlo tramite automazione OLE. Costruire bene questo tipo di controllo significa risolvere tre problemi specifici: mappare un clic del mouse che finisce dentro una cella unita sulla cella logica corretta, mantenere coerenti la posizione di scroll, le bande di intestazione e la selezione della cella mentre un utente scorre un foglio molto più grande della finestra visibile, e decidere cosa dovrebbe realmente fare un clic su un marcatore di commento o su una cella con collegamento ipertestuale
La maggior parte delle software house Delphi ricorre a un visualizzatore di fogli di calcolo per motivi che non hanno nulla a che fare con la modifica: una stazione di audit che mostra in anteprima le cartelle di lavoro caricate prima che entrino in una pipeline, un chiosco o un visualizzatore di report dove Microsoft Office non fa parte dell'immagine di distribuzione, oppure uno strumento di QA che deve mostrare il contenuto di una cartella di lavoro senza l'imprevedibilità di automatizzare un vero processo Excel tramite COM. Una semplice griglia di stringhe ti dà rapidamente testo nelle celle, ma un file di foglio di calcolo non è una semplice griglia: le celle si uniscono in blocchi che esistono una sola volta nel modello sottostante, i fogli portano bande di intestazione fisse e posizioni di scroll orizzontale e verticale indipendenti, e le singole celle portano commenti e collegamenti ipertestuali che necessitano di un proprio modello di interazione. TXLSWorkbookViewer è la risposta di HotXLS a questa lacuna, e il suo design interno è un progetto ragionevole per chiunque costruisca un controllo simile da zero
Come evita un visualizzatore di cartelle di lavoro di dipendere da Excel?
TXLSWorkbookViewer evita del tutto Excel leggendo attraverso il proprio modello a oggetti analizzato di HotXLS invece di aprire un documento tramite Excel e pilotarlo come una marionetta. La proprietà Workbook collega un TXLSWorkbook esistente per i file XLS classici, e XlsxWorkbook collega un TXLSXWorkbook per XLSX, XLSM e varianti template; entrambi possono già essere aperti altrove nell'applicazione, e il visualizzatore si limita a leggerne. Quando il controllo deve possedere il file stesso, LoadFromFile ispeziona l'estensione, instrada XLSX, XLSM, XLTX, XLTM e ODS attraverso il motore moderno e tutto il resto attraverso quello classico, e libera qualunque cartella di lavoro abbia creato una volta che il controllo viene svuotato o distrutto
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;
Individuare la cella giusta dentro un intervallo unito
Risolvere un clic sulla cella corretta in TXLSWorkbookViewer è una ricerca a due fasi, e la separazione conta perché la geometria dei pixel e la semantica del foglio di calcolo sono problemi genuinamente diversi. La prima fase è pura geometria: un metodo privato CellAtPoint percorre larghezze di colonna e altezze di riga a partire dalla posizione di scroll corrente finché non trova la banda che contiene la coordinata X e Y cliccata, senza alcuna consapevolezza delle celle unite. La seconda fase è semantica: ogni percorso che cambia la selezione, un clic del mouse, un tasto freccia, Tab, o una chiamata diretta a GoToCell, confluisce in un'unica routine interna ChangeSelection, che normalizza la riga e la colonna grezze rispetto a qualunque unione e le fa scattare sulla cella di ancoraggio dell'unione prima che la selezione cambi realmente
L'ancora è la cella in alto a sinistra dell'intervallo unito, ed è l'unica cella in quel blocco che detiene genuinamente un valore, un formato, un commento o un collegamento ipertestuale nel modello di cartella di lavoro sottostante; ogni altra cella che l'unione copre visivamente è vuota nel dato stesso. Per le cartelle di lavoro XLS classiche l'ancora proviene da Cell.MergeArea, un IXLSRange il cui Row e Column puntano alla cella proprietaria; per le cartelle di lavoro XLSX e ODS, MergedCells.FindAt restituisce un TXLSXMergedRange che espone la stessa ancora come Row1 e Col1. Il disegno risolve un problema equivalente indipendentemente, espandendo il rettangolo di una cella unita al proprio pieno intervallo di righe e colonne e saltando le celle interne a quell'intervallo, cosicché il contorno di selezione avvolga l'intero blocco unito anziché solo il suo angolo di ancoraggio, e scrivere layout uniti anziché limitarsi a rileggerli è un problema correlato ma distinto trattato nell'articolo di approfondimento sul layout a celle unite per i modelli di report
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;
Cosa mantiene sincronizzati scroll, intestazioni e selezione?
TXLSWorkbookViewer mantiene coerenti tre elementi di stato separati: la posizione di scroll logica contenuta in TopRow e LeftCol, le barre di scorrimento native di Windows che il controllo richiede tramite WS_HSCROLL e WS_VSCROLL in CreateParams, e la selezione corrente in SelectedRow e SelectedCol. Trascinare una barra di scorrimento o girare la rotella del mouse genera WM_HSCROLL, WM_VSCROLL o WM_MOUSEWHEEL, che aggiornano TopRow o LeftCol e ridisegnano; la selezione non si sposta, il che corrisponde a come Excel stesso separa lo scorrimento dalla selezione. Dopo uno qualsiasi di questi aggiornamenti, UpdateScrollBars riporta la nuova posizione nella barra di scorrimento nativa tramite SetScrollInfo, cosicché il cursore della barra non finisca mai in disaccordo con ciò che la griglia sta effettivamente mostrando
La navigazione da tastiera esegue la stessa sincronizzazione in direzione opposta: spostare la selezione oltre il bordo della griglia visibile chiama EnsureSelectionVisible, che sposta TopRow o LeftCol accumulando le larghezze di colonna e le altezze di riga effettive anziché semplicemente incrementando di uno, poiché righe e colonne possono portare dimensioni personalizzate, e poi chiama UpdateScrollBars cosicché il cursore rifletta ovunque la tastiera abbia appena portato la vista. Le bande di intestazione con numero di riga e lettera di colonna, dimensionate tramite RowHeaderWidth e ColumnHeaderHeight, sono la parte di questo controllo che resta fissa sullo schermo mentre TopRow e LeftCol scorrono il dato sottostante, ed è questa l'estensione del blocco che questo controllo esegue autonomamente: non è la funzionalità Blocca Riquadri di Excel, e non c'è alcun modo integrato per fissare una riga o colonna di dati arbitraria mentre il resto del foglio scorre oltre. Un confine che vale la pena testare prima di distribuire un visualizzatore su file che non controlli pienamente è che TopRow e LeftCol non sono limitati contro l'effettivo intervallo usato del foglio di lavoro, quindi un cursore trascinato al proprio limite strutturale può atterrare sulla riga 1.048.576 o sulla colonna 16.384 e mostrare una griglia vuota invece dell'ultima riga o colonna che effettivamente contiene dati; le cartelle di lavoro abbastanza grandi da rendere questo evidente sono di solito anche abbastanza grandi da richiedere l'attenzione sul lato caricamento trattata nell'articolo sulle prestazioni delle cartelle di lavoro di grandi dimensioni
Collegare commenti e collegamenti ipertestuali agli eventi di mouse e selezione
TXLSWorkbookViewer tratta commenti e collegamenti ipertestuali come attributi di qualunque cella sia attualmente selezionata piuttosto che come target di passaggio del mouse, quindi SelectedCellCommentText, SelectedCellCommentAuthor e SelectedCellHyperlink si aggiornano ogni volta che OnSelectionChange si genera, che la selezione si sia spostata per clic del mouse, tasto freccia, o una chiamata a GoToCell. Una cella commentata riceve un piccolo triangolo rosso disegnato nell'angolo in alto a destra come indizio visivo, simile al proprio indicatore di commento di Excel, ma quel marcatore è puramente visivo; non esiste alcun tooltip attivato dal passaggio del mouse integrato nel controllo, quindi un'applicazione che desidera un popup al passaggio del mouse anziché alla selezione deve costruire da sé quel livello. L'attivazione dei collegamenti ipertestuali funziona nello stesso modo basato sulla selezione: fare doppio clic su una cella chiama ActivateSelectedCell, che legge SelectedCellHyperlink e, se non è vuoto, genera OnHyperlinkClick con l'indirizzo target e un parametro var Handled: Boolean che il gestore può impostare
Ciò che OnHyperlinkClick non fa è altrettanto importante: TXLSWorkbookViewer non chiama mai ShellExecute né apre un browser da sé, indipendentemente dal fatto che il gestore imposti Handled su vero o lo lasci falso. La navigazione, e qualsiasi decisione su cosa conti come target sicuro, è interamente responsabilità dell'applicazione ospitante, il che è il comportamento predefinito corretto per un componente che non ha idea se sia incorporato in uno strumento interno affidabile o in un visualizzatore per file che un cliente ha appena caricato
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;
Ambito della selezione e limiti della navigazione da tastiera
La selezione in TXLSWorkbookViewer è sempre una singola cella logica, tracciata come SelectedRow e SelectedCol; non esiste alcuna selezione di intervallo multi-cella rettangolare nel controllo di base, quindi qualsiasi funzionalità che debba agire su un blocco di celle deve essere costruita sopra di esso invece di leggersi da un oggetto di selezione. La copertura da tastiera è deliberatamente di base: i tasti freccia spostano una cella alla volta, Home ritorna all'inizio della riga o, con Ctrl, alla cella A1, Pagina Su e Pagina Giù saltano dieci righe, e Tab e Shift+Tab avanzano tra le colonne; non c'è alcun salto Ctrl+Freccia al bordo di una regione dati e nessuna selezione di intervallo estesa con Shift, quindi gli utenti che arrivano direttamente da Excel noteranno la lacuna su un foglio denso
I limiti di colonna sono imposti nello stesso punto di strozzatura ChangeSelection che gestisce la normalizzazione delle unioni, e differiscono per motore apposta: un visualizzatore collegato a un TXLSWorkbook classico si blocca alla colonna 256, il tetto strutturale del formato BIFF8, mentre uno collegato a TXLSXWorkbook rispetta il limite moderno di 16.384 colonne che XLSX ha ereditato da Excel 2007 in poi. Le righe sono limitate a 1.048.576 in entrambi i casi, quindi la differenza pratica tra aprire un file XLS legacy e un file XLSX nello stesso visualizzatore riguarda interamente quanto lontano verso destra la griglia sia disposta a lasciarti andare
Nulla di tutto ciò è esotico una volta scomposto in ricerca di pixel, normalizzazione dell'ancora e una manciata di gestori di messaggi, ma far concordare i tre su file reali, con unioni, commenti e collegamenti ipertestuali reali, è gran parte del lavoro in un componente come questo. TXLSWorkbookViewer fa parte del componente Excel HotXLS standard per Delphi e C++Builder, insieme ai modelli a oggetti classico e XLSX da cui renderizza