Articles::Creating Controls at Run Time
Whilst browsing the newsgroups this question seems to be asked
very often: How do I create controls at runtime ? as
well as Why can I not see the control I just created ? and
How do I assign event handlers to my controls? Here I
intend to answer all of the above questions and provide sample code
to show just how easy it is to do.
Getting Started - What the program will do
What we will do is write a very simple program
which will comprise of a form with 2 buttons (TButton) on it. One
will allow us to add new buttons to the form and the other will
delete the last added button. All new buttons that are added will be
placed next to each other on the form. When ever one of the new
buttons is clicked a dialog box will be displayed showing which
button was pressed.
Designing the form in the IDE
First we need to start a new
applictaion, (I called mine RunTime.dpr with the only other unit
being main.pas). As it is a very simple program that we are writing
all we need to add to the form is 2 buttons, so by selecting the
Button from the Standard page of the component palette add 2 new
buttons to your form. Place the 2 buttons next to each other at the
top of the form calling one AddNewButton and the other
DeleteLastButton. Then by using the Object Inspector create the
function stubs for the OnClick event of the two buttons. We now have
the skeleton of our program but do not run it yet as Delphi will
remove our OnClick program stubs as they are not doing anything.
Writing the actual
code
All we need to do now is fill in the two procedures (Delphi created
for us) for the OnClick events of our two buttons, and write another
procedure that will be called when one of our buttons is
clicked.
The first procedure to write then is the one for
adding the NewButton
procedure TForm1.AddNewButtonClick(Sender:
TObject);
var (* This is a
pointer to the new button that we are going to create
*) NewButton :
TButton;
begin (* This creates
(in memory) the new button with the owner of it being
*) (* self so that the
NewButton will be destroyed automatically when the
*) (* form is destroyed
*)
NewButton :=
TButton.create(self);
(*
By using the with statment on the new button we do not need to
*) (* to keep referencing
its properties with NewButton. all the time
*)
with NewButton
do
begin (*
Set Top so that it appears underneath our two fixed buttons
*)
Top :=
30;
(* Make
the width large enough to hold the
caption
*) Width
:= 60;
(*
This line takes a little more explanation. Every WinControl has
a
*) (*
ControlCount propery which holds the number of controls that
are
*) (* parented
by it. So self.ControlCount will return the number of controls
*) (* on our
form. We know of two of these controls (our fixed
buttons
*) (* so by
takeing 2 off this we have the number of NewButtons that
we
*) (* have
created and multiplying this by the width we have the left
position*) (* of
the NewButton
*)
Left := Width *
(self.ControlCount-2);
(* This is the line that is most often forgotten, the parent
property *) (*
should be set to the WinControl the button (or any other
component)*) (*
is to be displayed on. In our case this is self which will be
the *) (*
main form, if it is not set your button will not be diplayed
*) Parent :=
self;
(*
This assigns the procedure CustomButtonClick (which will be
written*) (*
later) to the OnClick event of the NewButton
*) OnClick :=
CustomButtonClick;
(* We calculate the button number as early, and add this to the
caption *) (* So
that all of our new buttons will have different captions
*)
Caption
:= 'Button '+ inttostr
(self.ControlCount-2);
end; //With
end;
Next we need to complete the procedure for
when the 'Delete Last Button' button is
clicked
procedure
TForm1.DeleteLastButtonClick(Sender:
TObject);
begin (* Make sure
there are some new buttons on the form
*) if
Self.ControlCount>2
then (* Delete the
last added button
*) TButton
(Controls[ControlCount-1]).destroy;
end;
Finally we need to write a new
procedure to deal with the OnClick event for all our new buttons.
First define it in the private section of the unit:
private {
Private declarations
}
procedure CustomButtonClick(Sender:
TObject);
Notice that the procedure has the same
layout as the other button clicks, so if we wish to respond to
another event just see what the syntax is using one of the Buttons
at design time (i.e. double clicking the event in the Object
Inspector). Copy it, changing the procedure name to something
different. And assign the event to that procedure (e.g. OnDragDrop
:= MyNewDragDop). Then write the procudure using the required
syntax.
Here is the procedure for our OnClick
event:
procedure
TForm1.CustomButtonClick(Sender:
TObject);
begin (* Display the
caption of the pressed button on a Message
*) (* Box to indicate which
button was pressed *) (* The
pressed button is got from the sender parameter of the procedure
*) (* which needs casting to
TButton *)
ShowMessage(TButton(Sender).caption + '
Pressed');
end;
Conclusion I hope this has been
a useful starter tutorial for creating controls at run time. When
writing your own application you may find it is better to store
pointers to them in an array or TList so that it is easier to access
them. I used the ControlCount on the form as this was the simplist
way in the circumstances, but if you have other controls on the form
this will probably not be the case. I will be writing a sample
application in the future that allows the user to create buttons at
runtime assigning them to execuatables on their machine which will
be launched when the button is clicked. A custom toolbar and launch
pad (like that seen in Microsoft Office).
|