Delphi - Referencing Components created at Runtime

自作多情 提交于 2019-12-05 19:39:49

I'm not sure what you mean by: "I can't use a string variable to refer to it - e.g. StringVar := Panel.Name."

Try this:

procedure TForm1.FormCreate(Sender: TObject);
var
  p: TPanel;
begin
  p := TPanel.Create(Self); // create a TPanel at run-time
  p.Name := 'MyPanel'; // set a unique name
  p.Parent := Self;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  p: TPanel;
  StringVar: string;
begin
  p := FindComponent('MyPanel') as TPanel;
  if Assigned(p) then // p has reference to MyPanel
  begin
    // use that reference
    p.Caption := 'Foo';
    StringVar := p.Name;
    ShowMessage(StringVar);
  end;
end;

Or did I miss anything?

You're conflating the component name with the variable name. The Delphi IDE strives to keep those two the same, for IDE-created components, but they're not necessarily equal. You don't have variable names because you're creating the components dynamically, and you don't know how many variables you'd need. However, you still have control over the component names: Simply assign the Name property of the component, and then you can use it the same way you use any other component names, by calling FindComponent. Just make sure the names are unique for each panel instance.

Remember also that the way to deal with variables, when you don't know at compile time how many you'll need, is to use arrays or lists. You can use plain old arrays, or you can use a more sophisticated data structure like TComponentList or TDictionary.

Finally, to make it easier to refer to the controls on the panels you're creating, you can dispense with the panels and use frames instead. You can visually design a TFrame in the IDE and give names to the buttons, and at run time, you can instantiate the frame class, and it will automatically create all the buttons for you, just like when you instantiate a form or data module. You only need to give a name to the new frame object, but that object will already have named fields referring to the buttons.

There should be no need to create a secondary list of items you created to display on a tForm instance.

AFAIK, whenever you create a new panel that uses a Form or an other Container in place of self

  pnl := Tpanel.Create(Self);

you don't need to take care of destroying then new pnl, because it is handled by the containing "self" component.

This means that there should be any construct that will hold the child components of the parent component.

I expect that you will find, either a ComponentCount, or Components list or a FindComponent method in the parent object. Assuming that the "Self" referenced object is a Tform.

 for i := 0 to tForm(self).ComponentCount -1 do 
   if tForm(self).Components[i] is tPanel then 
      tPanel(tForm(self).Components[i]).Caption := intToStr(i) ;

will change all Captions of tPanels in your application. In order to distinguish between Panels created by code and designer you can use the TAG property of each created tPanel and use it in the above example.

You can add the components you need references to a TList|Container... and then, use your list|container to access them

var
  slPanels: TStringList;
...


 With TypeQuery do begin // Create Panels
   First;
   While (not eof) do begin        // create the actual panel
      panelno := FieldByName('Product_type_id').AsInteger;
      pnl := Tpanel.Create(Self);
      pnl.name := FieldByName('PanelName').AsString;
      slPanels.AddObject(FieldByName('PanelName').AsString, pnl);

when you need it:

TPanel(slPanels.Objects[slPanels.IndexOf(FieldByName('PanelName').AsString)]) ...

I dislike the code above (there are better container... but this should do the job :o))

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!