Как изменить порядок элементов в коллекции TCollection?

Я пытаюсь реализовать методы MoveItemUp и MoveItemDown, которые перемещают выбранную строку вверх или вниз на один индекс в пределах TCollection,

Следующий код, добавленный в мой подкласс TCollection, не работает:

procedure TMyCollection.MoveRowDown(index: Integer);
var
 item:TCollectionItem;
begin
  if index>=Count-1 then exit;
  item := Self.Items[index];
  Self.Delete(index); // whoops this destroys the item above.
  Self.Insert(index+1);
  Self.SetItem(index+1,item); // this actually does an assign from a destroyed object.
end;

Я вполне уверен, что это должно быть возможно во время выполнения, так как это делается во время разработки самой Delphi IDE, которая предоставляет способ переупорядочить элементы Collection в списке. Я надеюсь сделать это, просто переупорядочив существующие объекты, не создавая, не уничтожая и не назначая какие-либо объекты. Возможно ли это из подкласса Classes.pas TCollection? (Если нет, возможно, мне придется сделать свою собственную TCollection из исходного клона)

3 ответа

Решение

Согласно источнику VCL, вам не нужно делать это вручную. Просто установите Index свойство, как @Sertac предложил, и оно должно работать просто отлично. Если у вас есть источник, проверьте код TCollectionItem.SetIndex,

Вы можете использовать что-то вроде этого - объявите фиктивный тип класса для коллекции и используйте его, чтобы получить доступ к внутреннему FItems из этой коллекции, которая является TList, Затем вы можете использовать TList.Exchange метод для обработки фактического перемещения (или любой другой функциональности TList, конечно).

type
  {$HINTS OFF}
  TCollectionHack = class(TPersistent)
  private
    FItemClass: TCollectionItemClass;
    FItems: TList;
  end;
  {$HINTS ON}

// In a method of your collection itself (eg., MoveItem or SwapItems or whatever)
var
  TempList: TList;
begin
  TempList := TCollectionHack(Self).FItems;
  TempList.Exchange(Index1, Index2);
end;

Вот вспомогательное решение для класса, которое сортирует по DisplayName: Вы можете улучшить сортировку, если хотите, я использовал TStringList для своей сортировки. Помощник класса доступен везде, где вы ссылаетесь на модуль, содержащий помощник класса, поэтому, если у вас есть служебный модуль, поместите его туда.

interface

  TCollectionHelper = class helper for TCollection    
  public    
    procedure SortByDisplayName;    
  end;

Implementation

procedure TCollectionHelper.SortByDisplayName;    
var i, Limit : integer;    
    SL: TStringList;    
begin    
  SL:= TStringList.Create;    
  try    
    for i := self.Count-1 downto 0 do    
      SL.AddObject(Items[i].DisplayName, Pointer(Items[i].ID));    
    SL.Sort;    
    Limit := SL.Count-1;    
    for i := 0 to Limit do    
      self.FindItemID(Integer(SL.Objects[i])).Index := i;    
  finally    
    SL.Free;    
  end;    
end;

Затем, чтобы использовать метод, просто представьте, что это метод класса TCollection. Это работает на любом подклассе TCollection.

MyCollection.SortByDisplayName или же MyCollectionItem.Collection.SortByDisplayName,

Другие вопросы по тегам