【问题标题】:TObjectList re-orderTObjectList 重新排序
【发布时间】:2015-10-11 13:24:17
【问题描述】:

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

所以我动态地将面板添加到 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 正在存储指向实际对象的指针,所以当我清除初始 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中,顺序和之前一样……

再次,我希望能够根据滚动框中的面板顺序对 ObjectList 进行排序。 在重新排序过程中,我实际上按所需顺序获得了所有面板。 我只需要在我的 ObjectList 中以相同的顺序排列它们。

还有其他方法吗?其他与我创建一个新类,该类将在 TPanel 旁边保存一个索引并在 ObjectList 中使用它来维护顺序?

【问题讨论】:

    标签: delphi-xe tobjectlist


    【解决方案1】:

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

    对于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;
    

    【讨论】:

    • 您的解决方案看起来很有趣,最重要的是,它不使用临时 ObjectList。我将对其进行测试,看看它是否有效并回复您。
    • 它很棒而且很优雅。谢谢您的回答。即使我不明白 PanelList.Sort() 的工作原理,它也能像魅力一样工作......你能详细说明一下吗?为了后代......
    • 我接受了您的回答,因为它比我的要优雅得多,并且使用的资源和代码行数更少。再次感谢
    【解决方案2】:

    我想我使用临时 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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-14
      • 2017-12-24
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多