Column sorting in a listview : LUSENET : OCI Best Practices : One Thread

How to implement sorting on a column when a user clicks on the column title:
The only thing you have to implement the sorting is to implement the two events you can see further down on this page. I have included the help file for OnColumnClick and OnCompare. Do NOT change the value of the list view's SortType property. SortType should be stNone.

As you can see from OnColumnClick help, the event is fired when a user for example click on the column header (OBS when viewstyle is vsReport). The VCL architecture is then fireing the onCompare event for us. The onCompare is just returning whether an item is 'greater','less' or'equal' to another item. It is up to you to implement the 'greater','less','equal' logic.

I have also included an example from the MiscellaneousAncestorForm and how to typecaste your item in the listview to your specific needs.

Occurs when the user clicks on a column header in a list view.

type TLVColumnClickEvent = procedure(Sender: TObject; Column: TListColumn) of object; property OnColumnClick: TLVColumnClickEvent;


Write an OnColumnClick event handler to respond to mouse clicks made in the column header when ViewStyle is vsReport. The Column parameter is the TListColumn object from the Columns property that describes the column that was clicked. OnColumnClick only occurs when ViewStyle is vsReport, ShowColumnHeaders is True, and ColumnClick is True.

Occurs to when two items need to be compared during a sort of the list.

type TLVCompareEvent = procedure(Sender: TObject; Item1, Item2: TListItem; Data: Integer; var Compare: Integer) of object; property OnCompare: TLVCompareEvent;


Write an OnCompare event handler to implement a sort order for the list. An OnCompare event handler is called when the SortType property is stData or stBoth, when the AlphaSort method is called, or when the CustomSort method is called without a SortProc parameter.

The OnCompare event handler compares the list items passed as the Item1 and Item2 parameters. If Item1 is the same as Item2 in the sort order, set the Compare parameter to 0. If Item1 is less than Item2, set the Compare parameter to a value less than 0. If Item1 is greater than Item2, set the Compare parameter to a value greater than 0. The Data parameter is 0 when the event handler is called to maintain the sort order of a list view with a SortType of stData or stBoth. Similarly, when OnCompare occurs in response to the AlphaSort method, the Data parameter is 0. When OnCompare occurs in response to the CustomSort method, the Data parameter is the value of the LParam parameter of CustomSort.



procedure TMiscellaneousAncestorForm.ListViewNameHistoryCollectionColumnClick(
  Sender: TObject; Column: TListColumn);
  // The user clicked on the listview column. Have it sort passing the column
  // index of the selected column.
	ListViewNameHistoryCollection.CustomSort(NIL, Column.Index);


procedure TMiscellaneousAncestorForm.ListViewNameHistoryCollectionCompare(
  Sender: TObject; Item1, Item2: TListItem; Data: Integer;
  var Compare: Integer);
  // Sort the listview based on the value passed in Data. Data corresponds
  // to the column clicked by the user.
	case Data of
  	0: Compare := CompareStr(Item1.Caption, Item2.Caption);
    else Compare := CompareStr(Item1.SubItems.Strings[Data-1], Item2.SubItems.Strings[Data-1]);
    end; // case


What you can do if you do not want to compare the caption, you can typecast the to your 

specific item and access the element of you object instead.
For example:
  // (A < B) = minus value, (A = B) = 0, (A > B) = positive value
  Compare := TCompanyStatusItem( - TCompanyStatusItem( then

-- Anonymous, August 18, 1998


Here's a more complicated example. Here there are six columns. Some columns contain dates, and some dates allow nulls. (And date columns sort earliest dates to the bottom.)

var collectionItem1, collectionItem2: TProducerCollectionListingHistoryItem;
var d1, d2: TDateTime;
  collectionItem1 := TProducerCollectionListingHistoryItem(Item1.Data);
  collectionItem2 := TProducerCollectionListingHistoryItem(Item2.Data);
  case Data of
    0: Compare := (collectionItem1.IdCmp.Value - collectionItem2.IdCmp.Value);
    1: Compare := CompareText(Item1.SubItems.Strings[Data-1], Item2.SubItems.Strings[Data-1]);
    2: Compare := -CompareDate(collectionItem1.DateEffective.Value, collectionItem2.DateEffective.Value);
    3: begin
      // This column can contain null dates. Treat null dates as later than any other date.
      if (collectionItem1.DateEnded.IsNull)
        then d1 := EncodeDate(9999,12,31)
        else d1 := collectionItem1.DateEnded.Value;
      if (collectionItem2.DateEnded.IsNull)
        then d2 := EncodeDate(9999,12,31)
        else d2 := collectionItem2.DateEnded.Value;
      Compare := -CompareDate(d1, d2);
      end; // 3: begin
    4: Compare := CompareText(collectionItem1.CodeTerminated.Value, collectionItem2.CodeTerminated.Value);
    5: Compare := CompareText(Item1.SubItems.Strings[Data-1], Item2.SubItems.Strings[Data-1]);
    6: begin
      // This column can contain null dates. Treat null dates as later than any other date.
      if (collectionItem1.DateLastBilled.IsNull)
        then d1 := EncodeDate(9999,12,31)
        else d1 := collectionItem1.DateLastBilled.Value;
      if (collectionItem2.DateLastBilled.IsNull)
        then d2 :=EncodeDate(9999,12,31)
        else d2 := collectionItem2.DateLastBilled.Value;
      Compare := -CompareDate(d1, d2);
      end; // 6: begin
    end; // case

-- Anonymous, September 25, 1998

Moderation questions? read the FAQ