Mirror

Sorting a TListView by the column clicked by the user (Views: 708)


Problem/Question/Abstract:

How to sort a ListView in ascending or descending order by a given column?

Answer:

Solve 1:

We want the following behaviour for a ListView:


When the user clicks on a column header, the ListView should be sorted by that column

The initial sort order should be ascending. If the user clicks on the same column again, the sort order should be toggled. If the user clicks on another column, the sort order of the new column should be the same as the last sorted column.


For the implementation we need two variables to hold the last column clicked by the user and the current sort order:

var
  LastSortedColumn: integer;
  Ascending: boolean;

We can initialize them when the form is created:

procedure TForm1.FormCreate(Sender: TObject);
begin
  LastSortedColumn := -1;
  Ascending := True;
end;

In the ColumnClick event of the ListView we determine the sort order and perform the sort:

procedure TForm1.ListView1ColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  if Column.Index = LastSortedColumn then
    Ascending := not Ascending
  else
    LastSortedColumn := Column.Index;
  TListView(Sender).CustomSort(@SortByColumn, Column.Index);
end;

SortByColumn is a function that should be previously declared and is the function used by CustomSort to compare two items. The value passed to the Data parameter of CustomSort will be passed as the Data parameter to SortByColumn and we use it for the sort column:

function SortByColumn(Item1, Item2: TListItem; Data: integer):
  integer; stdcall;
begin
  if Data = 0 then
    Result := AnsiCompareText(Item1.Caption, Item2.Caption)
  else
    Result := AnsiCompareText(Item1.SubItems[Data - 1],
      Item2.SubItems[Data - 1]);
  if not Ascending then
    Result := -Result;
end;

Solve 2:

Instead of calling the custum sort procedure we also can use the OnColumnClick and the OnCompare events ion conjunction... That way the compare routine is kept within the class, here a TForm decedant.

Like this:

procedure TfrmMain.lvThingyColumnClick(Sender: TObject; Column: TListColumn);
begin
  if Column.Index = fColumnClicked then
    fSortAscending := not fSortAscending
  else
  begin
    fSortAscending := true;
    fColumnClicked := Column.Index;
  end; // else

  Screen.Cursor := crHourglass;
  try
    (Sender as TListView).AlphaSort;
  finally
    Screen.Cursor := crDefault;
  end; // try
end;

procedure TfrmMain.lvThingyCompare(Sender: TObject; Item1, Item2: TListItem; Data:
  Integer; var Compare: Integer);
begin
  case fColumnClicked of
    0: Compare := CompareStr(Item1.Caption, Item2.Caption);
  else
    Compare := CompareStr(Item1.SubItems[fColumnClicked - 1],
      Item2.SubItems[fColumnClicked - 1]);
  end; // case
  if not fSortAscending then
    Compare := -Compare;
end;

<< Back to main page