By: Tim DelChiaro
Abstract: Introducing TTMSFMXGrid: a flexible, productivity feature-packed cross platform grid for FireMonkey
TMS Grid for FireMonkey
Copyright © 2012 by tmssoftware.com bvba
Introducing TTMSFMXGrid: a flexible, productivity feature-packed cross platform grid for FireMonkey
As part of the Embarcadero RAD Studio XE3 bonus pack, TMS is proud to contribute our popular TMS Grid for FireMonkey for this limited time promotion. The TMS Grid for FireMonkey is part of a larger collection of FireMonkey components from TMS called the TMS Pack for FireMonkey.The TMS Grid for FireMonkey is a full-featured grid component that fully integrates with FireMonkey, respects FireMonkey design principles, and support IDE designer features, like the new Visual LiveBindings Designer.
This paper discusses the motivation and requirements of a FireMonkey grid component from TMS. Additionally, this paper describes some of the basic functionality of this component. Finally, this paper includes information about and a discount offer for the full TMS Pack for FireMonkey.
As soon as Delphi XE2 with the new FireMonkey framework was released, a feature-rich and high performance grid was one of the most requested components for this new framework.
While we had already delivered a grid for the VCL framework for over 15 years now, it immediately became clear that first of all, there was not a quick way to port this VCL grid to the FireMonkey framework and secondly, that is was also highly undesirable to do simply attempt a direct port. We were convinced that a TMS grid for FireMonkey only made sense as a long term project in that it was rebuilt from the ground up designed in the spirit and according to the principles of the FireMonkey framework - being style-able and fully cross platform a fundamental requirement.
As a component developer, the FireMonkey framework is different from the VCL framework, so the TMS team first wet its feet with the development of several more lightweight FireMonkey components. Only after we felt sufficiently experienced and understoodthe inner workings of the FireMonkey framework, we started the architecting of a new grid. A solid house is built on strong foundations.
When we met to set the requirements of the new grid, this was the shortlist:
Being style-able as core feature: TMS Grid for FireMonkey in the IDE style editor
Architecture of the TMS Grid for FireMonkey
The grid consists of a data layer and a UI layer. The data layer takes care of memory management, organization, manipulations like sorting, grouping, filtering of cell data, i.e. cell content and cell properties. The UI layer handles the display of FireMonkey objects that represent the grid and takes care of dealing with all mouse & keyboard events.
In its basic layout, a grid is a matrix of cells with mainly fixed cells (not editable) and normal cells. The base cell FireMonkey object is TTMSFMXGridCell. A fixed cell will not scroll along with normal cells and thus remain visible on any of the 4 sides of the grid. This number of fixed rows and/or columns on the 4 sides of the grid is controlled by properties: grid.FixedRows, grid.FixedColumns, grid.FixedFooterRows, grid.FixedRightColumns. In addition to fixed, non-scrolling rows and/or columns, the grid can also perform column freezing. These are columns or rows of normal cells that will not scroll along with the other columns or rows in the grid. The number of freeze columns and rows is set with grid.FreezeColumns, grid.FreezeRows.
Cells are accessible via grid.Cells[Column,Row]:string and the selected cell(s) can be set with properties:
grid.Selection := CellRange(StartCol,StartRow,EndCol,EndRow);
grid.FocusedCell := Cell(Col,Row);
The grid features several selection modes: single cell selection, single row selection, single column selection, cell range selection, row range selection, column range selection, disjunct row selection, disjunct cell selection and disjunct column selection.
The selection mode is chosen with the property:
The scroll position in the grid can be programmatically set or retrieved via the properties grid.LeftCol: integer, grid.TopRow: integer. Note that scrolling in the grid can be performed in two ways: cell scrolling and pixel level scrolling. In cell scrolling mode, the minimum quantity of a scroll is an entire column or row, in pixel scrolling mode, scrolling is per pixel and can thus be done on sub cell level.
The scrolling mode is controlled by the property:
grid.ScrollMode = (smCellScrolling, smPixelScrolling)
The size of columns & rows is controlled by grid.ColumnWidths[ColumnIndex], grid.RowHeights[RowIndex] and it can be configured that the user can resize columns or rows at runtime with: grid.Options.ColumnSizing,grid.Options.FixedColumnSizing, grid.Options.RowSizing, grid.Options.FixedRowSizing.
Importing and exporting data with the TMS Grid for FireMonkey
The grid is able to import data from different file formats as well as export it to different formats.
The top left cell from where the loading of data starts is set with grid.IOOffset: TPoint. Typically this is set to the first normal cell in the grid, i.e.
grid.IOOffset := Point(grid.FixedColumns,grid.FixedRows);
Following formats and methods are available:
Sorting, grouping and filtering data in the grid
Grid grouped by column 1 and with nodes to expand/collaps groups
The grid has built-in sorting capabilities as well as filtering capabilities. Sorting can be performed from the UI by clicking the column header to perform a sort on a column and can be done programmatically as well.
Four types of sorting are available:
Single column sort
Multi column sort
Single column grouped sort
Multi column grouped sort
For single column sort, the sort is based on just a single column clicked. Clicking a new column will perform the sort on the new column clicked. In multi column sort mode, additional columns can be used as sort criteria by performing a shift-click on the column header. When the grid is grouped, the equivalent sorting capabilities are available but limited to groups, i.e. rows are sorted within groups rather than within the full grid.
Programmatically sorting can be done with:
grid.SortData(ColumnIndex, SortDirection); // single column sort
grid.SortIndexes.AddIndex(ColumnIndex1, SortDirection1); // multi column sort on column 1,2
Note that to control sorting from the UI, the events OnCanSortColumn, OnColumnSorted events are available.
To perform grouping of data on a column, simply call
and to undo the grouping, call:
Filtering can be performed programmatically. The filter conditions are added via the grid.Filter collection property and filtering is started by calling grid.ApplyFilter;
fltr := grid.Filter.Add;
fltr.Condition := 'Some condition';
fltr.Column := ColumnIndex1;
fltr.Condition := 'Other condition';
fltr.Column := ColumnIndex2;
fltr.Operation := foAND;
Cell properties and controls
In its most basic form, two types of cells exist, the fixed cells and normal cells. A normal cell can be in normal state, in selected state and in focused state. The style for these different states can be edited in the IDE via the style editor but can be programmatically accessed as well. To programmatically change the default style for a normal cell to make it appear with a yellow background color and red border, following code could be used:
grid.GetDefaultNormalLayout.Layout.Fill.Color := claYellow;
grid.GetDefaultNormalLayout.Layout.Stroke.Color := claRed;
The style of cells can also be dynamically changed with the event OnGetCellLayout. This example implementation of the OnGetCellLayout event will set the font color of cells with a value higher than 50 to red and as this is a column with numbers only, set its alignment to right justified:
procedure TForm1.TMSFMXGrid1GetCellLayout(Sender: TObject; ACol, ARow: Integer;
ALayout: TTMSFMXGridCellLayout; ACellState: TCellState);
if (ACol = 2) and (ARow >= TMSFMXGrid1.FixedRows) then
ALayout.TextAlign := TTextAlign.taTrailing;
if i > 50 then
ALayout.FontFill.Color := claRed;
Programmatic access to many cell appearance characteristics are also available:
grid.Alignments[ColumnIndex, RowIndex]: TTextAlign;
grid.Colors[ColumnIndex, RowIndex]: TAlphaColor;
grid.FontColors[ColumnIndex, RowIndex]: TAlphaColor;
grid.FontStyles[ColumnIndex, RowIndex]: TFontStyle;
grid.FontNames[ColumnIndex, RowIndex]: string;
grid.FontSizes[ColumnIndex, RowIndex]: integer;
The above information applies to default cells (in the grid of the class TTMSFMXGridCell) but you can actually add any type of FireMonkey TFMXObject as a cell in the grid.This can be done in following way with the event OnGetCellClass:
procedure TForm1.TMSFMXGrid1GetCellClass(Sender: TObject; ACol, ARow: Integer;
var CellClassType: TFmxObjectClass);
if (ACol = 3) and (ARow >= TMSFMXGrid1.FixedRows) then
CellClassType := TTMSFMXCheckGridCell;
if (ACol = 4) and (ARow >= TMSFMXGrid1.FixedRows) then
CellClassType := TButton;
This code snippet specifies a checkbox (a specific grid adapted checkbox that can have a background color) for column 3 and a button for column 4. Note that using OnGetCellClass means that the grid itself will be responsible for the creation of the cell control. It is equally possible to insert any type of control in a cell that was created outside the grid via the event OnGetCellControl.
This allows for an unprecedented flexibility to customize the grid. For example, inserting a grid in a grid cell is equally simple this way:
procedure TForm1.TMSFMXGrid1GetCellControl(Sender: TObject; ACol, ARow: Integer;
var AControl: TFmxObject);
if (ACol = 2) and (ARow = 2) then
AControl := MySubGrid;
Several methods are provided as well that allow to add controls to cells programmatically:
grid.CellControls[ColumnIndex, RowIndex]: TControl
Use of merged cells in the grid
The grid has built-in support for cell merging. Cell merging means that several adjacent cells are merged together to form a new single cell. The access to a merged cell is done via the top left cell this means the top left cell will be the visible cell of all the merged cells. Cell merging and the reverse action, cell splitting is easy:
grid.MergeCells(Col, Row, ColumnCount, RowCount: integer);
and several helper functions exist:
grid.IsMergedCell(), grid.RowSpan(), grid.ColSpan(), grid.BaseCell().
Editing the grid
The grid supports different inplace editors as well as the capability to choose any type of FireMonkey control as inplace cell edit control. By default, a cell is edited with a regular TEdit control. Different editor types can be selected from the built-in edit controls via the event OnGetCellEditorType:
procedure TForm1.TMSFMXGrid1GetCellEditorType(Sender: TObject; ACol,
ARow: Integer; var CellEditorType: TTMSFMXGridEditorType);
case acol of
1: CellEditorType := etNumericEdit;
2: CellEditorType := etNumericEditBtn;
3: CellEditorType := etComboBox;
4: CellEditorType := etDatePicker;
5: CellEditorType := etCustom;
The TTMSFMXGridEditorType defines many types of already built-in editor types in the grid. This includes different kind of editors for string, numeric, float, hex type, a spin editor, date picker, color picker, combobox, trackbar and dial. When using the OnGetCellEditorType, normally nothing else is required to handle editing. If in addition validation is needed, the event OnCellEditValidateData can be implemented. This event is triggered when editing is about to stop. It passes the new edited value together with an Allow parameter.
In this example event handler, the validation is performed that the maximum length cannot exceed 10:
procedure TForm1.TMSFMXGrid1CellEditValidateData(Sender: TObject; ACol,
ARow: Integer; CellEditor: TFmxObject; var CellString: string;
var Allow: Boolean);
Allow := Length(CellString) <= 10;
If another type of FireMonkey control is needed as inplace editor that is not listed in TTMSFMXGridEditorType, the CellEditorType can be set to etCustom. In this case, the grid will trigger the event OnGetCellEditorCustomClassType. In this event, it can be specified via the parameter CellEditorCustomClassType what the class is of the inplace editor:
procedure TForm1.TMSFMXGrid1GetCellEditorCustomClassType(Sender: TObject; ACol,
ARow: Integer; var CellEditorCustomClassType: TFmxObjectClass);
if ACol = 5 then
CellEditorCustomClassType := TMyVerySpecialCustomEditor;
To map the cell content to the custom inplace editor and vice versa, the events OnCellEditGetData, OnCellEditSetData can be used.
procedure TForm1.TMSFMXGrid1CellEditGetData(Sender: TObject; ACol,
ARow: Integer; CellEditor: TFmxObject; var CellString: string);
if (ACol = 5) then
(CellEditor as TMyVerySpecialCustomEditor).Data := CellString;
procedure TForm1.TMSFMXGrid1CellEditSetData(Sender: TObject; ACol,
CellString := (CellEditor as TMyVerySpecialCustomEditor).Data;
Printing, print preview, print to image
The included print preview dialog for the grid
Even though we’re living in the digital age, for a lot of people, data only exists when it is on paper. The grid being the primary tool of data presentation and organization features as such also a printing capability. The full grid or a range of cells can be printed. Printing is as easy as calling grid.Print but many more equivalent methods are available like: grid.PrintPageSelection(), grid.PrintPageFromTo() and many more to limit nr. of cells to print, limit nr. of pages etc.. As a helper, there is also a print preview dialog component: TTMSFMXPrintPreviewDialog that is simple in its use. Assign a grid to the TMSFMXPrintPreviewDialog.Grid property and call its Execute method.
Finally, the print output can also be rendered to an image file or a memory bitmap canvas. The method grid.PrintPageToImage(FileName:string) can generate .BMP, .PNG, .GIF and .JPG files. The file format simply depends on the extension of the filename specified.
The grid fully supports LiveBindings to display, navigate in and edit datasets. In XE3, the easiest way to do this is via Visual LiveBindings. To get started, drop a TTMSFMXGrid on the form and a dataset. Right-click on the form and select “Bind Visually” to start the Visual LiveBindings editor. Either connect the fields you want to have in the grid from the dataset to the grid or connect the ‘*’ item from the dataset to the ‘*’ item from the grid. This way, the grid will display all fields.
Visual LiveBindings editor in the IDE
The grid will automatically display Boolean fields as checkboxes, graphic or blob fields as images and memo fields as memo text in cells when possible. It will also by default select a numeric edit control as inplace editor for numeric fields, a datepicker for date fields or a memo for memo fields. You can override this at any time though by implementing the OnGetEditorType event.
Grid connected to a dataset: checkboxes for boolean fields, displays images from BLOBs
This is Visual LiveBindings with the TMS grid in its most straightforward form. Of course you can also use the grid in more complex binding expression setup scenarios. In the full grid documentation, it is covered, for example, how you can setup the grid and LiveBindings to have a combobox lookup inplace editor. It is also possible to automatically insert or remove records in the dataset from a bound grid when grid.Options.Keyboard.DeleteKeyHandling = dkhDeleteRow or grid.Options.Keyboard.InsertKeyHandling = dkhInsertRowAfter.
In this article, we have tried to give a background on the requirements and decisions that were made in the creation of the TMS Grid for FireMonkey and give an overview of its capabilities. Each topic covered in this overview article can be elaborated in much more detail and depth unveiling the power of the grid. Please see the TMS Grid for FireMonkey developers guide for this.
Download Delphi 10 now!
Webinars on demand!
More social media choices:
Delphi on Google+
@RADTools on Twitter
Server Response from: ETNASC03