Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Steema/TeeGrid-VCL-FMX-Samples/llms.txt
Use this file to discover all available pages before exploring further.
Overview
TeeGrid supports custom column sorting by clicking on column headers. You can implement your own sort logic for any data type or structure.
Enabling Sorting
uses
Tee.Grid.Header, Tee.Grid.Columns;
procedure TFormCustomSorting.FormCreate(Sender: TObject);
begin
// Enable header sorting
TeeGrid1.Header.Sortable := True;
// Create and assign sort render
TeeGrid1.Header.SortRender := CreateSortable;
// Set grid data
TeeGrid1.Data := TVirtualArrayData<TLocation>.Create(Locations);
end;
function TFormCustomSorting.CreateSortable: TSortableHeader;
begin
result := TSortableHeader.Create(TeeGrid1.Header.Changed);
// Optional: Customize appearance
result.Format.Brush.Color := TColors.Red;
result.Format.Stroke.Show;
// Set custom events
result.OnCanSort := CanSortBy;
result.OnSortBy := SortBy;
result.OnSortState := SortState;
end;
Implementing Sort Events
OnCanSort - Control Which Columns Are Sortable
procedure TFormCustomSorting.CanSortBy(const AColumn: TColumn;
var CanSort: Boolean);
begin
// Only allow sorting on specific columns
CanSort := (AColumn <> nil) and
(
(AColumn.Header.Text = 'Name') or
(AColumn.Header.Text = 'Country')
);
end;
OnSortState - Return Current Sort State
type
TLastSorted = record
public
Column: TColumn;
Ascending: Boolean;
end;
var
LastSorted: TLastSorted;
procedure TFormCustomSorting.SortState(const AColumn: TColumn;
var State: TSortState);
begin
if AColumn = LastSorted.Column then
if LastSorted.Ascending then
State := TSortState.Ascending
else
State := TSortState.Descending
else
State := TSortState.None;
end;
procedure TFormCustomSorting.SortBy(Sender: TObject; const AColumn: TColumn);
var
tmp: TSortState;
begin
// Get current sort order
SortState(AColumn, tmp);
// Toggle order (Ascending <-> Descending)
LastSorted.Ascending := tmp <> TSortState.Ascending;
// Sort the data
SortData(AColumn, LastSorted.Ascending);
// Remember last sorted column
LastSorted.Column := AColumn;
end;
Custom Sort Implementation
Using TComparer for Arrays
uses
System.Generics.Collections, System.Generics.Defaults;
type
TLocation = record
public
Name: String;
Country: String;
end;
TLocationField = (Name, Country);
TLocationComparer = class(TComparer<TLocation>)
public
Ascending: Boolean;
Field: TLocationField;
function Compare(const Left, Right: TLocation): Integer; override;
end;
function TLocationComparer.Compare(const Left, Right: TLocation): Integer;
var
A, B: String;
begin
if Field = TLocationField.Name then
begin
A := Left.Name;
B := Right.Name;
end
else
begin
A := Left.Country;
B := Right.Country;
end;
if Ascending then
result := CompareText(A, B)
else
result := CompareText(B, A);
end;
procedure TFormCustomSorting.SortData(const AColumn: TColumn;
const Ascending: Boolean);
var
Comparer: TLocationComparer;
begin
Comparer := TLocationComparer.Create;
try
Comparer.Ascending := Ascending;
if AColumn.Header.Text = 'Name' then
Comparer.Field := TLocationField.Name
else
Comparer.Field := TLocationField.Country;
// Sort the array in place
TArray.Sort<TLocation>(Locations, Comparer);
finally
Comparer.Free;
end;
end;
Complete Example
From Unit_Custom_Sorting.pas:
unit Unit_Custom_Sorting;
interface
uses
System.SysUtils, FMX.Forms, FMX.StdCtrls, FMX.Layouts,
FMXTee.Control, FMXTee.Grid,
Tee.Grid.Header, Tee.Grid.Columns, Tee.GridData.Rtti;
type
TLastSorted = record
public
Column: TColumn;
Ascending: Boolean;
end;
TFormCustomSorting = class(TForm)
TeeGrid1: TTeeGrid;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
private
LastSorted: TLastSorted;
procedure CanSortBy(const AColumn: TColumn; var CanSort: Boolean);
function CreateSortable: TSortableHeader;
procedure SortBy(Sender: TObject; const AColumn: TColumn);
procedure SortData(const AColumn: TColumn; const Ascending: Boolean);
procedure SortState(const AColumn: TColumn; var State: TSortState);
end;
implementation
uses
System.Generics.Collections, System.Generics.Defaults;
type
TLocation = record
public
Name: String;
Country: String;
end;
var
Locations: TArray<TLocation>;
procedure AddRandomData;
begin
SetLength(Locations, 10);
Locations[0].Name := 'New York';
Locations[1].Name := 'Barcelona';
Locations[2].Name := 'Tokyo';
Locations[3].Name := 'Sao Paulo';
Locations[4].Name := 'Santa Cruz';
Locations[5].Name := 'Oslo';
Locations[6].Name := 'Camberra';
Locations[7].Name := 'Delhi';
Locations[8].Name := 'Montreal';
Locations[9].Name := 'Beijing';
Locations[0].Country := 'USA';
Locations[1].Country := 'Catalonia';
Locations[2].Country := 'Japan';
Locations[3].Country := 'Brazil';
Locations[4].Country := 'USA';
Locations[5].Country := 'Norway';
Locations[6].Country := 'Australia';
Locations[7].Country := 'India';
Locations[8].Country := 'Canada';
Locations[9].Country := 'China';
end;
procedure TFormCustomSorting.FormCreate(Sender: TObject);
begin
AddRandomData;
// Enable header sorting
TeeGrid1.Header.Sortable := True;
TeeGrid1.Header.SortRender := CreateSortable;
// Bind data
TeeGrid1.Data := TVirtualArrayData<TLocation>.Create(Locations);
end;
function TFormCustomSorting.CreateSortable: TSortableHeader;
begin
result := TSortableHeader.Create(TeeGrid1.Header.Changed);
// Cosmetic customization
result.Format.Brush.Color := TColors.Red;
result.Format.Stroke.Show;
// Set events
result.OnCanSort := CanSortBy;
result.OnSortBy := SortBy;
result.OnSortState := SortState;
end;
procedure TFormCustomSorting.CanSortBy(const AColumn: TColumn;
var CanSort: Boolean);
begin
CanSort := (AColumn <> nil) and
(
(AColumn.Header.Text = 'Name') or
(AColumn.Header.Text = 'Country')
);
end;
procedure TFormCustomSorting.SortState(const AColumn: TColumn;
var State: TSortState);
begin
if AColumn = LastSorted.Column then
if LastSorted.Ascending then
State := TSortState.Ascending
else
State := TSortState.Descending
else
State := TSortState.None;
end;
procedure TFormCustomSorting.SortBy(Sender: TObject; const AColumn: TColumn);
var
tmp: TSortState;
begin
SortState(AColumn, tmp);
LastSorted.Ascending := tmp <> TSortState.Ascending;
SortData(AColumn, LastSorted.Ascending);
LastSorted.Column := AColumn;
end;
type
TLocationField = (Name, Country);
TLocationComparer = class(TComparer<TLocation>)
public
Ascending: Boolean;
Field: TLocationField;
function Compare(const Left, Right: TLocation): Integer; override;
end;
function TLocationComparer.Compare(const Left, Right: TLocation): Integer;
var
A, B: String;
begin
if Field = TLocationField.Name then
begin
A := Left.Name;
B := Right.Name;
end
else
begin
A := Left.Country;
B := Right.Country;
end;
if Ascending then
result := CompareText(A, B)
else
result := CompareText(B, A);
end;
procedure TFormCustomSorting.SortData(const AColumn: TColumn;
const Ascending: Boolean);
var
Comparer: TLocationComparer;
begin
Comparer := TLocationComparer.Create;
try
Comparer.Ascending := Ascending;
if AColumn.Header.Text = 'Name' then
Comparer.Field := TLocationField.Name
else
Comparer.Field := TLocationField.Country;
TArray.Sort<TLocation>(Locations, Comparer);
finally
Comparer.Free;
end;
end;
end.
Automatic Sorting with TeeBI
For automatic sorting without custom code, see the TeeBI demos. The BI.GridData.pas unit provides built-in sorting functionality.
uses
BI.Data.DB, BI.Grid.Data;
// TeeBI provides automatic sorting
TeeGrid1.Data := TBIGridData.FromList<TPerson>(MyPersons);
Database Sorting
For database grids, use the sortable dataset helpers:
uses
Tee.GridData.DB.Sortable;
// ClientDataSet
TSortableClientDataset.CreateSortable(TeeGrid1.Grid, ClientDataSet1);
// FireDAC
TSortableFireDACDataset.CreateSortable(TeeGrid1.Grid, FDMemTable1);
See the Database Grid example for details.
Visual Indicators
The TSortableHeader automatically displays:
- ▲ Triangle pointing up for ascending sort
- ▼ Triangle pointing down for descending sort
- No indicator for unsorted columns
Key Concepts
TSortState Enum
type
TSortState = (None, Ascending, Descending);
Sort Events Flow
- User clicks column header
OnCanSort - Check if column is sortable
OnSortBy - Perform the sort
OnSortState - Return current state (for visual indicator)
Custom Comparer Pattern
type
TMyComparer = class(TComparer<TMyType>)
public
Ascending: Boolean;
function Compare(const Left, Right: TMyType): Integer; override;
end;
Advanced Usage
Multi-Column Sort
type
TSortInfo = record
Column: TColumn;
Ascending: Boolean;
end;
var
SortColumns: TArray<TSortInfo>;
// Sort by multiple columns (e.g., Country then Name)
procedure MultiColumnSort;
begin
// Primary: Country
// Secondary: Name
// Implementation left as exercise
end;
Case-Sensitive Sort
function TLocationComparer.Compare(const Left, Right: TLocation): Integer;
begin
if Ascending then
result := CompareStr(Left.Name, Right.Name) // Case-sensitive
else
result := CompareStr(Right.Name, Left.Name);
end;
Numeric Sort
function TMyComparer.Compare(const Left, Right: TMyRecord): Integer;
begin
if Ascending then
begin
if Left.Value < Right.Value then result := -1
else if Left.Value > Right.Value then result := 1
else result := 0;
end
else
begin
if Left.Value > Right.Value then result := -1
else if Left.Value < Right.Value then result := 1
else result := 0;
end;
end;
Features Demonstrated
- Enabling sortable column headers
- Custom sort logic with TComparer
- Visual sort indicators (▲▼)
- Controlling which columns are sortable
- Toggling between ascending/descending
- Sorting arrays in place
Best Practices
- Implement OnCanSort to control which columns can be sorted
- Track sort state to toggle between ascending/descending
- Use TComparer for clean, reusable sort logic
- Sort in place when possible for best performance
- Refresh grid after sorting (usually automatic)
Next Steps