Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I refer to a control whose name is determined at runtime?

Tags:

delphi

As a kind of self-study exercise, I've made a form which contains six panels in a 2x3 rectangle and I want them to switch between visible and invisible one after another. I'm trying to do so by using a for loop of some kind. I could of course write something like:

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

But this takes quite a lot of typing and is pretty inefficient when I decide I want it to wait for 100ms between each step. For example, I'd then have to edit all the six steps to wait. This is doable for six steps, but maybe another time I want to do it a hundred times! So I'm thinking there must also be a way to use a for loop for this, where a variable varies from 1 to 6 and is used in the object identifier. So it would something like this:

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

Now, this obviously doesn't work, but I hope somebody here can tell me if this is in fact possible and if yes, how. Maybe I can use a string as the identifier? My explanation is probably pretty bad because I don't know all the technical terms but I hope the code explains something.

like image 751
Jim Daniël Teunis Avatar asked Nov 30 '12 16:11

Jim Daniël Teunis


3 Answers

You can loop through the panel's Owner's Components array.

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;

You can also use the FindComponent method, if you want a very specific type of component by name. For instance, if you have the 6 panels, and their names are Panel1, Panel2, and so forth:

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;
like image 160
Ken White Avatar answered Nov 12 '22 08:11

Ken White


This is a situation where you want to create the controls dynamically at runtime rather than at designtime. Trying to grapple with 6 different variables is just going to be a world of pain. And when you need the grid to be 3x4 rather than 2x3, you'll regret that decision even more.

So, start with a completely blank form. And add, in the code, a two dimensional array of panels:

private
  FPanels: array of array of TPanel;

Then, in the form's constructor, or an OnCreate event handler, you can initialise the array by calling a function like this:

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;

And now you can refer to your panels using cartesian coordinates rather than a flat one dimensional array. Of course, you can easily enough declare a flat one dimensional array as well if you want.

The key idea is that when you are creating large numbers of control in a structured layout, you are best abandoning the designer and using code (loops and arrays).

like image 6
David Heffernan Avatar answered Nov 12 '22 10:11

David Heffernan


Use FindComponent method of 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;
like image 5
Igor Avatar answered Nov 12 '22 08:11

Igor