Как изменить порядок элементов в коллекции 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
,