Question

I am trying to implement MoveItemUp and MoveItemDown methods that move a selected row up or down one index within a TCollection.

The following code added to my subclass of TCollection does not work:

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;

I am fairly sure this must be possible at runtime, as its done in designtime by the Delphi IDE itself which provides a way to reorder Collection items in a list. I am hoping to do this by simply reordering existing objects, without creating, destroying, or Assigning any objects. Is this possible from a subclass of Classes.pas TCollection? (If not, I may have to make my own TCollection from a source clone)

Was it helpful?

Solution

According to the VCL source, you don't need to manually do that. Simply set the Index property like @Sertac suggested and it should work just fine. If you have the source, check out the code of TCollectionItem.SetIndex.

OTHER TIPS

You can use something like this - declare a dummy class type for a collection, and use it to gain access to the internal FItems of that collection, which is a TList. You can then use the TList.Exchange method to handle the actual move (or any other functionality of the TList, of course).

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;

Here is a class helper solution that sorts by DisplayName: You can improve the sort if you like, I used a TStringList to do my sorting for me. The Class helper is available anywhere you reference the unit containing the class helper, so if you have a utility unit put it there.

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;

Then to use the method simply pretend it is a method of the TCollection class. This works on any subclass of TCollection as well.

MyCollection.SortByDisplayName or MyCollectionItem.Collection.SortByDisplayName.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top