Skip to main content

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.

Custom Sorting with TSortableHeader

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

Build docs developers (and LLMs) love