Architecture
The DataGrid is built as three layers stacked on top of each other. Knowing which layer owns which job makes it easy to find APIs, write custom cells, and reason about performance.
The three layers
┌─────────────────────────────────────────┐
│ TTMSFNCDataGridRenderer │ ← drawing, layout, controls
│ ┌─────────────────────────────────┐ │
│ │ TTMSFNCDataGridData │ │ ← data, sort/filter/group
│ │ ┌─────────────────────────┐ │ │
│ │ │ TTMSFNCDataGridCore │ │ │ ← geometry, coordinates
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
| Layer | Class | Responsibility |
|---|---|---|
| Geometry | TTMSFNCDataGridCore |
Column and row counts, fixed/freeze regions, cell coordinates, dimensions. |
| Data | TTMSFNCDataGridData |
Cell values (TValue-based), formatting, sorting, filtering, grouping, calculations. |
| Drawing | TTMSFNCDataGridRenderer |
Layout calculation, cell painting, embedded controls, scrolling, selection visuals. |
The user-facing component, TTMSFNCDataGrid, wraps a root instance of TTMSFNCDataGridRenderer and exposes its API. When you embed a sub-grid in a cell (see Cell controls), a fresh TTMSFNCDataGridRenderer is created — so the same three-layer stack applies inside the nested grid too.
Why three layers?
The split lets each layer be reused without dragging the others along:
- Headless processing — the data layer can sort, filter, and group without ever drawing.
- Custom rendering — the drawing layer can be subclassed without touching how data is stored.
- Cross-platform —
TTMSFNCDataGridCorehas no FMX, VCL, or Web dependencies, so the same coordinate system works everywhere.
Where to subclass
| If you want to… | Subclass / override |
|---|---|
| Change how a cell is painted | TTMSFNCDataGridCell (see Custom cells) |
| Change how a cell is laid out | Hook OnGetCellLayout (see Appearance & theming) |
| Change how data is converted to display text | Hook OnGetCellFormatting (see Data, formatting & conversion) |
| Add a custom column type | Subclass TTMSFNCDataGridColumn |
| Build a master-detail UI | Use two adapter pairs (see Master-detail) |
Combining the three layers
The three layers are easiest to understand when you see them used together. Through TTMSFNCDataGrid you configure the geometry layer (column and row counts, fixed rows), the data layer (typed cell values and sorting) and the drawing layer (an OnGetCellLayout handler that recolours cells) without ever leaving one object. The same data layer is also available on its own as TTMSFNCDataGridData, which is what you want for background processing where nothing is drawn. Use the combined form when you build a visible grid, and the headless form when the result never reaches the screen. The example below does both, so you can compare which calls belong to which layer.
procedure TForm1.UseTheThreeLayers;
var
Data: TTMSFNCDataGridData;
begin
// Geometry layer: counts and fixed regions.
Grid.ColumnCount := 3;
Grid.FixedRowCount := 1;
Grid.RowCount := 4;
// Data layer: typed cell values and sorting.
Grid.Strings[0, 0] := 'Name';
Grid.Strings[1, 0] := 'Status';
Grid.Strings[2, 0] := 'Amount';
Grid.Strings[0, 1] := 'Alpha'; Grid.Strings[1, 1] := 'Overdue'; Grid.Floats[2, 1] := 120.5;
Grid.Strings[0, 2] := 'Beta'; Grid.Strings[1, 2] := 'Paid'; Grid.Floats[2, 2] := 80;
Grid.Strings[0, 3] := 'Gamma'; Grid.Strings[1, 3] := 'Overdue'; Grid.Floats[2, 3] := 310.2;
Grid.Sort(2, gsdDescending);
// Drawing layer: change how cells look without touching how they are stored.
Grid.OnGetCellLayout := GridGetCellLayout;
// The data layer also works on its own, with no drawing at all.
Data := TTMSFNCDataGridData.Create(Self);
try
Data.ColumnCount := 3;
Data.RowCount := 3;
Data.Floats[2, 0] := 5;
Data.Floats[2, 1] := 1;
Data.Floats[2, 2] := 3;
Data.Sort(2, gsdAscending);
finally
Data.Free;
end;
end;
procedure TForm1.GridGetCellLayout(Sender: TObject; ACell: TTMSFNCDataGridCell);
begin
if (ACell.Column = 1) and SameText(ACell.Text, 'Overdue') then
ACell.Layout.Fill.Color := gcMistyrose;
end;
void __fastcall TForm1::UseTheThreeLayers()
{
// Geometry layer: counts and fixed regions.
Grid->ColumnCount = 3;
Grid->FixedRowCount = 1;
Grid->RowCount = 4;
// Data layer: typed cell values and sorting.
Grid->Strings[0][0] = "Name";
Grid->Strings[1][0] = "Status";
Grid->Strings[2][0] = "Amount";
Grid->Strings[0][1] = "Alpha"; Grid->Strings[1][1] = "Overdue"; Grid->Floats[2][1] = 120.5;
Grid->Strings[0][2] = "Beta"; Grid->Strings[1][2] = "Paid"; Grid->Floats[2][2] = 80;
Grid->Strings[0][3] = "Gamma"; Grid->Strings[1][3] = "Overdue"; Grid->Floats[2][3] = 310.2;
Grid->Sort(2, gsdDescending);
// Drawing layer: change how cells look without touching how they are stored.
Grid->OnGetCellLayout = GridGetCellLayout;
// The data layer also works on its own, with no drawing at all.
TTMSFNCDataGridData *Data = new TTMSFNCDataGridData(this);
try
{
Data->ColumnCount = 3;
Data->RowCount = 3;
Data->Floats[2][0] = 5;
Data->Floats[2][1] = 1;
Data->Floats[2][2] = 3;
Data->Sort(2, gsdAscending);
}
__finally
{
delete Data;
}
}
void __fastcall TForm1::GridGetCellLayout(TObject *Sender, TTMSFNCDataGridCell *ACell)
{
if ((ACell->Column == 1) && SameText(ACell->Text, "Overdue"))
ACell->Layout->Fill->Color = gcMistyrose;
}
See also
- Cells, columns & coordinates — how the geometry layer addresses cells.
- Layout priority — how the drawing layer resolves cell appearance.
- Data, formatting & conversion — how the data layer stores and renders values.