Managing Run-Time Component Arrays
Accessing a group of run-time created components using an array
A component (or control) array, as the name implies, is nothing more than an array of components.
In the first part of this article (Acessing a group of design-time created components using an array), you have learned how to access a group of components by storing a reference to each control into an element of an array.
An Array of Components Created at Run-Time
While in most situations you will build the user interface of your application at design-time using the IDE, there are scenarios where you need to programmatically create components at run time.Consider an entry form for a database application. You could decide to write a function that takes the name of a database table, and creates a DBEdit (data-aware TEdit) for each field (column) in the table.
You could store all DBEdit's into an array and use it to, for example, run a common validation code against all the DBEdit in a loop.
Run-Time Buttons Array Example
Let's start by creating a
simple example, but powerful enough to show how to create buttons at
run-time, assign the OnClick event handler and store all the buttons
into an array.
Download buttons array example code.
- A form hosts an edit control and a button. When you click the button an array of buttons will be created (at run-time). The number of buttons to be created is retrieved from the edit control.
- Since we'll be dealing with buttons in an array, we need to declare our array type variable. We will use a dynamic array here, as we do not know how many buttons will be created.
All the buttons will share the same event handler ("ButtonClicked").
We'll need a function to free the memory when we exit the program ("FreeButtons").
Place the following declarations in the private section of the form declaration
TMainForm = class(TForm)
...
private
buttonsArray : array of TButton;
//free the memory
procedure FreeButtons(buttons : array of TButton) ;
//shared "onclick" handler
procedure ButtonClicked(sender : TObject) ; -
Here's the code to fill the buttons array:
var
Each element of the "buttonsArray" is a TButton. To dynamically create the button we use its constructor by passing "nil" for the owner parameter.
ctrlCount : integer;
cnt : integer;
begin
//first clear any previously created buttons
FreeButtons(buttonsArray) ;
ctrlCount := StrToInt(edCtrlCount.Text) ;
//set the "new" length
SetLength(buttonsArray, ctrlCount) ;
//create "new" buttons
for cnt := 0 to -1 + ctrlCount do
begin
buttonsArray[cnt] := TButton.Create(nil) ;
//assign the OnClick event handler
buttonsArray[cnt].OnClick := ButtonClicked;
//set the caption
buttonsArray[cnt].Caption := Format('Button %d',[cnt]) ;
//position randomly
buttonsArray[cnt].Left := Random(pnlRunTime.Width - buttonsArray[cnt].Width) ;
buttonsArray[cnt].Top := Random(pnlRunTime.Height - buttonsArray[cnt].Height) ;
//if no parent is set, button will not be visible!
buttonsArray[cnt].Parent := pnlRunTime; //TPanel
end;
end;
The OnClick event handler is assigned, Caption is set and the button is placed on a parent. -
Just for a test, the OnClick event handler display the Caption of the clicked button:
procedure TMainForm.ButtonClicked(sender: TObject) ;
begin
if Sender is TButton then
begin
ShowMessage(Format('%s clicked!',[TButton(Sender).Caption])) ;
end;
end; - Since we used nil for each button's Owner, the "FreeButtons" procedures ensures that the memory is freed:
procedure TMainForm.FreeButtons(buttons : array of TButton) ;
var
cnt : integer;
begin
for cnt := Low(buttons) to High(buttons) do
begin
buttons[cnt].Free;
buttons[cnt] := nil;
end;
end;