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
Populates the columns collection with columns representing the data structure (fields, properties, etc.).
Retrieves the display value for a specific cell. This is the most frequently called method during rendering.
Returns the total number of rows in the data source. Return -1 if the count is unknown (streaming data).
Associates existing columns with data source fields. Used when columns are defined manually rather than auto-generated.
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:
Automatic via DataSource Property
Manual Creation
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:
Array of Records
TList of Classes
TObjectList
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:
Data Assignment
You set TeeGrid.DataSource or TeeGrid.Data.
Adapter Creation
If using DataSource, TeeGrid automatically creates the appropriate TVirtualData implementation based on the component type.
Column Generation
TeeGrid calls TVirtualData.AddColumns() to populate the Columns collection automatically.
Column Association
Each TColumn gets a TagObject linking it to the data source field or property.
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:
Database Fields
RTTI Members
String Columns
// 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
// TVirtualDataRtti creates columns from public fields/properties
for Member in RttiType.GetFields do
if IsVisible(Member) then
Column := Columns. Add ;
Column.Header.Text := Member. Name ;
Column.TagObject := Member; // Links column to member
// TVirtualStringData uses predefined columns
for I := 0 to ColumnCount - 1 do
Column := Columns. Add ;
Column.Header.Text := 'Column ' + IntToStr(I);
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:
Triggered when the data structure changes (columns added/removed, data reloaded). procedure DataRefresh (Sender: TObject);
begin
// Rebuild grid structure
end ;
Triggered when data values change but structure remains the same. procedure DataRepaint (Sender: TObject);
begin
// Redraw grid
end ;
Triggered when the current row changes. procedure ChangeRow ( const Sender: TObject; const ARow: Integer );
begin
// Update related UI
end ;
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.
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