Hi,
how can I create a component at runtime and then work with it (changing properties, etc.)?
Hi,
how can I create a component at runtime and then work with it (changing properties, etc.)?
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.
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:
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;
To simplify the runtime component creation process, you can use GExperts.
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;
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.
Some components override the 'Loaded' method. This method will not be called automatically if you create an instance at runtime. It will be called by Delphi when loading from the form file (DFM) is complete.
If the method contains initialization code, your application might show unexpected behaviour when created at runtime. In this case, check if the component writer has used this method.
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.