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.
TeeGrid supports sorting data by column headers with visual indicators for sort direction.
Basic Sorting Setup
Enable sortable headers:
TeeGrid1.Header.Sortable := True;
This enables built-in sorting for simple data sources. For custom sorting logic, use a TSortableHeader render.
For full control over sorting behavior, create a custom sortable header:
uses
Tee.Grid.Header, System.Generics.Defaults;
procedure TForm1.FormCreate(Sender: TObject);
var
Sortable: TSortableHeader;
begin
// Create sortable header
Sortable := TSortableHeader.Create(TeeGrid1.Header.Changed);
// Set custom events
Sortable.OnCanSort := HeaderCanSortBy;
Sortable.OnSortBy := HeaderSortBy;
Sortable.OnSortState := HeaderSortState;
// Assign to grid header
TeeGrid1.Header.SortRender := Sortable;
end;
Sorting Events
OnCanSort
Control which columns can be sorted:
procedure TForm1.HeaderCanSortBy(const AColumn: TColumn; var CanSort: Boolean);
begin
// Disable sorting for specific column (index 4)
CanSort := (AColumn <> nil) and (AColumn.Index <> 4);
end;
OnSortBy
Implement the actual sorting logic:
procedure TForm1.HeaderSortBy(Sender: TObject; const AColumn: TColumn);
begin
// Toggle sort direction
if SortColumn = AColumn.Index then
SortAsc := not SortAsc
else
begin
SortColumn := AColumn.Index;
SortAsc := True; // Default to ascending on first click
end;
// Perform the sort
SortData(AColumn, SortAsc);
end;
OnSortState
Provide visual feedback for the current sort state:
procedure TForm1.HeaderSortState(const AColumn: TColumn; var State: TSortState);
begin
if SortColumn = AColumn.Index then
begin
if SortAsc then
State := TSortState.Ascending
else
State := TSortState.Descending;
end
else
State := TSortState.None;
end;
TSortState Values
The sort state determines the visual indicator:
TSortState.None - No sort indicator
TSortState.Ascending - Up arrow (△)
TSortState.Descending - Down arrow (▽)
Implementing Sort Logic
For array-based data, use TArray.Sort with a custom comparer:
type
TPersonField = (Name, Street, Number, BirthDate, Children, Height);
TPersonComparer = class(TComparer<TPerson>)
public
Ascending: Boolean;
PersonField: TPersonField;
function Compare(const Left, Right: TPerson): Integer; override;
end;
function TPersonComparer.Compare(const Left, Right: TPerson): Integer;
var
APerson, BPerson: TPerson;
begin
if Ascending then
begin
APerson := Left;
BPerson := Right;
end
else
begin
APerson := Right;
BPerson := Left;
end;
case PersonField of
Name: Result := CompareText(APerson.Name, BPerson.Name);
Street: Result := CompareText(APerson.Address.Street, BPerson.Address.Street);
Number: Result := CompareValue(APerson.Address.Number, BPerson.Address.Number);
BirthDate: Result := CompareValue(APerson.BirthDate, BPerson.BirthDate);
Children: Result := CompareValue(APerson.Children, BPerson.Children);
Height: Result := CompareValue(APerson.Height, BPerson.Height);
else
Result := 0;
end;
end;
procedure TForm1.SortData(const AColumn: TColumn; const Ascending: Boolean);
var
Comparer: TPersonComparer;
begin
Comparer := TPersonComparer.Create;
try
Comparer.Ascending := Ascending;
Comparer.PersonField := FieldOf(AColumn.Header.Text);
TArray.Sort<TPerson>(Persons, Comparer);
TeeGrid1.Invalidate; // Refresh display
finally
Comparer.Free;
end;
end;
Sorting Sub-Columns
Handle sorting for columns with sub-columns:
function ParentColumn(AGrid: TTeeGrid; AColumn: TColumn): TColumn;
var
i: Integer;
begin
if AColumn.Parent <> nil then
for i := 0 to AGrid.Columns.Count - 1 do
if AColumn.Parent = AGrid.Columns[i] then
Exit(AGrid.Columns[i]);
Result := nil;
end;
procedure TForm1.HeaderSortBy(Sender: TObject; const AColumn: TColumn);
var
ParentCol: TColumn;
begin
ParentCol := ParentColumn(TeeGrid1, AColumn);
if ParentCol <> nil then
begin
// Sorting a sub-column
if (SortColumn = ParentCol.Index) and (SortSubColumn = AColumn.Index) then
SortAsc := not SortAsc;
SortColumn := ParentCol.Index;
SortSubColumn := AColumn.Index;
end
else
begin
// Sorting a top-level column
if SortColumn = AColumn.Index then
SortAsc := not SortAsc
else
SortColumn := AColumn.Index;
end;
SortData(AColumn, SortAsc);
end;
Database Sorting
For database-backed grids, update the query:
procedure TForm1.HeaderSortBy(Sender: TObject; const AColumn: TColumn);
var
OrderBy: String;
begin
if SortAsc then
OrderBy := AColumn.Header.Text + ' ASC'
else
OrderBy := AColumn.Header.Text + ' DESC';
FDQuery1.Close;
FDQuery1.SQL.Text := 'SELECT * FROM Customers ORDER BY ' + OrderBy;
FDQuery1.Open;
end;
Customizing Sort Indicators
Customize the sort indicator appearance:
var
Sortable: TSortableHeader;
begin
Sortable := TSortableHeader.Create(TeeGrid1.Header.Changed);
Sortable.Size := 8; // Size of sort indicator (default: 6)
Sortable.Format.Font.Color := clBlue; // Color of indicator
TeeGrid1.Header.SortRender := Sortable;
end;
Multi-Column Sorting
For advanced scenarios, implement multi-column sorting by maintaining a list of sort columns:
type
TSortInfo = record
Column: TColumn;
Ascending: Boolean;
end;
var
SortColumns: TList<TSortInfo>;
procedure TForm1.HeaderSortBy(Sender: TObject; const AColumn: TColumn);
var
Info: TSortInfo;
begin
// Hold Shift to add secondary sort columns
if GetKeyState(VK_SHIFT) < 0 then
begin
Info.Column := AColumn;
Info.Ascending := True;
SortColumns.Add(Info);
end
else
begin
SortColumns.Clear;
Info.Column := AColumn;
Info.Ascending := True;
SortColumns.Add(Info);
end;
PerformMultiColumnSort;
end;
Best Practices
- Use
OnCanSort to disable sorting on columns where it doesn’t make sense (actions, images, etc.)
- Provide visual feedback with
OnSortState so users know the current sort order
- For large datasets, consider database-level sorting instead of in-memory sorting
- Cache sort column and direction in form fields for persistence
- Use
CompareText for case-insensitive string sorting
- Use
CompareValue from System.Math for numeric comparisons