Solved

Delphi 7 TListView

Posted on 2006-07-18
11
977 Views
Last Modified: 2010-04-05
Dear Experts,

I had a nearly similar post previously on this site.

-------Background--------
I was looking for a elegant way to order a listviews items according to the column that has been clicked.
It should order Integers, Float, String etc...
So I got a very nice ListViewSort unit which automates this entirely.
The implemention looks like this:
TListViewSort.CreateOwned(lsvDownloads, [stDate, stString, stString, stInteger, stInteger]);
And its working perfectly but I need to enhance this more.

-------Problem now-------
1. I need to show which column is sorted and also if ascending / descending by placing an arrow in the column header.
2. Also I need the form to remember which column was sorted last and whether ascending / descending if possible.
I would absolutely love to keep the current implementation because the ListSortView unit I got from some other expert is really making my life easy!!!


PLease help with example code and references etc..


Thanks in advance!
0
Comment
Question by:Marius0188
  • 4
  • 4
  • 3
11 Comments
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17134015
I find it better to stick the "sort arrow" above the column, as putting it inside the column looks icky
you can use an imageindex for putting it in the column though
here's my implementation.
I use the listview's "Tag" property to store teh current sort method and direction
make a TImageList (called ilSortImages) with an up and down arrow
put a TImage (called iSortImage) just above the listview with the same dimensions as the up/down arrow

add this code

// sorting constants
    st_Printed      = 0;
    st_ID           = 1;
    st_Till         = 2;
    st_Docket       = 3;
    st_Time         = 4;
    st_ProductCode  = 5;
    st_VariantCode  = 6;
    st_Description  = 7;
    st_Collected    = 8;
    st_ReverseOrder = 100; // add this to the sort type to get reversed



// in the sort code
        if listview1.Tag >= st_ReverseOrder then
          Compare := (0 - Compare); //reverse it


procedure TForm1.listview1ColumnClick(Sender: TObject; Column: TListColumn);
    begin
        if (listview1.Tag = Column.Index) then
          listview1.Tag := Column.Index + st_ReverseOrder
        else if (listview1.Tag = Column.Index + st_ReverseOrder) then
          listview1.Tag := Column.Index
        else
          listview1.Tag := Column.Index;

        RedrawColumnSortImage;
        listview1.CustomSort(nil, 0);
    end;


procedure TForm1.RedrawColumnSortImage;
    var
        i: integer;
        leftpos: integer;
        tempbmp: TBitmap;
        sortedcolumnindex: integer;
    begin
        sortedcolumnindex := (listview1.Tag mod st_ReverseOrder);

        leftpos := 0;

        for i := 0 to pred(listview1.Columns.Count) do
        begin
            if i = sortedcolumnindex then
            begin
                iSortImage.Left := listview1.Left +
                  leftpos - GetScrollPos(listview1.Handle, SB_HORZ) +
                  (listview1.Columns[i].Width div 2) - (iSortImage.Picture.Bitmap.Width div 2);

                if listview1.Tag >= st_ReverseOrder then
                begin
//                    listview1.Columns[i].ImageIndex := 1;
                    tempbmp := TBitmap.Create;
                    try
                        ilSortImages.GetBitmap(1, tempbmp);
                        iSortImage.Picture.Assign(tempbmp);
                    finally
                        tempbmp.free;
                    end;
                end
                else
                begin
//                    listview1.Columns[i].ImageIndex := 0;
                    tempbmp := TBitmap.Create;
                    try
                        ilSortImages.GetBitmap(0, tempbmp);
                        iSortImage.Picture.Assign(tempbmp);
                    finally
                        tempbmp.free;
                    end;
                end;
            end
            else
              listview1.Columns[i].ImageIndex := -1;
            leftpos := leftpos + listview1.Columns[i].Width;
        end;
    end;

procedure TForm1.lvBulkCollectionCustomDraw(Sender: TCustomListView; const ARect: TRect; var DefaultDraw: Boolean);
    begin
        RedrawColumnSortImage;
    end;

procedure TForm1.Create(Sender: TObject);
    begin
        RedrawColumnSortImage;
    end;

0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17134020
oops, that last procedure should say ListView1, not lvBulkCollection, it's from one of my apps
0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17134036
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17134060
Very minor work to do this.

1 - Expose an event that you bind to in order to catch the sorting change. Example below with code (my example changes the image index assigned to a column, assuming you place the up/down images into the image list).
2 - Expose the LastSort (column) as a public  property, or persist it from the OnSortChange event
3- Expose the Ascending value as a public proprerty, or persist it from the OnSortChange event


Regards,
Russell

-- example --

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, ImgList, ListViewSort;

type
  TForm1            = class(TForm)
     ImageList1:    TImageList;
     ListView1:     TListView;
     procedure      FormCreate(Sender: TObject);
  private
     // Private declarations
     FListSort:     TListViewSort;
     FAscending:    Boolean;
     FLastSort:     Integer;
  protected
     // Protected declarations
     procedure      OnSortChange(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean);
  public
    { Public declarations }
  end;

var
  Form1:      TForm1;

implementation
{$R *.DFM}

procedure TForm1.OnSortChange(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean);
begin

  // Update the last sorted column index
  FLastSort:=NewColumn;

  // Save ascending
  FAscending:=True;

  // Clear old sort column item index
  if not(OldColumn < 0) then TListView(Sender).Columns[OldColumn].ImageIndex:=(-1);

  // Set new sort column item index
  if not(NewColumn < 0) then
  begin
     if Ascending then
        TListView(Sender).Columns[NewColumn].ImageIndex:=0
     else
        TListView(Sender).Columns[NewColumn].ImageIndex:=1;
  end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

  FListSort:=TListViewSort.CreateOwned(ListView1, [stString, stString, stInteger]);
  FListSort.OnSortChange:=OnSortChange;
  FLastSort:=(-1);
  FAscending:=False;

end;

end.

-- updated unit --

unit ListViewSort;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  ListViewSort
//   Author      :  rllibby
//   Date        :  06.13.2006
//   Description :  Sorting class for list view control. Allows sorting to be
//                  handled for multiple columns (with different sorting), using
//                  minimal amount of code.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes, ComCtrls;

////////////////////////////////////////////////////////////////////////////////
//   Sorting types
////////////////////////////////////////////////////////////////////////////////
type
  TSortType         =  (stString, stInteger, stFloat, stDate, stTime, stDateTime);

////////////////////////////////////////////////////////////////////////////////
//   TListViewSort
////////////////////////////////////////////////////////////////////////////////
type
  TOnSortChange     =  procedure(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean) of object;
  TListViewSort     =  class(TComponent)
  private
     // Private declarations
     FListView:     TListView;
     FSortTypes:    TList;
     FLastSort:     Integer;
     FAscending:    Boolean;
     FOnSortChange: TOnSortChange;
  protected
     // Protected declarations
     function       GetSortType(Column: Integer): TSortType;
     procedure      OnColumnClick(Sender: TObject; Column: TListColumn);
  public
     // Public declarations
     constructor    CreateOwned(ListView: TListView; ColumnSorting: Array of TSortType);
     destructor     Destroy; override;
     procedure      ApplyLastSort;
     property       LastSort: Integer read FLastSort;
     property       IsAscending: Boolean read FAscending;
  published
     property       OnSortChange: TOnSortChange read FOnSortChange write FOnSortChange;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Utility functions
////////////////////////////////////////////////////////////////////////////////
function   MakeColumnSort(Column: Integer; Ascending: Boolean): Integer;
function   GetColumn(Value: Integer): Integer;
function   GetAscending(Value: Integer): Boolean;

implementation

//// Base sorting function /////////////////////////////////////////////////////
function LVBaseSort(lParam1, lParam2: TListItem; Column: Integer; Ascending: Boolean; SortType: TSortType): Integer;
var  szItem1:       String;
     szItem2:       String;
     dwSubItem:     Integer;
     dblResult:     Double;
begin

  // Check items
  if (lParam1 = nil) then
     result:=(-1)
  else if (lParam2 = nil) then
     result:=1
  else
  begin
     // Clear strings
     SetLength(szItem1, 0);
     SetLength(szItem2, 0);
     // Resource protection
     try
        // Get values to sort on
        if (Column = 0) then
        begin
           // Get values from caption
           szItem1:=lParam1.Caption;
           szItem2:=lParam2.Caption
        end
        else
        begin
           // Get subitem index
           dwSubItem:=Pred(Column);
           // Get subitems
           if (dwSubItem >= 0) then
           begin
              if (dwSubItem < lParam1.SubItems.Count) then szItem1:=lParam1.SubItems[dwSubItem];
              if (dwSubItem < lParam2.SubItems.Count) then szItem2:=lParam2.SubItems[dwSubItem];
           end;
        end;
        // Perform comparison
        case SortType of
           stString    :  result:=CompareStr(szItem1, szItem2);
           stInteger   :  result:=StrToInt(szItem1) - StrToInt(szItem2);
           stFloat     :
           begin
              dblResult:=StrToFloat(szItem1) - StrToFloat(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stDate      :
           begin
              dblResult:=StrToDate(szItem1) - StrToDate(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stTime      :
           begin
              dblResult:=StrToTime(szItem1) - StrToTime(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stDateTime  :
           begin
              dblResult:=StrToDateTime(szItem1) - StrToDateTime(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
        else
           // Sort by string
           result:=CompareStr(szItem1, szItem2);
        end;
     except
        // Conversion error, compare by string values
        result:=CompareStr(szItem1, szItem2);
     end;
  end;

  // Reverse the sort if not ascending
  if not(Ascending) then result:=result * (-1);

end;

//// Specialized sorting functions /////////////////////////////////////////////
function LVStringSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stString);
end;

function LVIntegerSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stInteger);
end;

function LVFloatSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stFloat);
end;

function LVDateSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stDate);
end;

function LVTimeSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stTime);
end;

function LVDateTimeSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stDateTime);
end;

//// TListViewSort /////////////////////////////////////////////////////////////
procedure TListViewSort.OnColumnClick(Sender: TObject; Column: TListColumn);
var  stColumn:      TSortType;
     lParam:        Integer;
begin

  // Check list view
  if Assigned(FListView) and Assigned(Column) then
  begin
     // Get sort type for the column
     stColumn:=GetSortType(Column.Index);
     // Resource protection
     try
        // Update the sorting direction
        if (Column.Index <> FLastSort) then
           // Ascending sort
           FAscending:=True
        else
           // Reverse the current sorting
           FAscending:=not(FAscending);
        // Encode into column and sort direction into an lParam
        lParam:=MakeColumnSort(Column.Index, FAscending);
        // Perform the sort
        case stColumn of
           stString    :  FListView.CustomSort(@LVStringSort, lParam);
           stInteger   :  FListView.CustomSort(@LVIntegerSort, lParam);
           stFloat     :  FListView.CustomSort(@LVFloatSort, lParam);
           stDate      :  FListView.CustomSort(@LVDateSort, lParam);
           stTime      :  FListView.CustomSort(@LVTimeSort, lParam);
           stDateTime  :  FListView.CustomSort(@LVDateTimeSort, lParam);
        else
           // Use string sorting
           FListView.CustomSort(@LVStringSort, lParam);
        end;
        // Fire the change if assigned
        if Assigned(FOnSortChange) then FOnSortChange(FListView, FLastSort, Column.Index, FAscending);
     finally
        // Save last sorted column index
        FLastSort:=Column.Index;
     end;
  end;

end;

function TListViewSort.GetSortType(Column: Integer): TSortType;
begin

  // Check column against the list count
  if (Column >= FSortTypes.Count) then
     // Default sorting will be by string
     result:=stString
  else
     // Return the stored sort type
     result:=TSortType(FSortTypes[Column]);

end;

procedure TListViewSort.ApplyLastSort;
var  stColumn:      TSortType;
     lParam:        Integer;
begin

  // Check list view and last sort column
  if Assigned(FListView) and (FLastSort >= 0) and (FLastSort < FListView.Columns.Count) then
  begin
     // Apply the last used sort against the list view; this is useful if the
     // list view data is reloaded (or newly loaded) and you wish to apply the previous
     // sort against the new data.
     stColumn:=GetSortType(FLastSort);
     // Encode into column and sort direction into an lParam
     lParam:=MakeColumnSort(FLastSort, FAscending);
     // Perform the sort
     case stColumn of
        stString    :  FListView.CustomSort(@LVStringSort, lParam);
        stInteger   :  FListView.CustomSort(@LVIntegerSort, lParam);
        stFloat     :  FListView.CustomSort(@LVFloatSort, lParam);
        stDate      :  FListView.CustomSort(@LVDateSort, lParam);
        stTime      :  FListView.CustomSort(@LVTimeSort, lParam);
        stDateTime  :  FListView.CustomSort(@LVDateTimeSort, lParam);
     else
        // Use string sorting
        FListView.CustomSort(@LVStringSort, lParam);
     end;
  end;

end;

constructor TListViewSort.CreateOwned(ListView: TListView; ColumnSorting: Array of TSortType);
var  dwIndex:       Integer;
begin

  // Perform inherited (list view becomes our owner)
  inherited Create(ListView);

  // Set defaults
  FSortTypes:=TList.Create;
  FLastSort:=(-1);
  FAscending:=True;

  // Add sorting types to the list
  for dwIndex:=0 to High(ColumnSorting) do FSortTypes.Add(Pointer(ColumnSorting[dwIndex]));

  // Save instance of list view
  FListView:=ListView;

  // Bind the OnColumnClick
  if Assigned(FListView) then FListView.OnColumnClick:=OnColumnClick;

end;

destructor TListViewSort.Destroy;
begin

  // Resource protection
  try
     // Unbind the list views OnColumnClick method
     if Assigned(FListView) then FListView.OnColumnClick:=nil;
     // Free the sorting list
     FSortTypes.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// Utility functions /////////////////////////////////////////////////////////
function MakeColumnSort(Column: Integer; Ascending: Boolean): Integer;
begin

  // Encode into integer
  if Ascending then
     result:=MakeLong(Word(Column), 1)
  else
     result:=MakeLong(Word(Column), 0);

end;

function GetColumn(Value: Integer): Integer;
begin

  // Decode from value
  result:=LoWord(Value);

end;

function GetAscending(Value: Integer): Boolean;
begin

  // Decode from value
  result:=(HiWord(Value) <> 0);

end;

end.
0
 

Author Comment

by:Marius0188
ID: 17134978
Hi rllibby,

I think you gave me this exact code from a previous comment of mine.
It is working excellent.

Ok but I have not noticed the "utility functions".
And it looks like it's what I need.

Delphi 7 would not compile with this line of code:
"  FListSort.OnSortChange := OnSortChange; "
Error: Undeclared identifier: 'OnSortChange'.
It seems like FListSort doent have such event?

Can you please advice.

Thanks!
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 26

Expert Comment

by:Russell Libby
ID: 17134991

The code is mine (yes I posted it in your original question). The code posted above is *NOT* the same as what you have, as you will see that it include the OnSortChange event and 2 new public properties. Did you replace the existing code with what I gave you above???


Russe;;
0
 

Author Comment

by:Marius0188
ID: 17135002
Here is my complete code in the oncreate of my frame:

--------------CODE START-------------

  FListSort := TListViewSort.CreateOwned(lsvDownloads, [stDate, stString, stString, stInteger, stInteger]);
//  FListSort.OnSortChange := OnSortChange; {****This line would not compile***}
  FLastSort := -1;
  FAscending := False;


-------------CODE END-----------------
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17135076
Thats not what I asked, so I'll ask again; did you replace your existing ListViewSort unit with the new ListViewSort code I supplied above?

From above

-- updated unit --

unit ListViewSort;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  ListViewSort
//   Author      :  rllibby
//   Date        :  06.13.2006
//   Description :  Sorting class for list view control. Allows sorting to be
//                  handled for multiple columns (with different sorting), using
//                  minimal amount of code.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes, ComCtrls;

////////////////////////////////////////////////////////////////////////////////
//   Sorting types
////////////////////////////////////////////////////////////////////////////////
type
  TSortType         =  (stString, stInteger, stFloat, stDate, stTime, stDateTime);

////////////////////////////////////////////////////////////////////////////////
//   TListViewSort
////////////////////////////////////////////////////////////////////////////////
type

// ******************** DECLARATION OF ONSORTCHANGE ****************************//

  TOnSortChange     =  procedure(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean) of object;

// ******************** END DECLARATION OF ONSORTCHANGE *************************//

  TListViewSort     =  class(TComponent)
  private
     // Private declarations
     FListView:     TListView;
     FSortTypes:    TList;
     FLastSort:     Integer;
     FAscending:    Boolean;
     FOnSortChange: TOnSortChange;
  protected
     // Protected declarations
     function       GetSortType(Column: Integer): TSortType;
     procedure      OnColumnClick(Sender: TObject; Column: TListColumn);
  public
     // Public declarations
     constructor    CreateOwned(ListView: TListView; ColumnSorting: Array of TSortType);
     destructor     Destroy; override;
     procedure      ApplyLastSort;
     property       LastSort: Integer read FLastSort;
     property       IsAscending: Boolean read FAscending;
  published
     property       OnSortChange: TOnSortChange read FOnSortChange write FOnSortChange;
  end;
0
 

Author Comment

by:Marius0188
ID: 17135089
Nope I didn't.
Thougth it's the same.

Ok now it's working. Thanks alot!

1. Another question. Can I programmatically control the ordering of a column?
    For example: If I want to order ListView by the Column[0] on a button's click?
    Please show example.

0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 250 total points
ID: 17135344
No, it wasn't the same.

As to the latest question; I will post example code first, then ListViewSort code next. Do *NOT* tell me the code is the same as what has been posted above, or what I gave you in a previous question; it is not. You need to take code from this comment and update your ListViewSort unit with it.

Russell

--- example code ---

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ListViewSort, StdCtrls, ImgList, ComCtrls;

type
  TForm1            = class(TForm)
     ImageList1:    TImageList;
     ListView1:     TListView;
     Button1:       TButton;
     procedure      FormCreate(Sender: TObject);
     procedure Button1Click(Sender: TObject);
  private
     // Private declarations
     FListSort:     TListViewSort;
     FAscending:    Boolean;
     FLastSort:     Integer;
  protected
     // Protected declarations
     procedure      OnSortChange(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean);
  public
    { Public declarations }
  end;

var
  Form1:      TForm1;

implementation
{$R *.DFM}

procedure TForm1.OnSortChange(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean);
begin

  // Update the last sorted column index
  FLastSort:=NewColumn;

  // Save ascending
  FAscending:=True;

  // Clear old sort column item index
  if not(OldColumn < 0) then TListView(Sender).Columns[OldColumn].ImageIndex:=(-1);

  // Set new sort column item index
  if not(NewColumn < 0) then
  begin
     if Ascending then
        TListView(Sender).Columns[NewColumn].ImageIndex:=0
     else
        TListView(Sender).Columns[NewColumn].ImageIndex:=1;
  end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

  FListSort:=TListViewSort.CreateOwned(ListView1, [stString, stString, stInteger]);
  FListSort.OnSortChange:=OnSortChange;
  FLastSort:=(-1);
  FAscending:=False;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin

  // Sort first column in ascending order
  FListSort.Sort(0, True);

end;

end.

--- updated ListViewSort ---

unit ListViewSort;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  ListViewSort
//   Author      :  rllibby
//   Date        :  06.13.2006
//   Description :  Sorting class for list view control. Allows sorting to be
//                  handled for multiple columns (with different sorting), using
//                  minimal amount of code.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes, ComCtrls;

////////////////////////////////////////////////////////////////////////////////
//   Sorting types
////////////////////////////////////////////////////////////////////////////////
type
  TSortType         =  (stString, stInteger, stFloat, stDate, stTime, stDateTime);

////////////////////////////////////////////////////////////////////////////////
//   TListViewSort
////////////////////////////////////////////////////////////////////////////////
type
  TOnSortChange     =  procedure(Sender: TObject; OldColumn, NewColumn: Integer; Ascending: Boolean) of object;
  TListViewSort     =  class(TComponent)
  private
     // Private declarations
     FListView:     TListView;
     FSortTypes:    TList;
     FLastSort:     Integer;
     FAscending:    Boolean;
     FOnSortChange: TOnSortChange;
  protected
     // Protected declarations
     function       GetSortType(Column: Integer): TSortType;
     procedure      OnColumnClick(Sender: TObject; Column: TListColumn);
  public
     // Public declarations
     constructor    CreateOwned(ListView: TListView; ColumnSorting: Array of TSortType);
     destructor     Destroy; override;
     procedure      ApplyLastSort;
     procedure      Sort(Column: Integer; Ascending: Boolean);
     property       LastSort: Integer read FLastSort;
     property       IsAscending: Boolean read FAscending;
  published
     property       OnSortChange: TOnSortChange read FOnSortChange write FOnSortChange;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Utility functions
////////////////////////////////////////////////////////////////////////////////
function   MakeColumnSort(Column: Integer; Ascending: Boolean): Integer;
function   GetColumn(Value: Integer): Integer;
function   GetAscending(Value: Integer): Boolean;

implementation

//// Base sorting function /////////////////////////////////////////////////////
function LVBaseSort(lParam1, lParam2: TListItem; Column: Integer; Ascending: Boolean; SortType: TSortType): Integer;
var  szItem1:       String;
     szItem2:       String;
     dwSubItem:     Integer;
     dblResult:     Double;
begin

  // Check items
  if (lParam1 = nil) then
     result:=(-1)
  else if (lParam2 = nil) then
     result:=1
  else
  begin
     // Clear strings
     SetLength(szItem1, 0);
     SetLength(szItem2, 0);
     // Resource protection
     try
        // Get values to sort on
        if (Column = 0) then
        begin
           // Get values from caption
           szItem1:=lParam1.Caption;
           szItem2:=lParam2.Caption
        end
        else
        begin
           // Get subitem index
           dwSubItem:=Pred(Column);
           // Get subitems
           if (dwSubItem >= 0) then
           begin
              if (dwSubItem < lParam1.SubItems.Count) then szItem1:=lParam1.SubItems[dwSubItem];
              if (dwSubItem < lParam2.SubItems.Count) then szItem2:=lParam2.SubItems[dwSubItem];
           end;
        end;
        // Perform comparison
        case SortType of
           stString    :  result:=CompareStr(szItem1, szItem2);
           stInteger   :  result:=StrToInt(szItem1) - StrToInt(szItem2);
           stFloat     :
           begin
              dblResult:=StrToFloat(szItem1) - StrToFloat(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stDate      :
           begin
              dblResult:=StrToDate(szItem1) - StrToDate(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stTime      :
           begin
              dblResult:=StrToTime(szItem1) - StrToTime(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
           stDateTime  :
           begin
              dblResult:=StrToDateTime(szItem1) - StrToDateTime(szItem2);
              if (dblResult > 0) then
                 result:=1
              else if (dblResult < 0) then
                 result:=(-1)
              else
                 result:=0;
           end;
        else
           // Sort by string
           result:=CompareStr(szItem1, szItem2);
        end;
     except
        // Conversion error, compare by string values
        result:=CompareStr(szItem1, szItem2);
     end;
  end;

  // Reverse the sort if not ascending
  if not(Ascending) then result:=result * (-1);

end;

//// Specialized sorting functions /////////////////////////////////////////////
function LVStringSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stString);
end;

function LVIntegerSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stInteger);
end;

function LVFloatSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stFloat);
end;

function LVDateSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stDate);
end;

function LVTimeSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stTime);
end;

function LVDateTimeSort(lParam1, lParam2: TListItem; lParamSort: Integer): Integer stdcall;
begin
  result:=LVBaseSort(lParam1, lParam2, GetColumn(lParamSort), GetAscending(lParamSort), stDateTime);
end;

//// TListViewSort /////////////////////////////////////////////////////////////
procedure TListViewSort.OnColumnClick(Sender: TObject; Column: TListColumn);
var  stColumn:      TSortType;
     dwOld:         Integer;
     lParam:        Integer;
begin

  // Check list view
  if Assigned(FListView) and Assigned(Column) then
  begin
     // Get sort type for the column
     stColumn:=GetSortType(Column.Index);
     // Resource protection
     try
        // Update the sorting direction
        if (Column.Index <> FLastSort) then
           // Ascending sort
           FAscending:=True
        else
           // Reverse the current sorting
           FAscending:=not(FAscending);
        // Encode into column and sort direction into an lParam
        lParam:=MakeColumnSort(Column.Index, FAscending);
        // Perform the sort
        case stColumn of
           stString    :  FListView.CustomSort(@LVStringSort, lParam);
           stInteger   :  FListView.CustomSort(@LVIntegerSort, lParam);
           stFloat     :  FListView.CustomSort(@LVFloatSort, lParam);
           stDate      :  FListView.CustomSort(@LVDateSort, lParam);
           stTime      :  FListView.CustomSort(@LVTimeSort, lParam);
           stDateTime  :  FListView.CustomSort(@LVDateTimeSort, lParam);
        else
           // Use string sorting
           FListView.CustomSort(@LVStringSort, lParam);
        end;
        // Save the last sort column
        dwOld:=FLastSort;
     finally
        // Save last sorted column index
        FLastSort:=Column.Index;
     end;
     // Fire the change if assigned
     if Assigned(FOnSortChange) then FOnSortChange(FListView, dwOld, Column.Index, FAscending);
  end;

end;

function TListViewSort.GetSortType(Column: Integer): TSortType;
begin

  // Check column against the list count
  if (Column >= FSortTypes.Count) then
     // Default sorting will be by string
     result:=stString
  else
     // Return the stored sort type
     result:=TSortType(FSortTypes[Column]);

end;

procedure TListViewSort.ApplyLastSort;
var  stColumn:      TSortType;
     lParam:        Integer;
begin

  // Check list view and last sort column
  if Assigned(FListView) and (FLastSort >= 0) and (FLastSort < FListView.Columns.Count) then
  begin
     // Apply the last used sort against the list view; this is useful if the
     // list view data is reloaded (or newly loaded) and you wish to apply the previous
     // sort against the new data.
     stColumn:=GetSortType(FLastSort);
     // Encode into column and sort direction into an lParam
     lParam:=MakeColumnSort(FLastSort, FAscending);
     // Perform the sort
     case stColumn of
        stString    :  FListView.CustomSort(@LVStringSort, lParam);
        stInteger   :  FListView.CustomSort(@LVIntegerSort, lParam);
        stFloat     :  FListView.CustomSort(@LVFloatSort, lParam);
        stDate      :  FListView.CustomSort(@LVDateSort, lParam);
        stTime      :  FListView.CustomSort(@LVTimeSort, lParam);
        stDateTime  :  FListView.CustomSort(@LVDateTimeSort, lParam);
     else
        // Use string sorting
        FListView.CustomSort(@LVStringSort, lParam);
     end;
     // Fire the change if assigned
     if Assigned(FOnSortChange) then FOnSortChange(FListView, FLastSort, FLastSort, FAscending);
  end;

end;

procedure TListViewSort.Sort(Column: Integer; Ascending: Boolean);
var  stColumn:      TSortType;
     dwOld:         Integer;
     lParam:        Integer;
begin

  // Check list view and sort column index
  if Assigned(FListView) and (Column >= 0) and (Column < FListView.Columns.Count) then
  begin
     // Apply the new sort against the listview
     stColumn:=GetSortType(Column);
     // Set new ascending
     FAscending:=Ascending;
     // Encode into column and sort direction into an lParam
     lParam:=MakeColumnSort(Column, FAscending);
     // Save last sort column
     dwOld:=FLastSort;
     // Resource protection
     try
        // Perform the sort
        case stColumn of
           stString    :  FListView.CustomSort(@LVStringSort, lParam);
           stInteger   :  FListView.CustomSort(@LVIntegerSort, lParam);
           stFloat     :  FListView.CustomSort(@LVFloatSort, lParam);
           stDate      :  FListView.CustomSort(@LVDateSort, lParam);
           stTime      :  FListView.CustomSort(@LVTimeSort, lParam);
           stDateTime  :  FListView.CustomSort(@LVDateTimeSort, lParam);
        else
           // Use string sorting
           FListView.CustomSort(@LVStringSort, lParam);
        end;
     finally
        // Update last sort column
        FLastSort:=Column;
     end;
     // Fire the change if assigned
     if Assigned(FOnSortChange) then FOnSortChange(FListView, dwOld, Column, FAscending);
  end;

end;

constructor TListViewSort.CreateOwned(ListView: TListView; ColumnSorting: Array of TSortType);
var  dwIndex:       Integer;
begin

  // Perform inherited (list view becomes our owner)
  inherited Create(ListView);

  // Set defaults
  FSortTypes:=TList.Create;
  FLastSort:=(-1);
  FAscending:=True;

  // Add sorting types to the list
  for dwIndex:=0 to High(ColumnSorting) do FSortTypes.Add(Pointer(ColumnSorting[dwIndex]));

  // Save instance of list view
  FListView:=ListView;

  // Bind the OnColumnClick
  if Assigned(FListView) then FListView.OnColumnClick:=OnColumnClick;

end;

destructor TListViewSort.Destroy;
begin

  // Resource protection
  try
     // Unbind the list views OnColumnClick method
     if Assigned(FListView) then FListView.OnColumnClick:=nil;
     // Free the sorting list
     FSortTypes.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// Utility functions /////////////////////////////////////////////////////////
function MakeColumnSort(Column: Integer; Ascending: Boolean): Integer;
begin

  // Encode into integer
  if Ascending then
     result:=MakeLong(Word(Column), 1)
  else
     result:=MakeLong(Word(Column), 0);

end;

function GetColumn(Value: Integer): Integer;
begin

  // Decode from value
  result:=LoWord(Value);

end;

function GetAscending(Value: Integer): Boolean;
begin

  // Decode from value
  result:=(HiWord(Value) <> 0);

end;

end.





0
 

Author Comment

by:Marius0188
ID: 17174801
Ok thanks, rlibby for your assistance and patience.
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Suggested Solutions

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now