Delphi - Referencing Components created at Runtime

ぐ巨炮叔叔 提交于 2019-12-07 11:43:09

问题


I'm using Delphi 5 and I'm creating a number of panels at runtime, then creating buttons on the panels, obviously again at runtime. I need to do it this way because I may need to dynamically create more panel/button combinations in the future.

I can do all of this, but I'm at a loss as to how to reference the panels I've created, because I can't find a way to access the panels' component name. Hunting around the Internet I've found that I can use FindComponent to find the panel components by name, but I still don't know how to use that name, because I can't use a string variable to refer to it - e.g. StringVar := Panel.Name. I get a type mismatch, TComponentName versus String.

I created the buttons for each panel as I created the panels. Simplified, it looks like this:

   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;
      pnl.color := clInactiveCaption;
      pnl.parent := MainForm;
      pnl.width := 365;
      pnl.Height := 551;
      pnl.left := 434
      pnl.top := 122;
      pnl.caption := '';
      With ButtonQuery do begin
         Close;
         Parameters.parambyname('PanelID').Value := PanelNo;
         Open;
         First;
         While (not eof) and (FieldByName('Product_type_id').AsInteger = PanelNo) do begin    //put the buttons on it.
            btnName := FieldByName('ButtonName').AsString;
            BtnText := FieldByName('ButtonText').AsString;
            BtnGroup := FieldByName('Product_Group_ID').AsString;
            GrpColour := FieldByName('ButtonColour').AsString;
            btn := TColorButton.Create(Self);
            btn.Parent := pnl;
            btn.Name := BtnName;
            Btn.backcolor := HexToTColor(GrpColour);
            btn.Font.Name := 'Arial Narrow';
            btn.Font.Style := [fsBold];
            btn.Font.Size := 10;
            . . .
        end;
        . . .
     end; 
  end;

I've read on several forums (including this one) that there is no way to reference the panels by name directly. I've tried using a component array, but I strike the same problem - I need to refer to the component by its assigned component name.

Okay, I'm not a gun programmer - I've used Delphi for years to create simple programs, but this one is a lot more complex. I've never worked with runtime component creation before.

Can I use FindComponent to make the panels visible or invisible? If so, given what I have shown you above, can you give me the approach I should take in baby-steps?

Thanks in advance ...


回答1:


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?




回答2:


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.




回答3:


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.




回答4:


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))



来源:https://stackoverflow.com/questions/13726129/delphi-referencing-components-created-at-runtime

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