是否可以使用自定义排序比较器对TListBox进行排序?

时间:2014-12-09 15:39:25

标签: delphi

我需要对TListBox进行排序,但我意识到修改我的代码需要做很多工作,如果我要说一个TStringList,对它进行排序,然后将这些项目复制到Listbox中。很多工作的主要原因是我在代码中有许多地方修改了列表框内容,我想我必须编辑它们以在添加,删除或其他任何内容时强制排序。

我更喜欢让我只使用自定义排序逻辑对列表框进行排序的方法。

有可能吗?

2 个答案:

答案 0 :(得分:5)

这不是问题!看看这个代码:

function CompareDates(List: TStringList; Index1, Index2: Integer): Integer;
var
  d1, d2: TDateTime;
begin
   d1 := StrToDate(List[Index1]);
   d2 := StrToDate(List[Index2]);
   if d1 < d2 then
     Result := -1
   else if d1 > d2 then 
     Result := 1
   else
     Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
begin
  sl := TStringList.Create; 
  try     
    sl.Assign(ListBox1.Items);
    sl.CustomSort(CompareDates);
    ListBox1.Items.Assign(sl);
  finally
    sl.Free
  end;
end;

答案 1 :(得分:0)

如果您使用的是Delphi XE或更高版本,我有可能为您服务。

请注意,我说的是“可能性”,而不是“解决方案”,因为它更像是一个黑客攻击,除非是最后的手段,否则我不会在生产代码中批准这个。

据我所知,您实际上想要实现的是覆盖Add函数(它是虚拟的)的行为,使其根据自定义顺序插入到正确的位置。 (如果你还需要覆盖插入,这也适用)。如果可以覆盖TLtrings后代的TListbox使用,那很简单,但我们没那么幸运。

Delphi XE引入了一个名为TVirtualMethodInterceptor(Rtti unit)的新类,它允许拦截虚拟方法以执行我们想要做的任何事情。我们可以检查和修改参数,调用其他功能,或者根本取消呼叫,什么都不做。

以下是我制作的概念证明:

//type
//  TCompareFunc<T1> = reference to function (const Arg1, Arg2 : T1) : Integer;
procedure TForm4.FormCreate(Sender: TObject);
var vCompareFunc : TCompareFunc<string>;
    RttiContext : TRttiContext;
    vAddMethod  : TRttiMethod;
    vRttiType : TRttiType;
begin
  RttiContext := TRttiContext.Create;
  vRttiType := RttiContext.GetType(TStrings);
  vAddMethod := vRttiType.GetMethod('Add');

  vCompareFunc :=  MyCompareFunc;
  Fvmi := TVirtualMethodInterceptor.Create(Listbox1.Items.ClassType);

  Fvmi.OnBefore :=   procedure(Instance: TObject; Method: TRttiMethod;
                        const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue)
                      var
                        idx : Integer;
                      begin
                        if Method = vAddMethod then
                        begin //if it's the Add method, map it to Insert at the right position
                          DoInvoke := False;
                          BinarySearch(TStrings(Instance), Args[0].AsString, vCompareFunc,idx);
                          TStrings(Instance).Insert(idx, Args[0].AsString);
                        end;
                      end;


  Fvmi.Proxify(Listbox1.Items);

end;

此概念证明拦截对TStrings.add的调用并将其映射到binarysearch / Insert,以便列表中的项始终按正确的顺序排列。这不会覆盖插入或分配功能,也不会覆盖修改列表的任何其他功能。如果您想使用这种方法,您需要覆盖所有“违规”功能。

免责声明:由于我从未真正使用过这种技术,因此不要将此示例视为TVirtualMethodInterceptor使用的黄金法则。它确实有效,但它可能具有性能影响或其他我不知道的。

需要提及的一点(来自Barry Kelly的博客,见下文)

  

TVirtualMethodInterceptor类没有一件事,但是,   是一种解开(取消提交)对象的方法。如果对象永远不会   没有被束缚,重要的是该物体不会比这更长久   拦截器,因为拦截器需要分配可执行文件   内存,以便创建重定向的小存根   方法调用事件。

如果你想深入挖掘,这里有一篇关于这个主题的非常好的文章: http://blog.barrkel.com/2010/09/virtual-method-interception.html