Tutorial - 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?
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 application, (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);
  (* Pointer to the new button that we are going to create *)
  NewButton : TButton;
  This creates (in memory) the new button with the owner of it 
  being the form (self) so that the NewButton will be 
  destroyed automatically when the form is destroyed

  NewButton := TButton.create(self);

  By using the with statement on the new button we do not need to
  to keep referencing its properties with NewButton. all the time

  with NewButton do
    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 property 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 taking 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 displayed
    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

Next we need to complete the procedure for when the 'Delete Last Button' button is clicked
procedure TForm1.DeleteLastButtonClick(Sender: TObject);
  (* 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;
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 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 procedure using the required syntax.

Here is the procedure for our OnClick event:
procedure TForm1.CustomButtonClick(Sender: TObject);
  (* 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');


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 simplest 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 executables on their machine which will be launched when the button is clicked. A custom toolbar and launch pad (like that seen in Microsoft Office).

You can also save the positions of these dynamically created controls by using the information from this other tutorial


Web www.Delphi-Central.com
Delphi Central - Delphi Programming Tutorials, Hints and Tips