Повторный заказ 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 порядок такой же, как и раньше...

Опять же, я хочу иметь возможность упорядочить ObjectList в соответствии с порядком панелей из scrollBox. В процедуре повторного заказа я фактически получаю все панели в нужном порядке. Мне просто нужно, чтобы они были в том же порядке в моем ObjectList.

Есть ли другой способ сделать это? Другой, что со мной, создающим новый класс, который будет содержать индекс рядом с TPanel и использовать его в ObjectList для поддержания порядка?

2 ответа

Решение

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 и 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;
Другие вопросы по тегам