あなたがやろうとしていることは今やかなり複雑です。これを維持できるようにするには、十分に因数分解された低レベルのヘルパールーチンのセットを作成することをお勧めします。次に、簡潔で明確なメソッドで高レベルのUIコードを作成できます。
まず、リストヘッダーの並べ替え状態を取得および設定するルーチンをいくつか用意します。これは、リストビューのヘッダーコントロールの上下の並べ替えアイコンです。
function ListViewFromColumn(Column: TListColumn): TListView;
begin
Result := (Column.Collection as TListColumns).Owner as TListView;
end;
type
THeaderSortState = (hssNone, hssAscending, hssDescending);
function GetListHeaderSortState(Column: TListColumn): THeaderSortState;
var
Header: HWND;
Item: THDItem;
begin
Header := ListView_GetHeader(ListViewFromColumn(Column).Handle);
ZeroMemory(@Item, SizeOf(Item));
Item.Mask := HDI_FORMAT;
Header_GetItem(Header, Column.Index, Item);
if Item.fmt and HDF_SORTUP<>0 then
Result := hssAscending
else if Item.fmt and HDF_SORTDOWN<>0 then
Result := hssDescending
else
Result := hssNone;
end;
procedure SetListHeaderSortState(Column: TListColumn; Value: THeaderSortState);
var
Header: HWND;
Item: THDItem;
begin
Header := ListView_GetHeader(ListViewFromColumn(Column).Handle);
ZeroMemory(@Item, SizeOf(Item));
Item.Mask := HDI_FORMAT;
Header_GetItem(Header, Column.Index, Item);
Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN);//remove both flags
case Value of
hssAscending:
Item.fmt := Item.fmt or HDF_SORTUP;
hssDescending:
Item.fmt := Item.fmt or HDF_SORTDOWN;
end;
Header_SetItem(Header, Column.Index, Item);
end;
私はこの答えからこのコードを取りました:TListView列にソート矢印を表示するにはどうすればよいですか?
次は、ソート仕様を保持するためのレコードを作成します。理想的には、これはそのData
パラメーターのソート比較関数に到達します。しかし悲しいことに、VCLフレームワークは、その意図された目的のためにそのパラメーターを使用する機会を逃しました。したがって、代わりに、アクティブな並べ替えの仕様をリストビューを所有するフォームに保存する必要があります。
type
TSortSpecification = record
Column: TListColumn;
Ascending: Boolean;
CompareItems: function(const s1, s2: string): Integer;
end;
次に、フォーム自体で、次のいずれかを保持するフィールドを宣言します。
type
TfrmFind = class(...)
private
....
FSortSpecification: TSortSpecification;
....
end;
比較関数は仕様を使用します。それは非常に簡単です:
procedure TfrmFind.ListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
var
Index: Integer;
s1, s2: string;
begin
Index := FSortSpecification.Column.Index;
if Index=0 then
begin
s1 := Item1.Caption;
s2 := Item2.Caption;
end else
begin
s1 := Item1.SubItems[Index-1];
s2 := Item2.SubItems[Index-1];
end;
Compare := FSortSpecification.CompareItems(s1, s2);
if not FSortSpecification.Ascending then
Compare := -Compare;
end;
次に、ソート関数を実装します。
procedure TfrmFind.Sort(Column: TListColumn; Ascending: Boolean);
var
ListView: TListView;
begin
FSortSpecification.Column := Column;
FSortSpecification.Ascending := Ascending;
case Column.Index of
1:
FSortSpecification.CompareItems := CompareTextAsInteger;
2:
FSortSpecification.CompareItems := CompareTextAsDateTime;
else
FSortSpecification.CompareItems := CompareText;
end;
ListView := ListViewFromColumn(Column);
ListView.OnCompare := ListViewCompare;
ListView.AlphaSort;
end;
この関数はハンドラーSort
から切り離されています。OnClick
これにより、ユーザーのUIアクションとは別に列を並べ替えることができます。たとえば、フォームを最初に表示するときに、特定の列のコントロールを並べ替えたい場合があります。
最後に、OnClick
ハンドラーはソート関数を呼び出すことができます。
procedure TfrmFind.lvwTagsColumnClick(Sender: TObject; Column: TListColumn);
var
i: Integer;
Ascending: Boolean;
State: THeaderSortState;
begin
Ascending := GetListHeaderSortState(Column)<>hssAscending;
Sort(Column, Ascending);
for i := 0 to ListView.Columns.Count-1 do
begin
if ListView.Column[i]=Column then
if Ascending then
State := hssAscending
else
State := hssDescending
else
State := hssNone;
SetListHeaderSortState(ListView.Column[i], State);
end;
end;
完全を期すために、これらのアイデアを実装する完全なユニットを次に示します。
unit uFind;
interface
uses
Windows, Messages, SysUtils, Classes, Math, DateUtils, Controls, Forms, Dialogs, ComCtrls, CommCtrl;
type
TSortSpecification = record
Column: TListColumn;
Ascending: Boolean;
CompareItems: function(const s1, s2: string): Integer;
end;
TfrmFind = class(TForm)
ListView: TListView;
procedure lvwTagsColumnClick(Sender: TObject; Column: TListColumn);
private
FSortSpecification: TSortSpecification;
procedure ListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure Sort(Column: TListColumn; Ascending: Boolean);
end;
var
frmFind: TfrmFind;
implementation
{$R *.dfm}
function CompareTextAsInteger(const s1, s2: string): Integer;
begin
Result := CompareValue(StrToInt(s1), StrToInt(s2));
end;
function CompareTextAsDateTime(const s1, s2: string): Integer;
begin
Result := CompareDateTime(StrToDateTime(s1), StrToDateTime(s2));
end;
function ListViewFromColumn(Column: TListColumn): TListView;
begin
Result := (Column.Collection as TListColumns).Owner as TListView;
end;
type
THeaderSortState = (hssNone, hssAscending, hssDescending);
function GetListHeaderSortState(Column: TListColumn): THeaderSortState;
var
Header: HWND;
Item: THDItem;
begin
Header := ListView_GetHeader(ListViewFromColumn(Column).Handle);
ZeroMemory(@Item, SizeOf(Item));
Item.Mask := HDI_FORMAT;
Header_GetItem(Header, Column.Index, Item);
if Item.fmt and HDF_SORTUP<>0 then
Result := hssAscending
else if Item.fmt and HDF_SORTDOWN<>0 then
Result := hssDescending
else
Result := hssNone;
end;
procedure SetListHeaderSortState(Column: TListColumn; Value: THeaderSortState);
var
Header: HWND;
Item: THDItem;
begin
Header := ListView_GetHeader(ListViewFromColumn(Column).Handle);
ZeroMemory(@Item, SizeOf(Item));
Item.Mask := HDI_FORMAT;
Header_GetItem(Header, Column.Index, Item);
Item.fmt := Item.fmt and not (HDF_SORTUP or HDF_SORTDOWN);//remove both flags
case Value of
hssAscending:
Item.fmt := Item.fmt or HDF_SORTUP;
hssDescending:
Item.fmt := Item.fmt or HDF_SORTDOWN;
end;
Header_SetItem(Header, Column.Index, Item);
end;
procedure TfrmFind.ListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
var
Index: Integer;
s1, s2: string;
begin
Index := FSortSpecification.Column.Index;
if Index=0 then
begin
s1 := Item1.Caption;
s2 := Item2.Caption;
end else
begin
s1 := Item1.SubItems[Index-1];
s2 := Item2.SubItems[Index-1];
end;
Compare := FSortSpecification.CompareItems(s1, s2);
if not FSortSpecification.Ascending then
Compare := -Compare;
end;
procedure TfrmFind.Sort(Column: TListColumn; Ascending: Boolean);
var
ListView: TListView;
begin
FSortSpecification.Column := Column;
FSortSpecification.Ascending := Ascending;
case Column.Index of
1:
FSortSpecification.CompareItems := CompareTextAsInteger;
2:
FSortSpecification.CompareItems := CompareTextAsDateTime;
else
FSortSpecification.CompareItems := CompareText;
end;
ListView := ListViewFromColumn(Column);
ListView.OnCompare := ListViewCompare;
ListView.AlphaSort;
end;
procedure TfrmFind.lvwTagsColumnClick(Sender: TObject; Column: TListColumn);
var
i: Integer;
Ascending: Boolean;
State: THeaderSortState;
begin
Ascending := GetListHeaderSortState(Column)<>hssAscending;
Sort(Column, Ascending);
for i := 0 to ListView.Columns.Count-1 do
begin
if ListView.Column[i]=Column then
if Ascending then
State := hssAscending
else
State := hssDescending
else
State := hssNone;
SetListHeaderSortState(ListView.Column[i], State);
end;
end;
end.