TObjectList重新排序

问题描述:

我需要根据一些规则重新排序TObjectList。我怎样才能做到这一点?TObjectList重新排序

因此,我将面板以dinamically方式添加到ScrollBox。 当我添加它们时,我也将它们按照在运行时添加的顺序添加到ObjectList中,以供将来使用。然后,我可以通过拖放来重新组织滚动框中的面板。 我希望ObjectList能够镜像在拖放时在运行时设置的相同顺序。

这里是我的代码:

var 
    MainForm: TMainForm; 
    PanelList,PanelListTMP:TObjectList; 

implementation 
... 

procedure TMainForm.FormCreate(Sender: TObject); 
begin 
    PanelList:=TObjectList.Create; 
    PanelListTMP:=TObjectList.Create; 
end; 

procedure TMainForm.Button1Click(Sender: TObject); 
begin 
    AddPanel('0'); 
    AddPanel('1'); 
    AddPanel('2'); 
    AddPanel('3'); 
    AddPanel('4'); 
end; 

procedure TMainForm.Addpanel(what:string); 
var 
    pan:TPanel; 
    bv:TShape; 
begin 
    pan:=TPanel.Create(self); 
    pan.Parent:=TheContainer; 
    pan.Height:=50; 
    pan.BevelOuter:=bvNone; 
    pan.BorderStyle:=bsNone; 
    pan.Ctl3D:=false; 
    pan.Name:='LayerPan'+what; 
    pan.Caption:=what; 
    pan.Align:=alBottom; 
    pan.OnMouseDown:=panMouseDown; 
end; 

procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer); 
var 
    i:integer; 
    idu:String; 
    panui:TPanel; 
begin 
    panui:=Sender as TPanel; 
    panui.ParentColor:=false; 
    panui.BringToFront; 
    // DRAG DROP STUFF 
    ReleaseCapture; 
    panui.Perform(wm_nclbuttondown,HTCAPTION,0); 

    for i := 0 to MainForm.ComponentCount - 1 do 
    begin 
     if MainForm.Components[i] is TWinControl then 
     if TWinControl(MainForm.Components[i]) is TPanel then 
     if (TWinControl(MainForm.Components[i]) as TPanel).Parent=MainForm.TheContainer then 
      begin 
      (TWinControl(MainForm.Components[i]) as TPanel).Align:=alBottom; 
      end; 
    end; 
    TheContainer.ScrollInView(panui); 
    ReOrderPanels; 
end; 


Procedure TMainForm.ReOrderPanels; 
begin 

end; 

我应该在ReOrderPanels程序呢? 我正在考虑从下往上将ScrollBox面板送入新的TObjectList(PanelListTMP),清除PanelList并从PanelListTMP重新添加它们,但是当我这样做时,出现错误:访问冲突,以及EInvalidPointer - 无效指针操作

所以这是我的想法:

procedure TMainForm.ReOrderPanels; 
var 
    ctrl:TControl; 
    pos:TPoint; 
    pan:TPanel; 
    bad:boolean; 
    ord,i:integer; 
begin 
    memo2.Lines.Add('*** new order START'); 
    panelListTMP.Clear; 
// scroll top 
    TheContainer.VertScrollBar.Position := 0; 
    // scroll down 
    TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range; 
    // get panel 
    Pos:=TheContainer.ClientOrigin; 
    Pos.Y:=Pos.Y+TheContainer.Height-5; 
    ctrl := FindVCLWindow(pos) ; 
    if ctrl is TPanel then 
    if TPanel(ctrl).Parent = TheContainer then 
    begin 
     pan:=(ctrl as TPanel); 
     panelListTMP.Add(pan); 
    end; 

    ord:=1; 
    bad:=false; 
    repeat 
    repeat 
     Pos.Y:=pos.Y-1; 
    until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan); 
    if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then 
    begin 
     pan:=FindVCLWindow(pos) as TPanel; 
     containeru.VertScrollBar.Position := 0; 
     containeru.ScrollInView(pan); 
     ord:=ord+1; 
     panelListTMP.Add(pan); 
    end 
    else 
    bad:=true; 
    until bad=true; 

    // and now I do the swap between the ObjectLists... 
    panelList.Clear; 
    for i:=0 to PanelListTMP.Count-1 do 
    begin 
     (PanelListTMP.Items[i] as TPanel).Parent:=containeru; 
     panelList.Add(PanelListTMP.Items[i]); 
    end; 
end; 

所以,我认为,因为链表类是存储指向的实际对象,然后当我清除了最初的链表类,实际的对象释放,所以第二个ObjectList包含一个不再可行的指针列表... 但是,那么我怎么能达到我想要的?

所以在ButtonClick,我得到一个包含以下顺序面板一个链表类:

PanelList[0] - Panel0 
PanelList[1] - Panel1 
PanelList[2] - Panel2 
PanelList[3] - Panel3 
PanelList[4] - Panel4 

后我拖累 - 在滚动框里面滴板,我可以用这样的顺序结束(在滚动框)

Panel3 
panel1 
Panel4 
Panel2 
Panel0 

但在链表类,顺序和以前一样......

同样,我希望能够有链表类下令根据订单来自滚动框的面板。 在重新排序过程中,我实际上按照所需顺序获取所有面板。 我只需要在我的ObjectList中以相同的顺序。

有没有其他方式做到这一点?除此之外,我还创建了一个新的类,它可以在TPanel旁边放置一个索引,并在ObjectList中使用它来维护顺序?

TObjectList有一个OwnsObjects属性,默认为true。确保将其设置为False,因为您不希望列表自动释放由Form拥有的对象。

至于TObjectList的实际排序,请考虑使用它的Sort()SortList()方法。在您的容器中根据需要重新定位面板后,请致电Sort()SortList()。在排序迭代列表时,您提供的排序回调将一次给出两个对象指针。使用对象相对于彼此的当前位置来告诉列表他们应该出现的顺序。

尝试这样:

var 
    MainForm: TMainForm; 
    PanelList: TObjectList; 

implementation 

... 

procedure TMainForm.FormCreate(Sender: TObject); 
begin 
    PanelList := TObjectList.Create(False); 
end; 

procedure TMainForm.FormDestroy(Sender: TObject); 
begin 
    PanelList.Free; 
end; 

procedure TMainForm.Button1Click(Sender: TObject); 
begin 
    AddPanel('0'); 
    AddPanel('1'); 
    AddPanel('2'); 
    AddPanel('3'); 
    AddPanel('4'); 
end; 

procedure TMainForm.Addpanel(what: string); 
var 
    pan: TPanel; 
    bv: TShape; 
begin 
    pan := TPanel.Create(Self); 
    try 
    pan.Parent := TheContainer; 
    pan.Height := 50; 
    pan.BevelOuter := bvNone; 
    pan.BorderStyle := bsNone; 
    pan.Ctl3D := false; 
    pan.Name := 'LayerPan'+what; 
    pan.Caption := what; 
    pan.Align := alBottom; 
    pan.OnMouseDown := panMouseDown; 
    PanelList.Add(pan); 
    except 
    pan.Free; 
    raise; 
    end; 
end; 

procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
var 
    i: integer; 
    idu: String; 
    panui, pan: TPanel; 
    tmpList: TObjectList; 
begin 
    panui := Sender as TPanel; 
    panui.ParentColor := false; 
    panui.BringToFront; 

    // DRAG DROP STUFF 
    ReleaseCapture; 
    panui.Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0); 

    tmpList := TObjectList.Create(False); 
    try 
    for i := 0 to TheContainer.ControlCount - 1 do 
    begin 
     if TheContainer.Controls[i] is TPanel then 
     tmpList.Add(TPanel(TheContainer.Controls[i])); 
    end; 
    for i := 0 to tmpList.Count - 1 do 
     TPanel(tmpList[i]).Align := alBottom; 
    finally 
    tmpList.Free; 
    end; 

    TheContainer.ScrollInView(panui); 
    ReOrderPanels; 
end; 

function SortPanels(Item1, Item2: Pointer): Integer; 
begin 
    Result := TPanel(Item2).Top - TPanel(Item1).Top; 
end; 

procedure TMainForm.ReOrderPanels; 
begin 
    PanelList.Sort(SortPanels); 

    // Alternatively: 
    { 
    PanelList.SortList(
    function(Item1, Item2: Pointer): Integer; 
    begin 
     Result := TPanel(Item2).Top - TPanel(Item1).Top; 
    end 
); 
    } 
end; 
+0

你解决方案似乎很有趣,最重要的是,它不使用临时ObjectList。我会测试它看看它是否有效并回复你。 – user1137313

+0

这是真棒,如此优雅。谢谢您的回答。尽管我不明白PanelList.Sort()是如何工作的,但它的工作原理就像是一种魅力......你能详细说明一下吗?为后代... – user1137313

+0

我接受你的答案,因为它比我的更优雅,使用更少的资源和代码行。再次感谢 – user1137313

我想我找到我的答案使用临时链表类和提取物(对象)

我的代码,似乎工作是:

procedure TMainForm.ReOrderPanels; 
var 
    ctrl:TControl; 
    pos:TPoint; 
    pan,panx:TPanel; 
    bad:boolean; 
    ord,i:integer; 
begin 

    panelListTMP.Clear; 
    panelList.OwnsObjects:=false; 

// scroll top 
    TheContainer.VertScrollBar.Position := 0; 
    // scroll down 
    TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range; 
    // get panel 
    Pos:=TheContainer.ClientOrigin; 
    Pos.Y:=Pos.Y+TheContainer.Height-5; 
    ctrl := FindVCLWindow(pos) ; 
    if ctrl is TPanel then 
    if TPanel(ctrl).Parent = TheContainer then 
    begin 
     pan:=(ctrl as TPanel); 
     panelListTMP.Add(PanelList.Extract(pan) as TPanel); 
    end; 

    ord:=1; 
    bad:=false; 
    repeat 
    repeat 
    Pos.Y:=pos.Y-1; 
    until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan); 
    if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then 
    begin 
     pan:=FindVCLWindow(pos) as TPanel; 
     TheContainer.VertScrollBar.Position := 0; 
     TheContainer.ScrollInView(pan); 
     ord:=ord+1; 
     panelListTMP.Add(PanelList.Extract(pan) as TPanel); 
    end 
    else 
    bad:=true; 
    until bad=true; 
    panelList.Clear; 
    panelListTMP.OwnsObjects:=false; 

    i:=0; 
    while (PanelListTMP.Count<>0) do 
     panelList.Add(PanelListTMP.Extract(PanelListTMP.Items[i]) as TPanel); 

    panelList.OwnsObjects:=true; 
    panelListTmp.Clear; 
end;