Skip to main content

Overview

TeeGrid uses an abstraction layer called TVirtualData to connect to various data sources. This design allows the grid to work with databases, arrays, lists, and custom data sources through a unified interface.

The TVirtualData Abstract Class

TVirtualData is defined in Tee.GridData.pas and provides the contract that all data adapters must implement:
TVirtualData = class abstract
  procedure AddColumns(const AColumns: TColumns); virtual; abstract;
  function AsString(const AColumn: TColumn; const ARow: Integer): String; virtual; abstract;
  function AutoWidth(const APainter: TPainter; const AColumn: TColumn): Single; virtual; abstract;
  function Count: Integer; virtual; abstract;
  procedure Load(const AColumns: TColumns); virtual; abstract;
  procedure SetValue(const AColumn: TColumn; const ARow: Integer; const AText: String); virtual; abstract;
end;

Key Methods

AddColumns
procedure
Populates the columns collection with columns representing the data structure (fields, properties, etc.).
AsString
function
Retrieves the display value for a specific cell. This is the most frequently called method during rendering.
Count
function
Returns the total number of rows in the data source. Return -1 if the count is unknown (streaming data).
Load
procedure
Associates existing columns with data source fields. Used when columns are defined manually rather than auto-generated.
SetValue
procedure
Updates a cell value when the user edits it. Implement validation and data conversion here.

Built-in Data Adapters

TeeGrid provides several ready-to-use data adapters:

Database Binding (TVirtualDBData)

Connects to TDataSet or TDataSource components:
uses Tee.GridData.DB;

// Simplest approach - just set DataSource
TeeGrid1.DataSource := DataSource1;
// or
TeeGrid1.DataSource := FDMemTable1;
Features:
  • Automatic column creation from dataset fields
  • Native data type support (string, integer, float, date, etc.)
  • Edit support with automatic dataset posting
  • Master-detail relationships
  • Filtering and sorting integration
Connection Process:

Array and List Binding (TVirtualData<T>)

Connects to TArray<T> and TList<T> using RTTI:
type
  TPerson = record
    Name: string;
    Age: Integer;
    Email: string;
  end;

var
  People: TArray<TPerson>;
begin
  SetLength(People, 100);
  // ... populate array ...
  
  TeeGrid1.Data := TVirtualArrayData<TPerson>.Create(People);
end;
Features:
  • Automatic column creation from public fields and properties
  • RTTI-based value retrieval and setting
  • Support for nested objects
  • Configurable visibility levels (mvPublic, mvPublished)
  • Edit support with automatic value conversion

String Grid Binding (TVirtualStringData)

Emulates a string grid with Cells[Col,Row] indexing:
uses Tee.GridData.Strings;

var
  StringData: TVirtualStringData;
begin
  StringData := TVirtualStringData.Create;
  
  // Define columns
  StringData.Columns.Add('Name');
  StringData.Columns.Add('Email');
  StringData.Columns.Add('Phone');
  
  // Add rows
  StringData.RowCount := 100;
  
  // Set cell values
  StringData.Cells[0, 0] := 'John Smith';
  StringData.Cells[1, 0] := '[email protected]';
  StringData.Cells[2, 0] := '555-0100';
  
  TeeGrid1.Data := StringData;
end;

Data Binding Process

When you assign data to a grid, TeeGrid follows this sequence:
1

Data Assignment

You set TeeGrid.DataSource or TeeGrid.Data.
2

Adapter Creation

If using DataSource, TeeGrid automatically creates the appropriate TVirtualData implementation based on the component type.
3

Column Generation

TeeGrid calls TVirtualData.AddColumns() to populate the Columns collection automatically.
4

Column Association

Each TColumn gets a TagObject linking it to the data source field or property.
5

Initial Render

The grid renders using TVirtualData.AsString() to retrieve cell values.

Automatic Column Generation

When AddColumns is called, the data adapter creates columns based on the data structure:
// TVirtualDBData creates one column per field
for Field in DataSet.Fields do
  Column := Columns.Add;
  Column.Header.Text := Field.FieldName;
  Column.TagObject := Field; // Links column to field

Manual Column Configuration

You can define columns manually and then bind them to data:
// Define columns first
TeeGrid1.Columns.Clear;
TeeGrid1.Columns.Add('Customer Name');
TeeGrid1.Columns.Add('Amount');
TeeGrid1.Columns.Add('Date');

// Then assign data
TeeGrid1.DataSource := DataSource1;

// The Load method associates columns with fields
TeeGrid1.Data.Load(TeeGrid1.Columns);

Data Events

TVirtualData provides events to notify the grid of changes:
OnRefresh
TNotifyEvent
Triggered when the data structure changes (columns added/removed, data reloaded).
procedure DataRefresh(Sender: TObject);
begin
  // Rebuild grid structure
end;
OnRepaint
TNotifyEvent
Triggered when data values change but structure remains the same.
procedure DataRepaint(Sender: TObject);
begin
  // Redraw grid
end;
OnChangeRow
TRowChangedEvent
Triggered when the current row changes.
procedure ChangeRow(const Sender: TObject; const ARow: Integer);
begin
  // Update related UI
end;
OnEditing
TDataEditingEvent
Triggered when edit mode starts or ends.
procedure EditingChanged(const Sender: TObject; const IsEditing: Boolean);
begin
  if IsEditing then
    // Edit started
  else
    // Edit ended
end;

Creating Custom Data Adapters

You can create custom TVirtualData implementations for specialized data sources:
type
  TMyCustomData = class(TVirtualData)
  private
    FMyDataSource: TMyDataSource;
  protected
    function Empty: Boolean; override;
    function KnownCount: Boolean; override;
  public
    procedure AddColumns(const AColumns: TColumns); override;
    function AsString(const AColumn: TColumn; const ARow: Integer): String; override;
    function AutoWidth(const APainter: TPainter; const AColumn: TColumn): Single; override;
    function Count: Integer; override;
    procedure Load(const AColumns: TColumns); override;
    procedure SetValue(const AColumn: TColumn; const ARow: Integer; const AText: String); override;
  end;

implementation

function TMyCustomData.Count: Integer;
begin
  Result := FMyDataSource.RecordCount;
end;

function TMyCustomData.AsString(const AColumn: TColumn; const ARow: Integer): String;
begin
  // Retrieve value from your data source
  Result := FMyDataSource.GetValue(AColumn, ARow);
end;

procedure TMyCustomData.AddColumns(const AColumns: TColumns);
var
  Column: TColumn;
begin
  // Create columns based on your data structure
  for FieldName in FMyDataSource.FieldNames do
  begin
    Column := AColumns.Add;
    Column.Header.Text := FieldName;
    Column.TagObject := FMyDataSource.FieldByName(FieldName);
  end;
end;
Inherit from TVirtualDataRtti instead of TVirtualData if your data source contains objects or records - it provides RTTI-based helpers.

Performance Considerations

Only visible rows are rendered. The AsString method is called only for cells currently displayed on screen.
For expensive calculations, cache values in your TVirtualData implementation rather than recalculating on each AsString call.
Return -1 from Count for streaming data. TeeGrid will fetch rows on-demand without requiring the total count upfront.
Implement AutoWidth efficiently - it’s called once per column but may be expensive if it scans all rows.

Common Patterns

Read-Only Data

function TMyCustomData.ReadOnly(const AColumn: TColumn): Boolean;
begin
  Result := True; // All columns read-only
end;

Calculated Columns

function TMyCustomData.AsString(const AColumn: TColumn; const ARow: Integer): String;
begin
  if AColumn.Header.Text = 'Total' then
    Result := FloatToStr(GetUnitPrice(ARow) * GetQuantity(ARow))
  else
    Result := inherited AsString(AColumn, ARow);
end;

Data Validation

procedure TMyCustomData.SetValue(const AColumn: TColumn; const ARow: Integer; const AText: String);
var
  Value: Integer;
begin
  if AColumn.Header.Text = 'Age' then
  begin
    if TryStrToInt(AText, Value) and (Value >= 0) and (Value <= 150) then
      FData[ARow].Age := Value
    else
      raise Exception.Create('Invalid age value');
  end
  else
    inherited SetValue(AColumn, ARow, AText);
end;

Registration System

TeeGrid includes a registration system for automatic adapter selection:
initialization
  TVirtualDataClasses.Register(TVirtualDBData);
  TVirtualDataClasses.Register(TMyCustomData);
When you set DataSource, TeeGrid calls TVirtualDataClasses.Guess() to find a compatible adapter.

Next Steps

Virtual Data

Deep dive into TVirtualData implementations

Columns

Learn about the column system and hierarchy

Database Grids

Working with TDataSet and database components

Arrays & Lists

Binding to in-memory data structures

Build docs developers (and LLMs) love