Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating components at runtime - Delphi

Tags:

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

like image 853
Lukáš Neoproud Avatar asked Jun 17 '09 06:06

Lukáš Neoproud


3 Answers

It depends if it is a visual or non-visual component. The principle is the same, but there are some additional considerations for each kind of component.

For non-visual components

var
  C: TMyComponent;
begin
  C := TMyComponent.Create(nil);
  try
    C.MyProperty := MyValue;
    //...
  finally
    C.Free;
  end;
end;

For visual components:

In essence visual components are created in the the same way as non-visual components. But you have to set some additional properties to make them visible.

var
  C: TMyVisualComponent;
begin
  C := TMyVisualComponent.Create(Self);
  C.Left := 100;
  C.Top := 100;
  C.Width := 400;
  C.Height := 300;
  C.Visible := True;
  C.Parent := Self; //Any container: form, panel, ...

  C.MyProperty := MyValue,
  //...
end;

A few explanations to the code above:

  • By setting the owner of the component (the parameter of the constructor) the component gets destroyed when the owning form gets destroyed.
  • Setting the Parent property makes the component visible. If you forget it your component will not be displayed. (It's easy to miss that one :) )

If you want many components you can do the same as above but in a loop:

var
  B: TButton;
  i: Integer;
begin
  for i := 0 to 9 do
  begin
    B := TButton.Create(Self);
    B.Caption := Format('Button %d', [i]);
    B.Parent := Self;
    B.Height := 23;
    B.Width := 100;
    B.Left := 10;
    B.Top := 10 + i * 25;
  end;
end;

This will add 10 buttons at the left border of the form. If you want to modify the buttons later, you can store them in a list. (TComponentList ist best suited, but also take a look at the proposals from the comments to this answer)

How to assign event handlers:

You have to create an event handler method and assign it to the event property.

procedure TForm1.MyButtonClick(Sender: TObject);
var
  Button: TButton;
begin
  Button := Sender as TButton; 
  ShowMessage(Button.Caption + ' clicked');
end;

B := TButton.Create;
//...
B.OnClick := MyButtonClick;
like image 150
10 revs Avatar answered Sep 23 '22 10:09

10 revs


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;
like image 20
gabr Avatar answered Sep 24 '22 10:09

gabr


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;
like image 31
Darren Avatar answered Sep 22 '22 10:09

Darren