如何引用名称在运行时确定的控件?

时间:2012-11-30 16:33:27

标签: delphi

作为一种自学习练习,我制作了一个包含6个面板的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;

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

5 个答案:

答案 0 :(得分:16)

您可以遍历面板的所有者的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;

答案 1 :(得分: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;

现在你可以使用笛卡尔坐标而不是平面一维数组来引用你的面板。当然,如果你愿意的话,你也可以很容易地声明一个平面的一维数组。

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

答案 2 :(得分:4)

使用FindComponent的{​​{1}}方法:

TComponent

答案 3 :(得分:0)

正如其他人回答的那样,FindComponent是必经之路。

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

这将同样有效:

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

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

答案 4 :(得分:0)

实际上是我的答案

如果使用命名约定将组件命名为

 "Mycomponent" + inttostr(global_int)

您可以使用它很容易地找到它:

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

您还可以使用(sender as TComponent).name来了解哪些其他组件与他相关,从而使生成的组件彼此交互。


示例

以下是您可以执行此操作的一个示例:

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

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

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

实际上,您可以通过单击按钮直接引用编辑,在运行时通过调用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循环子级(兄弟)组件),它将变成地狱会很丑。

也许这个例子没有用,但是有一天它可能对某人有所帮助。