TObjectList重新排序

时间:2015-07-21 21:39:00

标签: delphi-xe tobjectlist

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

所以我将面板添加到ScrollBox中。 当我添加它们时,我还按照它们在运行时添加的顺序将它们添加到ObjectList中,以备将来使用。然后我可以通过拖放重新组织scrollBox中的面板。 我希望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重新添加它们,但是当我这样做时,我收到一个错误:Access Violation和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存储指向实际对象的指针,所以当我清除初始ObjectList时,实际对象被释放,因此第二个ObjectList包含一个不再可行的指针列表...... 但那我怎么能实现我想要的呢?

所以在ButtonClick上,我按以下顺序得到一个包含面板的ObjectList:

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

在ScrollBox中拖放面板后,我最终会得到这样的订单(在ScrollBox中)

Panel3
panel1
Panel4
Panel2
Panel0

但在ObjectList中,顺序与之前相同......

同样,我希望能够根据scrollBox中面板的顺序对ObjectList进行排序。 在重新订购程序中,我实际上按所需顺序获得所有面板。 我只需要在我的ObjectList中以相同的顺序使用它们。

还有其他办法吗?还有哪些我创建了一个新类,它将在TPanel旁边保存一个索引并在ObjectList中使用它来维护订单?

2 个答案:

答案 0 :(得分:2)

TObjectList具有OwnsObjects属性,默认情况下为True。请确保将其设置为False,因为您不希望列表自动释放对象,因为它们归表单所有。

至于TObjectList的实际排序,请考虑使用其Sort()SortList()方法。在容器中根据需要重新定位Panel后,请致电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;

答案 1 :(得分:0)

我想我使用临时的ObjectList和Extract(Object)

找到了答案

我的代码似乎有效:

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;