HotXLS incluye TXLSWorkbookViewer, un control VCL nativo que renderiza libros XLS, XLSX, XLSM, y ODS como una grilla de hoja de cálculo interactiva dentro de un formulario Delphi o C++Builder, sin instalar Excel ni controlarlo mediante automatización OLE. Construir bien ese tipo de control significa resolver tres problemas específicos: mapear un clic de mouse que aterriza dentro de una celda combinada a la celda lógica correcta, mantener consistentes la posición de desplazamiento, las bandas de encabezado, y la selección de celda mientras un usuario recorre una hoja mucho más grande que la ventana visible, y decidir qué debería hacer realmente un clic en un marcador de comentario o una celda con hipervínculo
La mayoría de las empresas Delphi recurren a un visor de hojas de cálculo por razones que no tienen nada que ver con la edición: una estación de auditoría que previsualiza libros subidos antes de que entren a un pipeline, un kiosco o visor de informes donde Microsoft Office no forma parte de la imagen de despliegue, o una herramienta de control de calidad que necesita mostrar el contenido de un libro sin la imprevisibilidad de automatizar un proceso real de Excel a través de COM. Una simple grilla de cadenas de texto da texto en celdas rápidamente, pero un archivo de hoja de cálculo no es una grilla simple: las celdas se combinan en bloques que solo existen una vez en el modelo subyacente, las hojas llevan bandas de encabezado fijas y posiciones de desplazamiento horizontal y vertical independientes, y las celdas individuales llevan comentarios e hipervínculos que necesitan su propio modelo de interacción. TXLSWorkbookViewer es la respuesta de HotXLS a esa brecha, y su diseño interno es un plano razonable para cualquiera que construya un control similar desde cero
¿Cómo evita un visor de libros depender de Excel?
TXLSWorkbookViewer evita Excel por completo leyendo a través del propio modelo de objetos analizado de HotXLS en lugar de abrir un documento a través de Excel y controlarlo como marioneta. La propiedad Workbook vincula un TXLSWorkbook existente para archivos XLS clásicos, y XlsxWorkbook vincula un TXLSXWorkbook para variantes XLSX, XLSM, y plantilla; cualquiera de los dos ya puede estar abierto en otra parte de la aplicación, y el visor solo lee de él. Cuando el control debe poseer el archivo en sí, LoadFromFile inspecciona la extensión, dirige XLSX, XLSM, XLTX, XLTM, y ODS a través del motor moderno y todo lo demás a través del clásico, y libera cualquier libro que haya creado en cuanto el control se limpia o se destruye
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;
Ubicar la celda correcta dentro de un rango combinado
Resolver un clic a la celda correcta en TXLSWorkbookViewer es una búsqueda en dos etapas, y la división importa porque la geometría de píxeles y la semántica de hoja de cálculo son problemas genuinamente distintos. La primera etapa es geometría pura: un método privado CellAtPoint recorre los anchos de columna y las alturas de fila desde la posición de desplazamiento actual hasta que encuentra la banda que contiene la coordenada X e Y del clic, sin ninguna conciencia de celdas combinadas en absoluto. La segunda etapa es semántica: cada ruta que cambia la selección, un clic de mouse, una tecla de flecha, Tab, o una llamada directa a GoToCell, se canaliza a través de una única rutina interna ChangeSelection, que normaliza la fila y columna crudas contra cualquier combinación y las ajusta a la celda ancla de la combinación antes de que la selección realmente cambie
El ancla es la celda superior izquierda del rango combinado, y es la única celda de ese bloque que genuinamente contiene un valor, un formato, un comentario, o un hipervínculo en el modelo de libro subyacente; cada otra celda que la combinación cubre visualmente está vacía en los datos mismos. Para libros XLS clásicos, el ancla viene de Cell.MergeArea, un IXLSRange cuyos Row y Column apuntan a la celda propietaria; para libros XLSX y ODS, MergedCells.FindAt devuelve un TXLSXMergedRange que expone la misma ancla como Row1 y Col1. El pintado resuelve un problema equivalente de forma independiente, expandiendo el rectángulo de una celda combinada a su extensión completa de fila y columna y omitiendo las celdas dentro de esa extensión, así que el contorno de selección envuelve todo el bloque combinado en lugar de solo su esquina ancla, y escribir diseños combinados en lugar de solo leerlos de vuelta es un problema relacionado pero distinto cubierto en el artículo complementario sobre diseño de celdas combinadas para plantillas de informe
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;
¿Qué mantiene sincronizados el desplazamiento, los encabezados y la selección?
TXLSWorkbookViewer mantiene coherentes tres piezas de estado separadas: la posición de desplazamiento lógica contenida en TopRow y LeftCol, las barras de desplazamiento nativas de Windows que el control solicita mediante WS_HSCROLL y WS_VSCROLL en CreateParams, y la selección actual en SelectedRow y SelectedCol. Arrastrar una barra de desplazamiento o girar la rueda del mouse dispara WM_HSCROLL, WM_VSCROLL, o WM_MOUSEWHEEL, que actualizan TopRow o LeftCol y repintan; la selección no se mueve, lo que coincide con cómo el propio Excel separa el desplazamiento panorámico de la selección. Después de cualquiera de esas actualizaciones, UpdateScrollBars empuja la nueva posición de vuelta a la barra de desplazamiento nativa mediante SetScrollInfo, así que el thumb (el control deslizante) nunca se desvía de lo que la grilla realmente está mostrando
La navegación por teclado ejecuta la misma sincronización en la dirección opuesta: mover la selección más allá del borde de la grilla visible llama a EnsureSelectionVisible, que empuja TopRow o LeftCol acumulando anchos de columna y alturas de fila reales en lugar de simplemente incrementar de a uno, ya que las filas y columnas pueden llevar tamaños personalizados, y luego llama a UpdateScrollBars para que el thumb refleje adonde sea que el teclado acaba de llevar la vista. Las bandas de encabezado de número de fila y letra de columna, dimensionadas mediante RowHeaderWidth y ColumnHeaderHeight, son la parte de este control que permanece fija en pantalla mientras TopRow y LeftCol desplazan los datos por debajo, y eso es todo lo que este control hace por sí solo en materia de congelamiento: no es la característica Inmovilizar Paneles de Excel, y no hay ninguna forma integrada de fijar una fila o columna de datos arbitraria mientras el resto de la hoja se desplaza más allá de ella. Un límite que vale la pena probar antes de enviar un visor sobre archivos que no controla completamente es que TopRow y LeftCol no están acotados contra el rango realmente usado de la hoja de cálculo, así que un thumb arrastrado a su límite estructural puede aterrizar en la fila 1.048.576 o la columna 16.384 y mostrar una grilla en blanco en lugar de la última fila o columna que realmente contiene datos; los libros lo bastante grandes como para que eso sea notable normalmente también son lo bastante grandes como para necesitar la atención del lado de la carga cubierta en el artículo sobre rendimiento de libros grandes
Conectar comentarios e hipervínculos a eventos de mouse y selección
TXLSWorkbookViewer trata los comentarios e hipervínculos como atributos de cualquiera que sea la celda actualmente seleccionada en lugar de como objetivos de hover, así que SelectedCellCommentText, SelectedCellCommentAuthor, y SelectedCellHyperlink se actualizan cada vez que se dispara OnSelectionChange, ya sea que la selección se haya movido por clic de mouse, tecla de flecha, o una llamada a GoToCell. Una celda comentada recibe un pequeño triángulo rojo pintado en su esquina superior derecha como señal visual, similar a la propia bandera de comentario de Excel, pero ese marcador es puramente visual; no hay ningún tooltip activado por hover integrado en el control, así que una aplicación que quiera un popup al pasar el mouse en lugar de al seleccionar tiene que construir esa capa ella misma. La activación de hipervínculo funciona de la misma manera basada en selección primero: hacer doble clic en una celda llama a ActivateSelectedCell, que lee SelectedCellHyperlink y, si no está vacío, dispara OnHyperlinkClick con la dirección de destino y un parámetro var Handled: Boolean para que el manejador lo establezca
Lo que OnHyperlinkClick no hace es igual de importante: TXLSWorkbookViewer nunca llama a ShellExecute ni abre un navegador por sí mismo, sin importar si el manejador establece Handled en verdadero o lo deja en falso. La navegación, y cualquier decisión sobre qué cuenta como un destino seguro, es enteramente responsabilidad de la aplicación anfitriona, que es el valor predeterminado correcto para un componente que no tiene idea de si está incrustado en una herramienta interna de confianza o un visor para archivos que un cliente acaba de subir
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;
Alcance de la selección y límites de navegación por teclado
La selección en TXLSWorkbookViewer siempre es una única celda lógica, rastreada como SelectedRow y SelectedCol; no hay selección de rango rectangular multi-celda en el control base, así que cualquier característica que necesite actuar sobre un bloque de celdas tiene que construirse por encima de él en lugar de leerse de un objeto de selección. La cobertura de teclado es deliberadamente básica: las teclas de flecha mueven una celda a la vez, Inicio regresa al comienzo de la fila o, con Ctrl, a la celda A1, Re Pág y Av Pág saltan diez filas, y Tab y Shift+Tab avanzan a través de columnas; no hay salto Ctrl+Flecha al borde de una región de datos y no hay selección de rango extendida con Shift, así que los usuarios que vienen directamente de Excel notarán la brecha en una hoja densa
Los límites de columna se hacen cumplir en el mismo punto de estrangulamiento ChangeSelection que maneja la normalización de combinación, y difieren por motor a propósito: un visor vinculado a un TXLSWorkbook clásico se limita en la columna 256, el techo estructural del formato BIFF8, mientras que uno vinculado a TXLSXWorkbook respeta el límite moderno de 16.384 columnas que XLSX heredó de Excel 2007 en adelante. Las filas están topadas en 1.048.576 en cualquier caso, así que la diferencia práctica entre abrir un archivo XLS heredado y un archivo XLSX en el mismo visor es enteramente sobre cuán lejos hacia la derecha está dispuesta a dejarlo ir la grilla
Nada de esto es exótico una vez que se descompone en búsqueda de píxeles, normalización de ancla, y un puñado de manejadores de mensajes, pero lograr que los tres concuerden bajo archivos reales, con combinaciones, comentarios, e hipervínculos reales, es la mayor parte del trabajo en un componente como este. TXLSWorkbookViewer se incluye como parte del componente Excel HotXLS estándar para Delphi y C++Builder, junto con los modelos de objetos clásico y XLSX desde los que renderiza