12

作为一种自学练习,我制作了一个表格,其中包含 2x3 矩形中的六个面板,我希望它们一个接一个地在可见和不可见之间切换。我试图通过使用某种 for 循环来做到这一点。我当然可以写类似的东西:

Panel1.Visible := true;
Panel1.Visible := false;
Panel2.Visible := true;
Panel2.Visible := false;
Panel3.Visible := true;
etc. etc.

但这需要大量的输入,并且当我决定希望它在每一步之间等待 100 毫秒时效率非常低。例如,我必须编辑所有六个步骤以等待。这六个步骤是可行的,但也许下次我想做一百次!因此,我认为还必须有一种方法可以为此使用 for 循环,其中变量从 1 到 6 不等,并在对象标识符中使用。所以它会是这样的:

for variable := 1 to 6 do begin
Panel + variable.Visible := true;
Panel + variable.Visible := false;
end;

现在,这显然行不通,但我希望这里有人可以告诉我这是否真的可行,如果可以,如何。也许我可以使用字符串作为标识符?我的解释可能很糟糕,因为我不知道所有的技术术语,但我希望代码能解释一些东西。

4

5 回答 5

23

您可以循环访问面板的所有者Components数组。

var
  i: Integer;
  TmpPanel: TPanel;
begin
  { This example loops through all of the components on the form, and toggles the
    Visible property of each panel to the value that is opposite of what it has (IOW,
    if it's True it's switched to False, if it's False it's switched to True). }
  for i := 0 to ComponentCount - 1 do                  
    if Components[i] is TPanel then                    
    begin
      TmpPanel := TPanel(Components[i]);
      TmpPanel.Visible := not TmpPanel.Visible;     // Toggles between true and false
    end;
end;

FindComponent如果您想要一个非常特定类型的组件名称,您也可以使用该方法。例如,如果您有 6 个面板,它们的名称是Panel1Panel2等:

var
  i: Integer;
  TmpPanel: TPanel;
begin
  for i := 1 to 6 do
  begin
    TmpPanel := FindComponent('Panel' + IntToStr(i)) as TPanel;
    if TmpPanel <> nil then      // We found it
      TmpPanel.Visible := not TmpPanel.Visible;
  end;
end;
于 2012-11-30T16:39:48.590 回答
6

在这种情况下,您希望在运行时而不是在设计时动态创建控件。试图应对 6 个不同的变量将是一个痛苦的世界。当您需要网格为 3x4 而不是 2x3 时,您会更加后悔这个决定。

所以,从一个完全空白的表格开始。并在代码中添加一个二维面板数组:

private
  FPanels: array of array of TPanel;

然后,在表单的构造函数或OnCreate事件处理程序中,您可以通过调用如下函数来初始化数组:

procedure TMyForm.InitialisePanels(RowCount, ColCount: Integer);
var
  Row, Col: Integer;
  aLeft, aTop, aWidth, aHeight: Integer;
  Panel: TPanel;
begin
  SetLength(FPanels, RowCount, ColCount);
  aTop := 0;
  for Row := 0 to RowCount-1 do begin
    aLeft := 0;
    aHeight := (ClientHeight-aTop) div (RowCount-Row);
    for Col := 0 to ColCount-1 do begin
      Panel := TPanel.Create(Self);
      FPanels[Row, Col] := Panel;
      Panel.Parent := Self;
      aWidth := (ClientWidth-aLeft) div (ColCount-Col);
      Panel.SetBounds(aLeft, aTop, aWidth, aHeight);
      inc(aLeft, aWidth);
    end;
    inc(aTop, aHeight);
  end;
end;

现在您可以使用笛卡尔坐标而不是平面一维数组来引用您的面板。当然,如果需要,您也可以轻松地声明一个平面一维数组。

关键思想是,当您在结构化布局中创建大量控件时,最好放弃设计器并使用代码(循环和数组)。

于 2012-11-30T16:54:56.443 回答
5

使用FindComponent方法TComponent

  for variable := 1 to 6 do begin
    pnl := FindComponent('Panel' + IntToStr(variable));
    if pnl is TPanel then
    begin
      TPanel(pnl).Visible := true;
      TPanel(pnl).Visible := false;
    end;
  end;
于 2012-11-30T16:43:50.163 回答
0

正如其他人所回答的那样, FindComponent 是要走的路。

但是如果你只是想修改组件的通用属性,例如可见、位置等,则不需要与类型进行比较。

这将同样有效:

  for i := 1 to 16 do
  begin
    (FindComponent( 'P' + inttostr(i) ) as TControl).Visible := false;
  end;

(注意:这是针对 Delphi 6/ 7,现代版本可能以其他方式执行此操作)

于 2020-01-17T22:08:21.807 回答
0

其实我的回答

如果您使用命名约定来命名您的组件,例如

 "Mycomponent" + inttostr(global_int)

你可以用它很容易地找到它:

function getMyComponent(id:integer) : TComponent;
begin
result := {Owner.}FindConponent('MyComponent'+inttostr(id));
end;

(sender as TComponent).name您还可以通过使用知道哪些其他组件与他相关来使您生成的组件相互交互。


示例

以下是您可以使用它执行的操作的示例:

想象一个页面控件,其中选项卡是您想要多次使用的界面(例如,用 1 tab = 1 col 描述文件中的列,并且您想要动态添加选项卡)。

对于我们的示例,我们正在命名按钮并以这种方式编辑:

Button : "C_(column_number)_btn"
Edit   : "C_(column_number)_edi"

实际上,您可以使用 buttonclick 直接引用编辑,在运行时通过调用 findcomponent 链接:

procedure TForm1.ColBtnClick(Sender:TObject);
var nr : string; Edit : TEdit; 
begin
// Name of the TButton.  C(col)_btn
nr := (Sender as TButton).Name;

// Name of the TEdit  C_(column)_edi
nr := copy(nr,1,length(nr)-3)+'edi';

// Get the edit component.
edit := (Form1.Findcomponent(nr) as TEdit);

//play with it
Edit.Enabled := Not Edit.Enabled ;
showmessage(Edit.Text);
Edit.hint := 'this hint have been set by clicking on the button';

//...

end;

当然,您将此过程链接到每个生成的按钮。

如果有人想练习它,您可能想知道如何生成选项卡和组件,在这里:

procedure Form1.addCol(idcol:integer, owner : TComponent); // Form1 is a great owner imo
var
pan : TPanel;    // Will be align client with the new tabsheet
c: TComponent;   //used to create components on the pannel
tab : TTabSheet; 
begin
try
  pan  := TPanel.create(owner);
  pan.name := format('Panel_%d',[idcol]);
  pan.caption := '';

  // dynamically create that button
  c := TButton.create(Owner);
  with c as TButton do
  begin
    Name := format('C%d_btn',[idcol]);
    Parent := pan;
    //Top  := foo;
    //Left := bar;
    caption := 'press me';
    OnClick := Form1.ColBtnClick;      // <<<<<<< link procedure to event
  end;

  //create a Tedit the same way

  c := TEdit.create(Owner);
  with c as TEdit do
    Name := format('C%d_edi',[idcol]);
    Parent := pan;
    // other properties


  //  create the tabsheet and put the panel in
finally
  tab := TTabSheet.Create(Parent);
  tab.caption := 'Column %d';
  tab.PageControl := Pagecontrol1;

  pan.Parent := tab;
  pan.Align := alClient;
end;  
end; 

生成名称来获取组件实际上是拥有干净代码的一种非常好的方法。

滚动父子组件以找到您想要的组件实际上效率低下,并且如果有很多组件(在我的示例中,如果有 3、10 或未知数量的 TEdit 循环子(兄弟)组件将很难看) .

也许这个例子是无用的,但它可能会帮助某人,总有一天。

于 2020-08-07T09:23:22.760 回答