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;
SelectLayerPan
程序模拟 table 中的选定行,因此当我添加新面板时,它显示为 'selected'(它 'deselects' 所有面板然后它 'selects' 新面板)
vt
是一个 VirtualTable
,我在其中输入有关生成的面板的信息
所以我添加了这样的面板:
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。
我正在运行时生成面板。我在窗体上有一个面板,然后我在里面有一个 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;
SelectLayerPan
程序模拟 table 中的选定行,因此当我添加新面板时,它显示为 'selected'(它 'deselects' 所有面板然后它 'selects' 新面板)vt
是一个VirtualTable
,我在其中输入有关生成的面板的信息
所以我添加了这样的面板:
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。