2011-11-28 64 views
4

我試圖實現MoveItemUp和MoveItemDown方法,將選定的行向上或向下移動到一個TCollection內的一個索引。如何重新排列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本身,這提供了一種方法做重新排列列表中的收集項目。我希望通過重新排序現有對象來完成此操作,而不創建,銷燬或分配任何對象。這可能來自Classes.pas TCollection的子類嗎? (如果沒有,我可能必須從源克隆中創建我自己的TCollection)

+10

設置集合項目的'Index'屬性應該執行'Item.Index:= Item.Index + 1'(這將調用集合的項目列表的「Move」)。如果需要進行特殊處理,則將重寫SetIndex方法。 –

回答

8

根據VCL源代碼,您不需要手動執行該操作。只需設置Index屬性,如@Sertac建議,它應該工作得很好。如果您有源代碼,請查看TCollectionItem.SetIndex的代碼。

+2

謝謝Sertac。這個答案基本上覆制了你的想法,但被張貼爲答案。\ –

+0

不客氣! –

4

您可以使用類似的東西 - 爲集合聲明一個虛擬類類型,並使用它來訪問該集合的內部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; 
+0

噢,這不是像使用我自己希望正確排序的類來破解私人成員那麼簡單嗎? –

+0

是的。這就是爲什麼我提到它是一種黑客行爲。在嘗試對集合的項目進行排序時,我無法使用'Index',並且交換未實現。 –

+0

向上/向下移動一個項目(我試圖做)似乎通過分配TcollectionItem.Index工作正常。 –

0

這是一個按照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.SortByDisplayNameMyCollectionItem.Collection.SortByDisplayName

+0

Embarcadero是否終於解決了只有一個類助手可以在範圍內的問題?如果沒有,那麼只有在TCollection範圍內沒有現有的幫助器時纔可以使用,如果將其他幫助器引入到範圍中,將來會中斷。 – Deltics

+0

@Deltics,類助手的範圍沒有改變。有一種可能性,以幫助者的子類,但你必須控制整個鏈。其他的可能性是像Ken展示的黑客攻擊,並製作一個全球性的程序,在一個集合上執行此操作,或者爲班級/記錄中的集合收集更多的「helper」rutines。 –

+0

**爲什麼不只是**:'爲i:= 0到Self.Count -1做SL.AddObject(Items [i] .DisplayName),Items [i]); SL.Sort; for i:= 0 to SL.Count -1 do TCollectionItem(SL.Objects [i])。Index:= i;' – Vassilis

相關問題