Delphi 同步面板订单与 table 订单

Delphi synchronizing panels order with table order

我正在运行时生成面板。我在窗体上有一个面板,然后我在里面有一个 scrollBox alClient。在那个 scrollBox 里面,我动态地添加了面板。它们都属于Scrollbox,生成后我将它们对齐到底部。所以它们一个一个地出现。

我使用的代码如下:

Procedure TMain.AdaugaElement(numar:integer; tip:string);
var
  pan:TPanel;
  semn:TShape;
  pos:TPoint;
  lab:TLabel;
  myimg:TImage;
  bmpsep, dest:TBitmap;
begin

  pan:=TPanel.Create(self);
  pan.Parent:=ScrollBox1;
  pan.Align:=alBottom;
  pan.Height:=50;
  pan.Name:='Layeru'+IntToStr(numar);
  pan.Caption:='Elementul '+IntToStr(numar);
  pan.Font.Color:=[=12=]004F4F;
  pan.Font.Quality:=fqAntialiased;
  pan.Color:=[=12=]80FFFF;
  pan.OnMouseDown:=layerRowMouseDown;
  pan.ParentColor:=false;
  pan.ParentBackground:=false;
  pan.Color:=clWhite;

  Pos:= pan.ClientOrigin;

  vt.AppendRecord([vt.RecordCount+1,Pan.name,numar,numar,0 ,0, 20,20,800,600,pan.Caption,tip]);

  lab:=TLabel.Create(self);
  lab.Parent:=pan;
  lab.Left:=54;
  lab.Top:=16;
  lab.Font.Size:=10;
  lab.Font.Color:=clBlack;
  lab.Font.Style:=[];
  lab.Font.Quality:=fqAntialiased;
  lab.Caption:=IntToStr(numar);
  Randomize;
  lab.Name:='Layernumber'+IntToStr(numar);

  semn:=TShape.Create(self);
  semn.Parent:=pan;
  semn.Left:=3;
  semn.Top:=3;
  semn.Height:=44;
  semn.Width:=44;
  semn.Pen.Color:=[=12=]009D9D;
  semn.Brush.Color:=clWhite;

  myimg:=TImage.Create(self);
  myimg.Width:=42;
  myimg.Height:=42;
  myimg.Left:=4;
  myimg.Top:=4;
  myImg.Visible:=true;
  myimg.Proportional:=true;
  myimg.Stretch:=true;
  myImg.Parent:=pan;

  SelectLayerPan(numar);
end;

所以我添加了这样的面板:

AdaugaElement(1,'type1');
AdaugaElement(2,'type1');
AdaugaElement(3,'type1');
AdaugaElement(4,'type1');

接下来,我想使用鼠标在面板之间移动面板。所以最初我添加了:

- panel5
- panel4
- panel3
- panel2
- panel1 

(按顺序)

然后使用鼠标,我将 panel1 拖放到 panel4 和 panel5 之间。所以它们将显示为:

- panel5
- **panel1**
- panel4
- panel3
- panel2

(按顺序)

这个拖放是在 layerRowMouseDown 中完成的。

但是,由于在 virtualTable 中我有关于面板的信息(按原始顺序),所以

record1: panel1  pos:1
record2: panel2  pos:2
record3: panel3  pos:3
record4: panel4  pos:4
record5: panel5  pos:5

我必须识别移动的面板记录并更新它以反映其新位置,因此 VirtualTable 必须如下所示:

record1: panel1  pos:4
record2: panel2  pos:1
record3: panel3  pos:2
record4: panel4  pos:3
record5: panel5  pos:5

我该怎么做?

我的意思是,当我放下面板时,如何检测面板结束的位置?或者我如何获得面板在其容器 (ScrollBox1) 中的顺序?

到目前为止,我尝试使用 FindVCLWindow(pos) 使用面板的起始位置和高度的倍数来识别面板(以及它们在滚动框中的顺序),但是当有更多的面板时,它们就没有了不再直接可见(无需滚动),它显然停止正常工作。

所以这不是解决方案

这似乎是一个低效的解决方案。另外,为什么要重新发明轮子?

您所描述的与工具栏的工作方式非常相似,只是它是垂直布局而不是水平布局。我会查看一些工具栏的源代码,看看您是否不能使用已经设计好的东西来做您想要的事情。如果不出意外,您将获得一些关于如何实现您想要做的事情的好主意。

或者,我会考虑在滚动框内使用 TFlowPanel,您可以将面板放在流动面板上,而不是直接放在滚动框上。

编辑:对于投反对票的人,我深表歉意,但我们不允许指向站点外的东西,而且我不打算在这里 post 几个工具栏组件的所有代码。

TScrollBox 中面板的 Top 属性 似乎是面板顺序的唯一可靠指标。要查找面板的顺序,您可以将面板临时分配给一个列表,然后将该列表排序到顶部 属性。现在,它们在列表中的顺序与它们在滚动框中的显示顺序相同。

下面只是一个用临时列表排序的例子。您需要对其进行修改以适合您的目的。

procedure TForm6.UpdateRecords;
var
  Comparer: IComparer<TMyPanel>;
  i: integer;
  s: string;
begin
  Comparer := TDelegatedComparer<TMyPanel>.Create(
    function (const A, B: TMyPanel):integer
    begin
      Result := A.Top - B.Top;
    end);
  TempPanelList := TMyPanelList.Create(Comparer);
  try
    for i := 0 to ScrollBox1.ControlCount-1 do
      TempPanelList.Add(TMyPanel(ScrollBox1.Controls[i]));
    TempPanelList.Sort;
    for i := 0 to TempPanelList.Count-1 do
    begin
      // replace this with your own code, something like in next snippet
      s := s + ', ' + inttostr(TempPanelList.items[i].Tag);
      Label1.Caption := s;
      //
    end;
  finally
    TempPanelList.Free;
  end;
end;

然后要在列表中的面板和 vt 中的相应记录之间获得引用,您可以在创建面板时使用 tag 属性。您似乎将参数 numar 传递给 AdaugaElement 过程,因此假设这可以用作 vt 的索引,您可以将此 numar 分配给 pan.Tag.

最后,要更新 vt 中的 pos 字段,您可以在一个循环中分配面板的新位置,您将 TempPanelList 中的 TPanel 索引分配给 vt记录。 注意! 我不知道使用 VirtualTable

的正确语法
for i := 0 to TempPanelList.Count-1 do
begin
  vt[TPanel(TempPanelList[i]).Tag].pos := i;
end;

但是您的设置在很多帐户上似乎都非常脆弱,而以上内容对此没有任何帮助。您将 TMain 实例(一个表单?)指定为所有这些 TPanel、TShape、TLabel 和 TImage 的所有者。当用户添加和删除图层时,它变得非常混乱。相反,我会创建一个复合 class TLayerItem 来保存与一层相关的所有内容,并创建一个 LayerManager class 来管理 TLayerItems。