你现在要做的事情相对较为复杂。为了能够掌握这个问题,我建议你构建一组良好因素的低级帮助程序例程。然后你可以用简短、清晰的方法组合高级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);
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);
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.
case ColumnToSort of
代替那个冗长的if else
语句。回答您的问题:创建一个变量来存储顺序,它将具有值1或-1,并将“比较”值乘以该值。 - TLamaTListColumn
实现一个类助手,并将GetListHeaderSortState
、SetListHeaderSortState
和ListViewFromColumn
放入其中。大部分其他代码都是特定于此特定表单的,因此属于该表单。而那些不属于该表单的代码,我想是TfrmFind.Sort
和TfrmFind.ListViewCompare
。它们可以有用地存在于列表视图派生类中。我不想在这个特定的答案中这样做,因为它会使它变得更加复杂,正如我们从之前的问题中所知道的,这可能对 JH 没有帮助。 - David HeffernanTRzListView
和TListView
非常接近,以至于它们之间的差异并不重要。 - David Heffernan