Difference between revisions of "FPSpreadsheet tutorial: Writing a mini spreadsheet application/es"

From Free Pascal wiki
Jump to navigationJump to search
Line 272: Line 272:
 
* '''Toolbar''': Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its <code>Action</code> property.
 
* '''Toolbar''': Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its <code>Action</code> property.
 
* '''Menu''': The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its <code>Action</code> property.
 
* '''Menu''': The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its <code>Action</code> property.
 +
 +
=== Reading from file ===
 +
 +
What is left is '''reading of a spreadsheet file''' into our application. Of course, FPSpreadsheet is well-prepared for this task. The operations are very similar to saving. But instead of using a TFileSaveAs standard action, we use a '''TFileOpen''' standard action. Again, this standard action has a built-in file dialog where we have to set the <code>DefaultExtension</code> (".xls" or ".xlsx", most probably) and the format <code>Filter</code>:
 +
 +
:<code>All spreadsheet files|*.xls;*.xlsx;*.ods;*.csv|All Excel files (*.xls, *.xlsx)|*.xls;*.xlsx|Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv</code>
 +
 +
(Copy this string into the field <code>Filter</code> of the action's <code>Dialog</code>). As you may notice the <code>Filter</code> contains selections which cover various file formats, such as "All spreadsheet files", or "All Excel files". This is possible because the TsWorkbookSource has a property <code>AutoDetectFormat</code> for automatic detection of the spreadsheet file format. In the other cases, like "Libre/OpenOffice", we can specify the format, <code>sfOpenDocument</code>, explicitly. Evaluation of the correct file format and reading of the file is done in the <code>OnAccept</code> event handler of the action:
 +
 +
<syntaxhighlight>
 +
{ Loads the spreadsheet file selected by the FileOpen standard action }
 +
procedure TForm1.FileOpen1Accept(Sender: TObject);
 +
begin
 +
  sWorkbookSource1.AutodetectFormat := false;
 +
  case FileOpen1.Dialog.FilterIndex of
 +
    1: sWorkbookSource1.AutoDetectFormat := true;        // All spreadsheet files
 +
    2: sWorkbookSource1.AutoDetectFormat := true;        // All Excel files
 +
    3: sWorkbookSource1.FileFormat := sfOOXML;            // Excel 2007+
 +
    4: sWorkbookSource1.FileFormat := sfExcel8;          // Excel 97-2003
 +
    5: sWorkbookSource1.FileFormat := sfExcel5;          // Excel 5.0
 +
    6: sWorkbookSource1.FileFormat := sfExcel2;          // Excel 2.1
 +
    7: sWorkbookSource1.FileFormat := sfOpenDocument;    // Open/LibreOffice
 +
    8: sWorkbookSource1.FileFormat := sfCSV;              // Text files
 +
  end;
 +
  sWorkbookSource1.FileName :=FileOpen1.Dialog.FileName;  // This loads the file
 +
end;
 +
</syntaxhighlight>
 +
 +
In order to see this action in the toolbar and menu, add a TToolButton to the '''toolbar''' and assign the TFileOpenAction to its <code>Action</code> property. In the '''menu''', add a new item before the "Save as" item, and assign its <code>Action</code> accordingly.
 +
 +
{{Note|You can see a spreadsheet file even at designtime if you assign its name to the <code>Filename</code> property of the TsWorkbookSource. But be aware that the file probably cannot be found at runtime if it is specified by a relative path and if the application is to run on another computer with a different directory structure! }}

Revision as of 14:57, 22 January 2015

Template:MenuTranslate

Introducción

FPSpreadsheet es un paquete potente para la lectura y escritura de ficheros spreadsheet (Hoja de cálculo). La intención principal es aportar una plataforma capaz de exportar e importar de forma nativa datos a/desde los más importantes formatos de fichero de hojas de cálculo sin tener que instalar aplicaciones adicionales.

Pronto, como siempre, surge el deseo de utilizar este paquete también para editar el contenido y formato. Para este propósito, la librería contiene un control grid (FPSpreadSheetGrid) dedicado, que nos recuerda las características de una hoja de trabajo (WorkSheet) de las aplicaciones de hojas de cálculo. Junto a un conjunto de opciones de formato, esta demo todavía viene a tener 1400 líneas de código en su unidad de formulario principal. Por lo tanto, se ha diseñado un conjunto de controles visuales que simplifican ampliamente la creación de aplicaciones de hoja de cálculo (SpreadSheets).

La intención de este tutorial es escribir un programa de hoja de cálculo simple en base a estos controles.

Aunque la mayor parte de la estructura interna de la librería FPSpreadsheet está cubierta mediante los controles visuales sigue siendo recomendable tener algún conocimiento de FPSpreadsheet. Por supuesto debería tenerse un conocimiento básico de Lazarus o FPC y sobre como trabajar con el inspector de objetos de Lazarus.

Controles Visuales FPSpreadSheet

FPSpreadsheet expone clases no visuales, tales como TsWorkbook, TsWorksheet etc. Esto mantiene la librería suficientemente generalizada para todo tipo de programas Pascal. Por otro lado, para programamas con Interfaz Gráfica de Usuario (GUI: Graphic Unit Interface), se necesita algo de infraestructura que relacione los SpreadSheets con los formularios, grids y otros controles.

TsWorkbookSource

TSWORKBOOKSOURCE.png El corazón de los controles visuales FPSpreadSheet es la clase TsWorkBookSource que aporta un enlace entre los datos no visuales spreadsheet y los controles visuales del formulario. Su propósito es similar al que tiene el componente TDataSource en aplicaciones de base de datos que enlazan las tablas de las bases de datos o consultas (queries) a controles orientados a datos (data-aware).

Todos los controles visuales FPSpreadSheet tienen una propiedad WorkbookSource que los enlaza con la cadena de información aportada por TsWorkbookSource. WorkbookSource mantiene un listado de todos los controles vinculados. Estos controles se llaman internamente "listeners" porque se encuentran en modo escucha de la informa distribuida por WorkbookSource.

El libro de trabajo (WorkBook) y las hojas de trabajo (WorkSheets) que contiene, utilizan eventos para notificar a WorkBookSource todos los cambios relevantes: cambios en el contenido de las celdas o su formato, selección de otras celdas, añadir o borrar hojas de trabajo, etc. La información de estos cambios se pasa a través de los controles "listening" y reaccionan a su propio modo especializado a dichos cambios. Si por ejemplo se añade una hoja de trabajo a un libro, entonces el control visual TsWorkBookTabControl crea una nueva solapa para la hoja de trabajo y el control grid "TsWorksheetGrid" carga la nueva hoja de trabajo dentro de la grid.

Control TsWorkbookTabControl

TSWORKBOOKTABCONTROL.png Es un control tipo solapa (tabcontrol) que provee una solapa para cada hoja de trabajo (WorkSheet)del actual libro de trabajo (WorkBook). Los nombres de las solapas son idénticas a los nombres de las hojas de trabajo. La selección de cualquier otra solapa es comunicada a los otros controles del SpreadSheet a través de WorkbookSource.

TsWorksheetGrid

TSWORKSHEETGRID.png Es un DrawGrid personalizado descendiente del LCL y muestra celdas de la hoja de trabajo actual seleccionada. El texto no se almacena en la grid (como haría un StringGrid), pero se toman de la estructura de datos de TsWorksheet. Similarmente, la hoja de trabajo aporta la información de como está formateada cada celda. Como cualquier otra grid del LCL tiene un conjunto de propiedades y se puede tunear por parte de las aplicaciones adaptando sus Options. La más importante se describe abajo.

Note-icon.png

Nota: TsWorksheetGrid puede ser manejado también sin TsWorkbookSource. Para este propósito provee su propio conjunto de métodos para lectura y escritura de ficheros.

TsCellEdit

TSCELLEDIT.png Las aplicaciones de hoja de cálculo típicas proveen un línea para la edición de fórmulas y contenido de las celdas. Este es el propósito de TsCellEdit. Esto muestra el contenido de la celda activa dentro de la hoja de trabajo la cual es la misma que la que se encuentra activa en el grid de la hoja de trabajo (WorksheetGrid). Si se finaliza la edición (presionando Enter, o seleccionando otra celda en la grid)entonces se transfiere a la hoja de trabajo el nuevo valor de la celda. El control TsCellEdit es internamente del tipo memo que por ejemplo es capaz de procesar múltiples líneas de texto correctametne. Para insertar un forced line-break se puede pulsar la secuencia Ctrl+ Enter.

TsCellIndicator

TSCELLINDICATOR.png This is a TEdit control which displays the address of the currently selected cell in Excel notation, e.g. 'A1' if the active cell is in the first row and first column (row = 0, column = 0). Conversely, if a valid cell address is entered into this control the corresponding cell becomes active.

TsCellCombobox

TSCELLCOMBOBOX.pngEste combobox se puede utilizar para modificar varias propiedades de la celda seleccionando sus valores de una lista desplegable. La propiedad afectada viene determinada por el CellFormatItem del combobox:

  • cfiFontName: el listado contiene los nombres de todas las fuentes disponibles en el sistema. Si se selecciona un elemento de ese listado entonces el tipo de fuente que se ha seleccionado se utiliza para dar formato a la/las celdas que tuviesemos seleccionadas antes de desplegar sus propiedades para cambiarlas.
  • cfiFontSize: este listado contine los tamaños de fuente más comunmente utilizados en hojas de cálculo. Seleccionando un elemento de los disponibles se establece por tanto el tamaño de fuente para la/las celda(s) seleccionadas.
  • cfiFontColor: este listado contiene todos los colores disponibles en la paleta del libro de trabaja (workbook). El color seleccionado se asigna a la fuente de la/las celda(s) seleccionada.
  • cfiBackgroundColor: similar a cfiFontColor - se utiliza el color seleccionado como color de relleno de fondo de la/las celda(s) seleccionadas.

TsSpreadsheetInspector

TSSPREADSHEETINSPECTOR.png Hereda de TValueListEditor y muestra pares nombre-valor (name-value) para propiedades del libro de trabajo, la hoja de trabajo y el contenido y formateado de la celda activa. Su propósito principal es ayudar con el depurado (debugging).

Escribiendo una aplicación de hoja de cálculo

Bueno suficiente teoría hasta aquí, comencemos. Vamos a escribir una pequeña aplicación de hoja de cálculo. Cierto, no va a competir con las principales aplicaciones ofimáticas como Excel u OpenOffice/LibreOffice, pero tiene todos los ingredientes principales asociados a FPSpreadsheet. Y utilizando controles FPSpreadsheet nos permite conseguir esto con unas mínimas líneas de código.

Preparativos

fpspreadsheetcontrols preparations.png

Primero de todo vamos a crear un nuevo proyecto y lo almacenamos en alguna carpeta de nuestra elección.

Ya que las aplicaciones ofimáticas tienen un menú y una barra de utilidades (toolbar) añadimos al formulario los correspondientes componentes TMainMenu y TToolbar

(Se puede incluso mimetizar el ribete del interfaz de usuario de las nuevas aplicacones de Microsoft añadiendo un TSpkToolbar de Lazarus Code and Components Repository, pero ten en cuenta que este componente no provee todavía todas las características de una toolbar standar).


En efecto, necesitaremos otra toolbar para la línea del editor de fórmulas. Como verás más adelante, será redimensionable; como control de tamaño añade un TSplitter al formulario con una alineación top de tal manera que esté posicionado debajo de las dos barras de utilidades. En orden a mantener un tamaño mínimo para la toolbar se establecen restricciones: Mira la altura actual de la toolbar e introduce su valor numérico dentro del campo MinHeight de la propiedad Constraints en la toolbar. Para separar la barra de utilidades (toolbar) de fórmulas del resto del formulario principal, activamos la opción ebBottom de la propiedad EdgeBorders de la segunda toolbar.

Debido a que tanto el menú como las barras de utilidades tienen que manejar las mismas acciones de usuario resulta ventajoso aportar un TActionList para almacenar todas las posibles acciones. Si se asigna a los elementos del menú y a los pulsadores de utilidades (ToolButtons) entonces ambos reaccionarán a la interacción del usuario de la misma manera sin necesidad de código adicional. Y: además los controles visuales del paquete FPSpreadsheet contienen un montón de acciones estandar listas para usar.

La barra de utilidades de la aplicación completa va a contener un montón de iconos. Por tanto necesitamos un componente TImageList el cual tiene que ser enlazado a la propiedad Images del menú principal (TMainMenu), las barras de utilidades (TToolbars), y los listados de acciones (TActionList). A la pregunta de donde obtener iconos la respuesta más fácil es buscar en la carpeta images de la propia instalación de Lazarus donde podemos encontrar iconos estandar para cargar, salvar, salir, flechas, imprimir,configurar,.... Es un subconjunto de la librería de iconos famfamfam SILK. Otra enorme colección se encuentra en la colección de iconos Fugue. Ambas colecciones están licenciadas como "Creative commons" y son libres incluso para su uso comercial, añadiendo la referencia apropiada en los programas creados. Cuando selecciones iconos procura que tengan el formato de imagen png y asegúrate de utilizar siempre el mismo tamaño, usualmente 16x16 pixels.

Setting up the visual workbook

TsWorkbookSource

Tal como se ha descrito en la sección de introducción, el componente TsWorkbookSource es el interface entre WorkBook y los controles de interface de usuario. Se añade por tanto este componente al formulario y se le asigna un nombre decente (en este caso dejaremos el nombre que tiene por defecto sWorkbookSource1). Como veremos en breve, este componente tendrá que ser asignado a la propiedad WorkbookSource de todos los controles del paquete visual FPSpreadsheet_visual.

WorkBookSource se encarga de la carga y escritura de datos desde/hacia el fichero y de la comunicación con WorkBook. Por tanto, posee un conjunto de opciones que se pasan al WorkBook y controla estos procesos:

type
  TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving, boReadFormulas);
  TsWorkbookOptions = set of TsWorkbookOption;
sTabControl.png

Los más importantes son:

  • boAutoCalc: activa el cálculo automático de las fórmulas en el momento que cambia el contenido de las celdas.
  • boCalcBeforeSaving: calcula las fórmulas antes de que el WorkBook se escriba al fichero.
  • boReadFormulas: si se establece entonces se leen las fórmulas del fichero, de otro modo solamente el resultado de la fórmula.
  • boBufStream y boVirtualMode: en programas no visuales estas opciones pueden ayudar si las aplicaciones se quedan sin memoria en el caso de workbooks extensos. boVirtualMode, en particular, no es usable para aplicaciones visuales, porque evita guardar datos en las celdas de la hoja de trabajo. Ver también FPSpreadsheet#Virtual_mode.

En este tutorial se asume que las opciones boAutoCalc y boReadFormulas se encuentran activas.

TsWorkbookTabControl

El primer control visual utilizado en el formulario es un TsWorkbookTabControl - lo emplazamos en el formulario (dentro del espacio no ocupado por la ToolBar). Client-align it within the form, this shows the TabControl as a bright rectangle only. Ahora vinculamos su propiedad WorkbookSource al componente TsWorkbookSource que hemos añadido previamente. Ahora TabControl muestra una solapa llamada "Sheet1". Esto es debido a que TsWorkbookSource ha creado WorkBook vacío conteniend una sola hoja (WorkSheet) "Sheet1". WorkbookSource sincroniza este WorkBook interno con TabControl (y el resto de elementos visuales asociados) tal como se muestra la hoja como solapa.

En Excel las solapas de las hojas se encuentran al fondo del formulario - para lograr este efecto se puede establecer la propiedad TabPosition del control TabControl a tpBottom; hay algunas incidencias para el dibujado del LCL con TabPosition, aunque, de ante mano, yo prefiero los valores por defecto, tpTop.

El pantallazo muestra lo que obtenemos.

TsWorksheetGrid

sWorksheetGrid.png

Ahora añadimos un control TsWorksheetGrid. Lo emplazamos en algún lugar dentro del espacio ocupado por el control TabControl. De esta forma viene a colgar de TabControl. Al hacer esto podremos ver algo parecido a un componente grid de cadenas (StringGrid). Enlazamos su propiedad WorkbookSource a la fuente añadida al principio y entonces la grid se parece más a una hoja de cálculo (SpreadSheet): están las cabeceras de las columnas etiquetadas con letras "A", "B", etc, y las filas etiquetadas con números "1", "2", etc; la celda activa, A1, está remarcada por el borde grueso.

Puede que se necesite cambiar TitleStyle a tsNative en la grid para lograr el themed painting de la cabecera de la fila y columna. Y aquí también es un buen lugar para adaptar las Options de la grid para activar algunas características bien conocidas de las hojas de cálculo:

  • goEditing debe estar activo, de otra manera el contenido de la grid no se podrá modificar.
  • goAlwaysShowEditor debe estar off porque interfiere con la convención de edición de las aplicaciones de hojas de cálculo.
  • goColSizing habilita cambiar el ancho de columna a través del dragging de la línea divisoria que encontramos en las cabeceras de las columnas adyacentes.. Dragging tiene lugar con el pulsador izquierdo del ratón presionado.
  • goRowSizing realiza lo mismo con la altura de las filas.
  • goDblAutoResize activa la característica de optimización de ancho de columna que se obtiene haciendo doble click en la línea divisoria del encabezado de la columna que se situa hacia al siguente columna. La "optimum" ancho de columna es tal que no se trunca el contenido de la celda y no se muestra espacio extra sobrante.
  • goHeaderHotTack nos da información adicional si se pasa el cursor del ratón por encima de la cabecera de las celdas.
  • goRangeSelect (se encuentra "on" por defecto) y habilita la selección de un rango rectangular de celdas haciendo click con el pulsador izquierdo del ratón y sin soltarlo arrastrandolo hasta completar la selección de celdas (en definitiva de esquina a esquina opuesta del rectángulo). Si se tiene una versión de Lazarus del trunk y incluso se puede realizar una selección multiple de areas rectangulares manteniendo pulsada la tecla CTRL antes de seleccionar el siguente rectángulo - en la versión 1.2.6 de Lazarus (en el momento de estribir esto) solamente se permite la selección de un rango.
  • goThumbTracking activa el scrolling inmediato de la hoja (Worksheet) si una de las barras de desplazamiento es arrastrada con el ratón. Las aplicaciones ofimáticas usualmente desplazan por líneas, lo cual se puede conseguir estableciendo a off goSmoothScroll.

En adición a estas Options heredada de TCustomGrid existen más opciones especializadas para operar con hojas de cálculo:

  • ShowGridLines, si se establece a false oculta la fila y columna en la grid.
  • ShowHeaders puede establecerse a false para ocultar las cabeceras de fila y columna. (Se puede conseguir esto mismo por medio de la desfasada propiedad DisplayFixedColRow).
  • Las grids del LCL normalmente truncan el texto al borde de la celda cuando es más largo que el ancho de la celda. Si se establece TextOverflow al valor true entonces el texto se expande a las celdas adyacentes.

Las propiedades AutoCalc y ReadFormulas están pensadas para su uso independientemente de la grid de la hoja de cálculo (WorkSheetGrid)(e.g. sin un TsWorkbookSource). Por favor utilizar las opciones correspondientes del WorkbookSource en su lugar. (AutoCalc habilita el cálculo automático de fórmulas cada vez que cambia el contenido de la celda. ReadFormulas activa la lectura de fórmulas desde ficheros, de otro modo la grid solamente mostraría el resultado de la fórmula).

Edición de valores y fórmulas, Navegación

Cuando se compile y arranque el programa se tendrá disponible entrar datos en el grid. Simplemente hay que seleccionar una celda que se necesita editar haciendo click sobre ella o bien desplazándose a la misma con las teclas de cursor, hasta que sea resaltada con el borde ancho, en ese momento ya se pueden editar. A continuación seleccionamos otra celda o bien presionamos la tecla Enter. Utilizando Enter automáticamente seleccionamos la siguiente celda en la grid. La propiedad de la grid AutoAdvance define lo que se conoce como "próxima celda": por defecto, Enter mueve a la siguiente celda situada debajo (aaDown), pero se puede mover también a la derecha (aaRight), o dejar esta característica apagada (aaNone) - ver el tipo TAutoAdvance definido en la unidad grids.pas para más opciones.

Si - como vimos antes - se habilita la opción WorkbookSource boAutoCalc entonces la hoja soporta automáticamente el cálculo de formulas. Por poner un ejemplo, vayamos a la celda A1, entra el número 10, a continuación vamos a la celda A2 y entramos la fórmula =A1+10. La fórmula automáticamente se evalúa y se muestra el resultado 20, que se muestra en la celda A2.

Cuando navegamos por la grid podemos notar que la celda A2 únicamente muestra el resultado de la fórmula, parece que no hay forma de modificar la fórmula una vez que se introduce. No hay necesidad de preocuparse - presionamos la tecla F2 o hacemos click en la celda una segunda vez de entrar enhanced edit mode momento en el cual las fórmulas estarán visibles en la celda.

sCellIndicator sCellEdit.png

En orden a editar las fórmulas, las aplicaciones ofimáticas ofrecen una barra dedicada a la edición de fórmulas. Por supuesto, fpspreadsheet tiene esta característica también. Se encuentra implementado dentro del componente TsCellEdit que está establecido para mostrar siempre el contenido completo de la fórmula. Si recuerdas la segunda barra de utilidades de la sección "Preparativos", pues ahí se situará TsCellEdit. Pero, un momento, - hay algo más que considerar: Debido a que las fórmulas ocasionalmente tienden a ser bastante más largas de lo que el control puede mostrar, entonces es necesario que soporte varias líneas de edición, lo mismo que con texto multi-línea. TsCellEdit puede hacer esto puesto que hereda de TCustomMemo el cual es un control multi-línea. También hay que recordar que añadimos un separador al formulario de la segunda barra de utilidades. Esto lo necesitamos para los ajustes verticales en caso de necesitar el uso de la característica multi-línea de TsCellEdit: simplemente presiona y mantén pulsado el separador para mostrar más líneas, o atrástralo hasta graduar la altura a las necesidades de una simple línea de acuerdo a las restricciones de MinHeight que hemos asignado a la barra de utiliddes.

TsCellEdit cubrirá todo el espacio disponible en la segunda barra de utilidades. Antes de añadir TsCellEdit podemos hacer la vidda más fácil si pensamos un poco sobre que otras cosas podemos añadir en esta segunda barra de utilidades. En Excel hay un indicador que muestra la dirección de la celda activa actual. Este es el propósito de TsCellIndicator. Since its height should not change when the toolbar is dragged down we first add a TPanel to the second toolbar; reduce su Width sobre 100 pixels, remueve su Caption y establece BevelOuter a bvNone.

Añade TsCellIndicator a este panel y alinealo al tope del panel. Conecta su WorkBookSource al control TsWorkbookSource del formulario e inmediatamente aparecerá el texto "A1", la dirección de la celda actualmente seleccionada.

Algunas veces resulta deseable cambiar la anchura de este cuadro en tiempo de ejecución, por lo cual ¿porqué no añadir un separador a esta segunda barra?, pues podemos hacerlo estableciendo su propiedad Align a alLeft. El resultado es un poco extraño: el separador (splitter) se ubica a su borde izquierdo aunque se esperaba encontrar a la derecha del panel. Esto es debido a que el panel no está alineado por defecto. Hay que establecer la propiedad Align del panel a alLeft, y arrastrar sin soltar el separador hacia la derecha del panel. Ahora el separador lo tenemos en la posición correcta.

Casi todo hecho... Finalmente añadimos un componente TsCellEdit al espacio vacío de la barra. La alineación del cliente es tal que rellena el resto del espacio de la barra. Como suele ser habitual, establecemos su propiedad WorkBookSource a la instancia de TsWorkbookSource en el formulario.

Compila y ejecuta. Juega con el programa:

  • Enter some dummy data. Navigate in the worksheet. You'll see that the CellIndicator always shows the address of the active cell. The contents of the active cell is displayed in the CellEdit box. The CellIndicator is not just a passive display of the current cell, it can also be edited. Type in the address of a cell which you want to become active, press Enter, and see what happens...
  • Entra una fórmula. Navigate back into the formula cell - se muestra la fórmula en el editor de fórmulas (CellEdit) donde se puede modificar rpidamente.
  • Enter multi-lined text - you can enforce a lineending in the CellEdit by holding the Ctrl key down when you press Enter. The cell displays only one line of the text. Drag the horizontal splitter underneath the second toolbar down - the CellEdit shows all lines. Another way to see all lines of the text, is to adjust the cell height. You must have activated the grid Option goRowSizing. Then you can drag the lower dividing line of the row with the multi-line cell down to increase the row height - the missing lines now appear in the cell!

Formatting of cells

In addition to entering data the user usually wants to apply some formatting to the cells in order to enhance or group them. The worksheet grid is set up in such a way that its cells display the formats taken from the workbook. In addition, the visual FPSpreadsheet controls are able to store formatting attributes into the cell. Because of the notification mechanism via the WorkbookSource these formats are returned to the WorksheetGrid for display.

Adding comboboxes for font name, font size, and font color

sCellFontCombobox.png

In this section, we want to provide the possibility to modify the font of the cell texts by selecting its name, size and/or color. The visual FPSpreadsheet provide the flexible TsCellCombobox for this purpose. It has the property CellFormatItem which defines which attribute it controls:

  • cfiFontName: This option populates the combobox with all fonts found in the current system. The selected item is used for the type face in the selected cells.
  • cfiFontSize fills the combobox with the mostly used font sizes (in points). Again, the selected item defines the font size of the selected cells.
  • cfiFontColor adds all pre-defined colors ("palette") of the workbook to the combobox to set the text color of the selected cells. The combobox items consist of a little color box along with the color name. If the ColorRectWidth is set to -1 the color name is dropped.
  • cfiBackgroundColor, the same with the background color of the selected cells.
  • cfiCellBorderColor, the same with the border color of the selected cells - this feature is currently not yet supported.

Add three TsCellComboboxes to the first toolbar and set their CellFormatItem to cfiFontname, cfiFontSize, and cfiFontColor, respectively. Link their WorkbookSource property to the TsWorkbookSource on the form. You may want to increase the width of the font name combobox such that the longest font names are not cut off; the other comboboxes may become narrower. You may also want to turn off the color names of the third combobox by setting its ColorRechtWidth to -1.

That's all to modify fonts. Compile and run. Enter some text and play with these new features of the program.

Using standard actions

sFontStyleAction selected.png

FPSpreadsheet supports a lot of formats that can be applied to cells, such as text alignment, text rotation, text font, or cell borders or background colors. Typical gui applications contain menu commands and/or toolbar buttons which are assigned to each of these properties and allow to set them by a simple mouse click. In addition, the state of these controls often reflects the properties of the active cell. For example, if there is a button for using a bold type-face this button should be drawn as being pressed if the active cell is bold, but as released if it is not. To simplify the coding of these tasks a large number of standard actions has been added to the library.

  • TsWorksheetAddAction: adds an empty worksheet to the workbook. Specify its name in the NameMask property. The NameMask must contain the format specifier %d which is replaced at runtime by a number such that the worksheet name is unique.
  • TsWorksheetDeleteAction: deletes the active worksheet from the workbook after a confirmation dialog. The last worksheet cannot be deleted.
  • TsWorksheetRenameAction: renames the active worksheet.
  • TsCopyAction: Copies the currently selected cells to an internal list ("CellClipboard") from where they can be pasted back into the spreadsheet to another location. The process can occur in a clipboard-manner ("copy"/"cut", then "paste") or in the way of the "copy brush" of the Office applications. The property CopyItem determines whether the entire cell, or only cell values, cell formulas, or cell formats are transferred.
  • TsFontStyleAction: Modifies the font style of the selected cells. The property FontStyle defines whether the action makes the font bold, italic, underlined or striked-out. Normally each font style is handles by its own action. See the example below.
  • TsHorAlignmentAction: Can be used to modify the horizontal alignment of text in the selected cells. Select HorAlignment to define which kind of alignment (left, center, right) is covered by the action. Like with the TsFontStyleAction, several actions should be provided to offer all available alignments. They are grouped in a mutually exclusive way like radiobuttons.
  • TsVertAlignmentAction: Changes the vertical alignment of text in the selected cells: the kind of alignment is defined by the VertAlignment property. Again, these actions work like radiobuttons.
  • TsTextRotationAction: Allows to specify the text orientation in the selected cells as defined by the property TextRotation in a mutially exclusive way.
  • TsWordWrapAction: Activates the word-wrapping feature for the selected cells: if text is longer than the width of the cell (or height, if the text is rotated) then it is wrapped into multiple lines.
  • TsNumberFormatAction: Defines the number format to be used for the selected cells. The format to be used is defined by the properties NumberFormat (such as nfFixed) for built-in formats, and NumberFormatStr for specialized formatting.
  • TsDecimalsAction: Allows to increase or decrease the number of decimal places shown in the selected cells. The property Delta controls whether an increase (+1) or decrease (-1) is wanted.
  • TsCellBorderAction: Allows to specify if a border will be drawn around the selected cells. The subproperties East, West, North, South, InnerHor, InnerVert of Borders define what the border will look like at each side of the cell range. Note that each rectangular range of cells is considere as a single block; the properties East, West, North and South are responsible for the outer borders of the entire block, inner borders are defined by InnerHor and InnerVert. Using these properties, borders can be switched on and off (Visible), and in addition, the line style and line color can be changed.
  • TsMergeAction: If checked, the cells of each selected rectangular range are merge to a single block. Unchecking the action separates the block to individual cells. Note that the block's content and formatting is defined by the top-left cell of each block; content and formats of other cells will be lost.

Adding buttons for "Bold", "Italic", and "Underline"

sFontStyleAction in ActionListEditor.png

If you have never worked with standard actions before here are some detailed step-by-step instructions. Let us stick to above example and provide the possibility to switch the font style of the selected cells to bold. The standard action which is responsible for this feature is the TsFontStyleAction.

  • At first, we add this action to the form: Double-click on the TActionList to open the "ActionList Editor".
  • Click on the down-arrow next to the "+" button, and select the item "New standard action" from the drop-down menu.
  • This opens a dialog with the list of registered "Standard Action Classes".
  • Scroll down until you find a group named "FPSpreadsheet".
  • In this group, select the item "TsFontStyleAction" by double-clicking.
  • Now an item sFontStyleAction1 appears in the ActionList Editor.
  • It should already be selected like in the screenshot at the right. If not, select sFontStyleAction1 in the ActionList Editor to bring it up in the Object Inspector and to set up its properties:
    • Use the text "Bold" for the Caption - this is the text that will be assigned to the corresponding menu item.
    • Similarly, assign "Bold font" to the Hint property.
    • Set the ImageIndex to the index of the icon in the form's ImageList that you want to see in the toolbar.
    • Make sure that the item fssBold is highlighted in the dropdown list of the property FontStyle. If not, select it. Since TsFontStyleAction can handle several font styles (bold, italic, underline, strikeout) we have to tell the action which font style it should be responsible of.
    • Like with the visual controls, don't forget to assign the TsWorkbookSource to the corresponding property WorkbookSource of the action. This activates the communication between the worksheet/workbook on the one hand, and the action and the related controls on the other hand.

Having set up the standard action we add a menu item to the form's MainMenu. Double-click on the TMainMenu of the form to bring up the "Menu Editor". Since the menu is empty so far there is only a dummy item, "New item1". This will become our "Format" menu. Select the item, and type "Format" into the Caption property field. Now the dummy item is re-labelled as "Format". Right-click on this "Format" item, and select "Create submenu" from the popup menu which brings up another new menu item, "New item2". Select it. In the dropdown list of the property Action of the object inspector, pick the sFontStyle1 action - this is the action that we have just set up - and the menu item automatically shows the caption provided by the action component, "Bold".

Finally we add a toolbar button for the "bold" action. Right-click onto the TToolbar, and add a new toolbutton by selecting item "New button" from the popup menu. Go to the property Action in the object inspector again, pick the sFontStyle1 item, and this is enough to give the tool button the ability to set a cell font to bold!

Repeat this procedure with two other buttons. Design them to set the font style to italic and underlined.

Test the program by compiling. Type some text into cells. Select one of them and click the "Bold" toolbutton - voila, the cell is in bold font. Select another cell. Note that the toolbutton is automatically drawn in the down state if the cell has bold font. Repeat with the other buttons.

Saving to file

After having entered data into the grid you will certainly want to save the grid to a spreadsheet file. Lazarus provides all the necessary infrastructure for saving available in the standard action TFileSaveAs. This action automatically opens a FileDialog for entering the file name.

Select the TFileSaveAs standard action from the list of standard action classes. Note that it cannot be found in the "FPSpreadsheet" category, but in the "File" group since it is a standard action of the LCL.

sFileFormatsForSaving.png

At first, let us specify the properties of the FileDialog. Select the property Dialog of the TFileSaveAs action in the object inspector. It is convenient to be able to store the workbook in various file formats; this can be prepared by providing a file format list in the Filter property of the dialog. Paste the following text into this property:

Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv|WikiTable (WikiMedia-Format, *.wikitable_wikimedia)|*.wikitable_wikimedia

When you click on the ellipsis button next to Filter the file list appears in a more clearly arranged dialog shown at the right.

Make one of these file extensions, e.g. xlsx, the default of the file dialog by assigning its list index to the FilterIndex property. The xlsx file is the first format in the filter list. FilterIndex, therefore, must be set to 1.

Light bulb  Nota: The indexes in the filter list are 1-based, in contrast to the convention of Lazarus and FPC using 0-based indexes.

Next, we define what happens after a file name has been selected in the file dialog. For this purpose, the TFileSaveAs action provides the event OnAccept. This is one of the few places where we have to write code in this project... But it is short: We check which file format has been selected in the format list and write the corresponding spreadsheet file by calling the method SaveToSpreadsheetFile of the TWorkbookSource:

uses
  ..., fpspreadsheet, ...;   // for TsSpreadsheetFormat

procedure TForm1.FileSaveAs1Accept(Sender: TObject);
var
  fmt: TsSpreadsheetFormat;
begin
  Screen.Cursor := crHourglass;
  try
    case FileSaveAs1.Dialog.FilterIndex of
      1: fmt := sfOOXML;                // Note: Indexes are 1-based here!
      2: fmt := sfExcel8;
      3: fmt := sfExcel5;
      4: fmt := sfExcel2;
      5: fmt := sfOpenDocument;
      6: fmt := sfCSV;
      7: fmt := sfWikiTable_WikiMedia;
    end;
    sWorkbookSource1.SaveToSpreadsheetFile(FileSaveAs1.Dialog.FileName, fmt);
  finally
    Screen.Cursor := crDefault;
  end;
end;

We will make the FileSaveAs action available in the toolbar and in the menu:

  • Toolbar: Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its Action property.
  • Menu: The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its Action property.

Reading from file

What is left is reading of a spreadsheet file into our application. Of course, FPSpreadsheet is well-prepared for this task. The operations are very similar to saving. But instead of using a TFileSaveAs standard action, we use a TFileOpen standard action. Again, this standard action has a built-in file dialog where we have to set the DefaultExtension (".xls" or ".xlsx", most probably) and the format Filter:

All spreadsheet files|*.xls;*.xlsx;*.ods;*.csv|All Excel files (*.xls, *.xlsx)|*.xls;*.xlsx|Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv

(Copy this string into the field Filter of the action's Dialog). As you may notice the Filter contains selections which cover various file formats, such as "All spreadsheet files", or "All Excel files". This is possible because the TsWorkbookSource has a property AutoDetectFormat for automatic detection of the spreadsheet file format. In the other cases, like "Libre/OpenOffice", we can specify the format, sfOpenDocument, explicitly. Evaluation of the correct file format and reading of the file is done in the OnAccept event handler of the action:

{ Loads the spreadsheet file selected by the FileOpen standard action }
procedure TForm1.FileOpen1Accept(Sender: TObject);
begin
  sWorkbookSource1.AutodetectFormat := false;
  case FileOpen1.Dialog.FilterIndex of
    1: sWorkbookSource1.AutoDetectFormat := true;         // All spreadsheet files
    2: sWorkbookSource1.AutoDetectFormat := true;         // All Excel files
    3: sWorkbookSource1.FileFormat := sfOOXML;            // Excel 2007+
    4: sWorkbookSource1.FileFormat := sfExcel8;           // Excel 97-2003
    5: sWorkbookSource1.FileFormat := sfExcel5;           // Excel 5.0
    6: sWorkbookSource1.FileFormat := sfExcel2;           // Excel 2.1
    7: sWorkbookSource1.FileFormat := sfOpenDocument;     // Open/LibreOffice
    8: sWorkbookSource1.FileFormat := sfCSV;              // Text files
  end;
  sWorkbookSource1.FileName :=FileOpen1.Dialog.FileName;  // This loads the file
end;

In order to see this action in the toolbar and menu, add a TToolButton to the toolbar and assign the TFileOpenAction to its Action property. In the menu, add a new item before the "Save as" item, and assign its Action accordingly.

Light bulb  Nota: You can see a spreadsheet file even at designtime if you assign its name to the Filename property of the TsWorkbookSource. But be aware that the file probably cannot be found at runtime if it is specified by a relative path and if the application is to run on another computer with a different directory structure!