Creating components at runtime - Delphi

前端 未结 9 1874
孤街浪徒
孤街浪徒 2020-12-02 15:04

How can I create a component at runtime and then work with it (changing properties, etc.)?

相关标签:
9条回答
  • 2020-12-02 15:08

    If you nest win controls in Group Boxes/Page Controls/Etc..., I think it is beneficial to have the parent group box also be the owner. I've noticed a sharp decrease in window close times when doing this, as opposed to having the owner always be the main form.

    0 讨论(0)
  • 2020-12-02 15:17

    Very ease. Call Create. Example:

    procedure test
    var
      b : TButton;
    begin
      b:=TButton.Create(nil);
      b.visible:=false;
    end;
    

    This creates a component (TButton is a component) at runtime and sets the property visible.


    For the constructor: pass nil if you want to manage the memory yourself. Pass a pointer another component if you want to have it destroyed when the other component is destroyed.

    0 讨论(0)
  • 2020-12-02 15:19

    To simplify the runtime component creation process, you can use GExperts.

    1. Create a component (or more components) visually and set its properties.
    2. Select one or more components and execute GExperts, Components to Code.
    3. Paste the generated code into your application.
    4. Remove component(s) from the visual form designer.

    Example (TButton-creation code generated in this way):

    var
      btnTest: TButton;
    
    btnTest := TButton.Create(Self);
    with btnTest do
    begin
      Name := 'btnTest';
      Parent := Self;
      Left := 272;
      Top := 120;
      Width := 161;
      Height := 41;
      Caption := 'Component creation test';
      Default := True;
      ParentFont := False;
      TabOrder := 0;
    end;
    
    0 讨论(0)
  • 2020-12-02 15:21

    But if I don't surely know how many components I want to create, e.g. if it depends on user's decision. So how can I declare components dynamically?

    The answer has been suggested - the easiest way is a List of Objects(components). TObjectList is the simplest to use (in unit contnrs). Lists are great!

      In Form1 Public
      MyList: TObjectList;
      procedure AnyButtonClick(Sender: TObject); 
    

    // You can get more sophisticated and declare //TNotifyevents and assign them but lets keep it simple :) . . .

    procedure Tform1.AnyButtonClick(Sender: TObject);
    begin
      If Sender is TButton then
      begin
        Case Tbutton(Sender).Tag of 
        .
        .
        .
    // Or You can use the index in the list or some other property 
    // you have to decide what to do      
    // Or similar :)
      end;
    end;
    
    procedure TForm1.BtnAddComponent(Sender: TObJect)
    var
      AButton: TButton;
    begin
      AButton := TButton.Create(self);
      Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
      AButton.OnClick := AnyButtonClick;
    // Set Height and width and caption ect.
      .
      .
      . 
      AButton.Tag := MyList.Add(AButton);
    end;
    

    An Object list can contain any object visual or not but that gives you an added overhead of sorting out which items are which - better to have related lists if you want multiple dynamic controls on similar panels for instance.

    Note: like other commenters I may have over-simplified for brevity but I hope you ge the idea. You need a mechanism to manage the objects once they are created and lists are excellent for this stuff.

    0 讨论(0)
  • 2020-12-02 15:21

    This is example how to emulate button tag on Evernote

    unit Unit7;
    
    interface
    
    uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, CHButton, Vcl.ExtCtrls, RzPanel, CHPanel, RzCommon,RzBmpBtn, Vcl.StdCtrls;
    
    type
      // This is panel Button
      TButtonClose = class (TRzPanel)
       CloseButton : TRzBmpButton;
       procedure CloseButtonClick(Sender: TObject);
       procedure CloseButtonMouseEnter(Sender: TObject);
       procedure MouseDown(Sender: TObject; Button: TMouseButton;
                 Shift: TShiftState; X, Y: Integer);
       procedure MouseUp(Sender: TObject; Button: TMouseButton;
                 Shift: TShiftState; X, Y: Integer);
    public
       constructor Create(AOwner: TComponent); override;
       destructor Destroy; override;
    end;
    
    TForm7 = class(TForm)
       CHButton1: TCHButton;
       RzPanel1: TRzPanel;
       RzBmpButton1: TRzBmpButton;
       procedure CHButton1Click(Sender: TObject);
       procedure RzBmpButton1Click(Sender: TObject);
       procedure RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer);
       procedure RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer);
       procedure RzPanel1MouseEnter(Sender: TObject);
       procedure RzBmpButton1MouseEnter(Sender: TObject);
       procedure FormMouseEnter(Sender: TObject);
       procedure FormCreate(Sender: TObject);
    private
      { Private declarations }
    public
      { Public declarations }
    end;
    
    var
      Form7: TForm7;
      MyCloseButton : TButtonClose;
    
    implementation
    
    {$R *.dfm}
    
    // constructor for on the fly component created
    constructor TButtonClose.Create(AOwner: TComponent);
    begin
       inherited Create(AOwner);
    
       // Set Events for the component
       Self.OnMouseEnter := Self.CloseButtonMouseEnter;
       Self.OnMouseDown := Self.MouseDown;
       Self.OnMouseUp := Self.MouseUp;
       Self.Height := 25;
    
       // Close button on top panel Button
       // Inherited from Raize Bitmap Button
       CloseButton := TRzBmpButton.Create(self);
       // Set On Click Event for Close Button
       CloseButton.OnClick := Self.CloseButtonClick;
       // Place Close Button on Panel Button
       CloseButton.Parent := self;
       CloseButton.Left := 10;
       CloseButton.Top := 5;
       CloseButton.Visible := False;
       // Setting the image for the button
       CloseButton.Bitmaps.Up.LoadFromFile(ExtractFilePath(Application.ExeName)+'\close.bmp');
    end;
    
    procedure TButtonClose.CloseButtonClick(Sender: TObject);
    begin
       // Free the parent (Panel Button)
       TControl(Sender).Parent.Free;
    end;
    
    procedure TButtonClose.CloseButtonMouseEnter(Sender: TObject);
    begin
       // Show the Close button
       CloseButton.Visible := True;
    end;
    
    procedure TButtonClose.MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
       // Emulate Button down state, since it is panel
       TRzPanel(Sender).BorderOuter := fsLowered;
    end;
    
    procedure TButtonClose.MouseUp(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
    begin
       // Emulate Button up state, since it is panel
       TRzPanel(Sender).BorderOuter := fsRaised;
    end;
    
    destructor TButtonClose.Destroy;
    begin
       inherited Destroy;
    end;
    
    procedure TForm7.FormCreate(Sender: TObject);
    begin
       // Create Panel Button on the fly
       MyCloseButton := TButtonClose.Create(self);
       MyCloseButton.Caption := 'My Button';
       MyCloseButton.Left := 10;
       MyCloseButton.Top := 10;
       // Don't forget to place component on the form
       MyCloseButton.Parent := self;
    end;
    
    procedure TForm7.FormMouseEnter(Sender: TObject);
    begin
       if Assigned(RzBmpButton1) then
          RzBmpButton1.Visible := False;
    
       // Hide when mouse leave the button
       // Check first if myCloseButton Assigned or not before set visible property
       if Assigned(MyCloseButton.CloseButton) then
          MyCloseButton.CloseButton.Visible := False;
    end;
    
    procedure TForm7.RzBmpButton1Click(Sender: TObject);
    begin
       TControl(Sender).Parent.Free;
    end;
    
    procedure TForm7.RzBmpButton1MouseEnter(Sender: TObject);
    begin
       RzBmpButton1.Visible := True;
    end;
    
    procedure TForm7.RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      TRzPanel(Sender).BorderOuter := fsLowered;
    end;
    
    procedure TForm7.RzPanel1MouseEnter(Sender: TObject);
    begin
       RzBmpButton1.Visible := True;
    end;
    
    procedure TForm7.RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
       TRzPanel(Sender).BorderOuter := fsRaised;
    end;
    
    procedure TForm7.CHButton1Click(Sender: TObject);
    begin
       FreeAndNil(Sender);
    end;
    
    end.
    
    0 讨论(0)
  • 2020-12-02 15:23

    I would just like to add that when dynamically adding controls... it as a good idea to add them to an object list (TObjectList) as suggested in <1> by @Despatcher.

    procedure Tform1.AnyButtonClick(Sender: TObject);
    begin
      If Sender is TButton then
      begin
        Case Tbutton(Sender).Tag of 
        .
        .
        .
    // Or You can use the index in the list or some other property 
    // you have to decide what to do      
    // Or similar :)
      end;
    end;
    
    procedure TForm1.BtnAddComponent(Sender: TObJect)
    var
      AButton: TButton;
    begin
      AButton := TButton.Create(self);
      Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
      AButton.OnClick := AnyButtonClick;
    // Set Height and width and caption ect.
      .
      .
      . 
      AButton.Tag := MyList.Add(AButton);
    end;
    

    You need to add the Unit 'Contnrs' to your Uses list. I.e System.Contnrs.pas the base Containers Unit And you can have many object lists. I suggest using a TObjectList for each type of control that you use e.g.

    Interface
     Uses Contnrs;
    Type
     TMyForm = class(TForm)
    private
       { Private declarations }
    public
       { Public declarations }
    end;
     Var
      MyForm: TMyForm;
      checkBoxCntrlsList: TObjectList; //a list for the checkBoxes I will createin a TPanel
      comboboxCntrlsList: TObjectList; //a list of comboBoxes that I will create in some Form Container
    

    this allows you to easily manipulate/manage each control as you will know what type of control it is e.g.

    Var comboBox: TComboBox;
    I: Integer;
    
    begin
     For I = 0 to comboboxCntrlsList.Count -1 do // or however you like to identify the control you are accessing such as using the tag property as @Despatcher said
       Begin
        comboBox := comboboxCntrlsList.Items[I] as TComboBox;
        ...... your code here
       End;
    end;
    

    This allows you to then use the methods and properties of that control Don't forget to create the TObjectLists, perhaps in the form create event...

    checkBoxCntrlsList := TObjectList.Create;
    comboboxCntrlsList := TObjectList.Create;
    
    0 讨论(0)
提交回复
热议问题