Difference between revisions of "Form Tutorial"

From Lazarus wiki
m (Two Forms That Can Call Each Other)
m (Fixed syntax highlighting)
 
(15 intermediate revisions by 7 users not shown)
Line 1: Line 1:
 
{{Form Tutorial}}
 
{{Form Tutorial}}
<br>
+
 
 
A short introduction on using Forms in Lazarus.
 
A short introduction on using Forms in Lazarus.
  
 
== What is a Form? ==
 
== What is a Form? ==
The form (class [[TForm]]) represents a window or dialog box that is the user interface of an application. It is the container on which all other components (e.g. buttons, labels, edit fields, images, etc.) are inserted .
+
 
 +
The form (class [[TForm]]) represents a window or dialog box that is the user interface of an [[Application|application]]. It is the container on which all other [[LCL Components|components]] (e.g. buttons, labels, edit fields, images, etc.) are inserted .
  
 
== The First GUI Application ==
 
== The First GUI Application ==
  
Upon successfully installing Lazarus and starting a new Application Project, an empty form is created. Otherwise you can create a new [[Overview_of_Free_Pascal_and_Lazarus#GUI_apps|GUI application]] under Main menu -> New Project -> New Application.
+
Upon successfully installing Lazarus and starting a new Application Project, an empty form is created. Otherwise you can create a new [[Overview_of_Free_Pascal_and_Lazarus#GUI_apps|GUI application]] under Main menu -> Project -> New Project -> Application OK.
  
 
Now you have created a new, fully functional project with a form:
 
Now you have created a new, fully functional project with a form:
Line 14: Line 15:
 
[[Image:Form1_Designmodus.png]]
 
[[Image:Form1_Designmodus.png]]
  
 
+
To run the newly created project, you can simply press the {{keypress|F9}} key or click with the mouse on the main menu icon [[Image:Start.png]] (or Main menu: Run -> Run). The project will be compiled and executed.
To run the newly created project, you can simply press the [F9] key or click with the mouse on the main menu icon [[Image:Start.png]] (or Main menu: Start -> Run). The project will be compiled and executed.
 
  
 
Nothing really exciting happens. The form (Form1) changes slightly in appearance and the points from the raster grid (help for positioning of individual components) disappear (as long as the grid points are visible, you also know that it is still in design mode):<br>
 
Nothing really exciting happens. The form (Form1) changes slightly in appearance and the points from the raster grid (help for positioning of individual components) disappear (as long as the grid points are visible, you also know that it is still in design mode):<br>
 
[[Image:Form1_Designmodus.png]]  ->  [[Image:Form1_Runmodus.png]]  
 
[[Image:Form1_Designmodus.png]]  ->  [[Image:Form1_Runmodus.png]]  
 
  
 
Next, you can place a [[TButton]] onto the form. This will be used later to allow the opening of a second form:
 
Next, you can place a [[TButton]] onto the form. This will be used later to allow the opening of a second form:
  
Select the TButton from the standard [[LCL Components|component]] palette
+
Select the TButton from the [[Standard tab]] on the [[Component Palette]].
  
 
[[Image:TButtonStandardpalette.png]]
 
[[Image:TButtonStandardpalette.png]]
Line 30: Line 29:
  
 
In order for this button to do something, you have to announce that you intend to do something when you click it. This can be regarded as a simple event. For this you need an [[Event_order | event handler]] that is called after the click.
 
In order for this button to do something, you have to announce that you intend to do something when you click it. This can be regarded as a simple event. For this you need an [[Event_order | event handler]] that is called after the click.
The event handler for a mouse click can be quite easily reached by double-clicking on Button1 (or in the Object Inspector, select Button1 -> Events tab -> OnClick, then click on the [...] button). This creates a procedure TForm1.Button1Click in the code editor, which is always called (at run time, not in design time) when Button1 is clicked:
+
The event handler for a mouse click can be quite easily reached by double-clicking on Button1 (or in the Object Inspector, select Button1 -> Events tab -> OnClick, then click on the [...] button). This creates a procedure TForm1.Button1Click in the code editor, which is always called (at [[runtime]], not in design time) when Button1 is clicked:
 
 
  
 
[[Image:NeuesProjekt2.png]]
 
[[Image:NeuesProjekt2.png]]
  
 +
To make the application do something after clicking on Button1, add some code between the [[Begin]] and [[End]] of the procedure TForm1.Button1Click like this:
  
To make the application do something after clicking on Button1, add some code between the begin and end of the procedure TForm1.Button1Click like this:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 45: Line 43:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
Now run the form by pressing [F9], and notice that when the button is clicked the form caption and the button caption both change.
 
  
 +
Now run the form by pressing {{keypress|F9}}, and notice that when the button is clicked the form caption and the button caption both change.
  
 
You can now experiment a little bit and learn how to use the standard components. To get started, I would recommend you to try samples of the following components:
 
You can now experiment a little bit and learn how to use the standard components. To get started, I would recommend you to try samples of the following components:
* [[TButton]]  
+
 
* [[TLabel]]
+
{|
* [[TEdit]]
+
|-
* [[TMemo]]
+
| [[TButton]] || [[image:tbutton.png]]
* [[TToggleBox]]
+
|-
* [[TRadioButton]]
+
| [[TLabel]]  || [[image:tlabel.png]]
* [[TListBox]]
+
|-
* [[TComboBox]]
+
| [[TEdit]]   || [[image:tedit.png]]
* [[TScrollBar]]
+
|-
 +
| [[TMemo]]   || [[image:tmemo.png]]
 +
|-
 +
| [[TToggleBox]] || [[image:ttogglebox.png]]
 +
|-
 +
| [[TCheckBox]] || [[image:tcheckbox.png]]
 +
|-
 +
| [[TRadioButton]] || [[image:tradiobutton.png]]
 +
|-
 +
| [[TListBox]] || [[image:tlistbox.png]]
 +
|-
 +
| [[TComboBox]] || [[image:tcombobox.png]]
 +
|-
 +
| [[TScrollBar]] || [[image:tscrollbar.png]]
 +
|}
  
 
There is additional helpful information in the [[Lazarus Tutorial]].
 
There is additional helpful information in the [[Lazarus Tutorial]].
Line 66: Line 78:
  
 
There is a main form with a button, which when clicked opens the new form. The new form also receives a button which when clicked closes the new form and returns you to the main form.
 
There is a main form with a button, which when clicked opens the new form. The new form also receives a button which when clicked closes the new form and returns you to the main form.
 
  
 
If you have worked through the first tutorial [[Form Tutorial#The first GUI app | The first GUI app]], you will need to delete the code between the begin and end of procedure TForm1.Button1Click. Otherwise you will need to create a new project (application), drop a button on the form, and create the OnClick event handler for this Button1.<br>
 
If you have worked through the first tutorial [[Form Tutorial#The first GUI app | The first GUI app]], you will need to delete the code between the begin and end of procedure TForm1.Button1Click. Otherwise you will need to create a new project (application), drop a button on the form, and create the OnClick event handler for this Button1.<br>
Line 73: Line 84:
 
[[Image:Objektinspektor.png]]
 
[[Image:Objektinspektor.png]]
  
 +
In the Main menu click File -> New Form, to add a second form (Form2) to the project. Place a button (Button1) on Form2 and create the OnClick event handler for it. Change the caption of this button to "Close".
  
In the Main menu click File -> New Form, to add a second form (Form2) to the project. Place a button (Button1) on Form2 and create the OnClick event handler for it. Change the caption of this button to "Close".
+
Now the project has two forms, each of which can be selected and displayed with Main menu Project -> Forms (or key combination {{keypress|Shift}} + {{keypress|F12}}). Alternatively, you can select the tab for a Unit in the Source Editor, then use {{keypress|F12}} to switch between the code editor and form designer for the form of the associated [[Unit]].
<br>
+
 
<br>
+
In the [[source code editor]], go to the Form1 associated [[Unit]] (Unit1) and add "Unit2" to the [[Uses]] clause:
Now the project has two forms, each of which can be selected and displayed with Main menu Project -> Forms (or key combination [Shift] + [F12]). Alternatively, you can select the tab for a Unit in the Source Editor, then use [F12] to switch between the code editor and form designer for the form of the associated [[Unit]].
 
  
In the code editor, go to the Form1 associated [[Unit]] (Unit1) and add "Unit2" to the uses clause:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
uses
 
uses
   Classes, SysUtils, ... , Unit2;  // It is important that the individual units are separated by a comma
+
   Classes, SysUtils, ... , Unit2;  // It is important that the individual units are separated by a comma(,)
                                   // and the uses clause ends with a semicolon.
+
                                   // and the uses clause ends with a semicolon(;).
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
Now you can call Unit2 (and thus Form2) from Unit1.
 
Now you can call Unit2 (and thus Form2) from Unit1.
  
 +
Next, edit the OnClick event of the button belonging to Form1:
  
Next, edit the OnClick event of the button belonging to Form1:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
unit Unit1;
 
unit Unit1;
 
...
 
...
Line 97: Line 108:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit2;
 
unit Unit2;
 
...
 
...
Line 105: Line 116:
 
end;  
 
end;  
 
</syntaxhighlight>
 
</syntaxhighlight>
Now you can start the project ([[Image:Start.png]] or [F9]) and open Form2 by clicking the button on Form1.
+
 
 +
Now you can start the project ([[Image:Start.png]] or {{keypress|F9}}) and open Form2 by clicking the button on Form1.
  
 
===Difference between Show and ShowModal===
 
===Difference between Show and ShowModal===
Line 115: Line 127:
 
* Select ''Button2'' and change the properties ''Name'' to ''btnShowModal'' and ''Caption'' to ''ShowModal''.
 
* Select ''Button2'' and change the properties ''Name'' to ''btnShowModal'' and ''Caption'' to ''ShowModal''.
 
* Create (or modify) the ''OnClick'' event handler of the two buttons as follows:
 
* Create (or modify) the ''OnClick'' event handler of the two buttons as follows:
<source>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.btnShowClick(Sender: TObject);
 
procedure TForm1.btnShowClick(Sender: TObject);
 
begin
 
begin
Line 127: Line 140:
 
   ShowMessage('Form2 is opened and closed again!');
 
   ShowMessage('Form2 is opened and closed again!');
 
end;
 
end;
</source>
+
</syntaxhighlight>
* Start and watch the difference!<br>
+
 
<br>
+
* Start and watch the difference!
'''Show:'''<br>
+
 
This procedure makes a form visible and continues to execute the code of the calling control (''Form1''). In our example, the message (ShowMessage) appears almost simultaneously with ''Form2''. You can also continue to use the calling form ''Form1''. For example, it is possible to move it, or click the ''Show'' button again (but clicking ''ShowModal'' will cause an error).<br>
+
;Show:
 +
This procedure makes a form visible and continues to execute the code of the calling control (''Form1''). In our example, the message ([[Dialog_Examples#ShowMessage|ShowMessage]]) appears almost simultaneously with ''Form2''. You can also continue to use the calling form ''Form1''. For example, it is possible to move it, or click the ''Show'' button again (but clicking ''ShowModal'' will cause an error).
 +
 
 
Instead of using '''<code><myForm>.Show;</code>''', it is also possible to use '''<code><myForm>.Visible:=True;</code>'''. The method ''Show'' does the following:
 
Instead of using '''<code><myForm>.Show;</code>''', it is also possible to use '''<code><myForm>.Visible:=True;</code>'''. The method ''Show'' does the following:
<source>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TCustomForm.Show;
 
procedure TCustomForm.Show;
 
begin
 
begin
Line 139: Line 155:
 
   BringToFront;
 
   BringToFront;
 
end;
 
end;
</source>
+
</syntaxhighlight>
The only difference between '''<code><myForm>.Show;</code>''', and '''<code><myForm>.Visible:=True;</code>''' is that the form displayed using ''Show'' will be on top of all other forms currently displayed.<br>
+
 
<br>
+
The only difference between '''<code><myForm>.Show;</code>''', and '''<code><myForm>.Visible:=True;</code>''' is that the form displayed using ''Show'' will be on top of all other forms currently displayed.
'''ShowModal:'''<br>
+
 
''ShowModal'' halts the processing of code in the calling control (''Form1'') until the newly opened form (''Form2'') has been closed. In the example, the ''ShowMessage'' line immediately following ''ShowModal'' is executed only after ''Form2'' is closed. The calling form is frozen: You can neither move ''Form1'' nor click any of its buttons.<br>
+
;ShowModal:
ShowModal is a function that returns an integer result, see [[doc:lcl/forms/tmodalresult.html|TModalResult]]. The returned value allows branching based on how the form was closed.<br>
+
 
 +
''ShowModal'' halts the processing of code in the calling control (''Form1'') until the newly opened form (''Form2'') has been closed. In the example, the ''ShowMessage'' line immediately following ''ShowModal'' is executed only after ''Form2'' is closed. The calling form is frozen: You can neither move ''Form1'' nor click any of its buttons.
 +
 
 +
ShowModal is a function that returns an integer result, see [[doc:lcl/forms/tmodalresult.html|TModalResult]]. The returned value allows branching based on how the form was closed.
  
 
== Two Forms That Can Call Each Other ==
 
== Two Forms That Can Call Each Other ==
Line 150: Line 169:
 
It is generally good program design to avoid calling the main form from a second form. It is better to return to the main form by closing the second form (as in the previous example) and letting focus revert to the main form. It is, however, possible to call the main from from a sub-form, as this example will show.
 
It is generally good program design to avoid calling the main form from a second form. It is better to return to the main form by closing the second form (as in the previous example) and letting focus revert to the main form. It is, however, possible to call the main from from a sub-form, as this example will show.
  
<br>
 
 
In order for Form1 to call Form2, Unit1 must have Unit2 in its Uses clause. Conversely, for Form2 to call Form1, Unit2 must have Unit1 in its Uses clause. This leads to a potential Circular Unit Reference compiler error, with each Unit referring to the other. This example also shows how to avoid the Circular Unit Reference error.
 
In order for Form1 to call Form2, Unit1 must have Unit2 in its Uses clause. Conversely, for Form2 to call Form1, Unit2 must have Unit1 in its Uses clause. This leads to a potential Circular Unit Reference compiler error, with each Unit referring to the other. This example also shows how to avoid the Circular Unit Reference error.
  
<br>
 
 
To set up this example, either modify the forms from the previous example ([[Form_Tutorial#The_use_of_a_second_form|The Use of a Second Form]]), or create a new project with two forms, each having a single button. Set the caption of Form1.Button1 to "Open Form2", and set the caption of Form2.Button1 to "Open Form1".
 
To set up this example, either modify the forms from the previous example ([[Form_Tutorial#The_use_of_a_second_form|The Use of a Second Form]]), or create a new project with two forms, each having a single button. Set the caption of Form1.Button1 to "Open Form2", and set the caption of Form2.Button1 to "Open Form1".
  
 
If you are modifying the previous example, remove "Unit2" from the Uses clause in Unit1.
 
If you are modifying the previous example, remove "Unit2" from the Uses clause in Unit1.
  
<br>
+
Add a Uses clause to the [[Implementation]] section of each [[Unit]]. Add Unit2 to the Unit1 clause, and add Unit1 to the Unit2 clause, as below:
Add a Uses clause to the Implementation section of each [[Unit]]. Add Unit2 to the Unit1 clause, and add Unit1 to the Unit2 clause, as below:
+
 
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
 
...
 
...
Line 169: Line 186:
 
...
 
...
 
</syntaxhighlight>
 
</syntaxhighlight>
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit2;
 
unit Unit2;
 
...
 
...
Line 180: Line 197:
  
 
Now change the code located behind the OnClick event of the button event handler:
 
Now change the code located behind the OnClick event of the button event handler:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
 
...
 
...
Line 188: Line 206:
 
end;  
 
end;  
 
</syntaxhighlight>
 
</syntaxhighlight>
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
unit Unit2;
 
unit Unit2;
 
...
 
...
Line 199: Line 218:
 
== Passing Variables to Other Forms ==
 
== Passing Variables to Other Forms ==
  
In accordance with Example [[Form Tutorial#The use of a second form | The use of a second form]] could be in the interface part of the [[Unit]] "Unit2" a [[Global_variables | global variable]] declare. This would give you access in Unit1 also in Unit2 to one and the same [[Variable]] (by inject Unit2 in uses clause of Unit1 all declared in the interface part of the Unit2 variables are available also in Unit1). This procedure should be limited to a minimum, as you quickly can not remember for larger projects, which the individual variables have a meaning and where to access it anywhere (error rate increases).
+
In accordance with the above example [[Form Tutorial#The use of a second form | The Use of a Second Form]], it is possible to declare a [[Global_variables | global variable]] in the interface part of Unit2. This would allow access in both Unit1 and Unit2 to one and the same [[Variable | variable]] (This is because by putting Unit2 in the Uses clause of Unit1, all variables declared in the interface part of Unit2 are also available in Unit1). This practice should be limited to a minimum, as it quickly becomes difficult to remember which individual variables are in scope, potentially increasing errors. It is better to use [[Local_variables|local variables]], define a [[Object_Oriented_Programming_with_Free_Pascal_and_Lazarus#Properties| Property]] in a [[Class]] or, alternatively, as a variable in a class.
 
 
It is better to use local variables, define it as [[Object_Oriented_Programming_with_Free_Pascal_and_Lazarus#Properties| Property]] in a [[Class]] or, alternatively, as a variable in a class.
 
  
<br>
 
<br>
 
 
In the next project, a second form is opened by clicking on the button. In this case, in the unit of the main form is counted how often the second shape has been shown. In this second form you can ask to see by clicking on the button, how often it has been opened:
 
In the next project, a second form is opened by clicking on the button. In this case, in the unit of the main form is counted how often the second shape has been shown. In this second form you can ask to see by clicking on the button, how often it has been opened:
 
 
* a new project with two forms, each a button on it and their OnClick events to evaluate.
 
* a new project with two forms, each a button on it and their OnClick events to evaluate.
 
 
* now, in the public part of the [[Class]] TForm2 (Unit2) a variable (in this case a [[Const | constant]], which we defined as variable abuse) of type integer with the name Count create:
 
* now, in the public part of the [[Class]] TForm2 (Unit2) a variable (in this case a [[Const | constant]], which we defined as variable abuse) of type integer with the name Count create:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit2;
 
unit Unit2;
 
...
 
...
Line 224: Line 237:
 
   end;
 
   end;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
* now even customize the Button event handler accordingly:
 
* now even customize the Button event handler accordingly:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
 
...
 
...
Line 234: Line 249:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
unit Unit2;
 
unit Unit2;
 
...
 
...
Line 242: Line 258:
 
end;  
 
end;  
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
In this way you can in Unit1 access all public variables/properties/functions/procedures (general methods) of Unit2.
 
In this way you can in Unit1 access all public variables/properties/functions/procedures (general methods) of Unit2.
  
Line 248: Line 265:
 
=== Use Another Form as the Main Form ===
 
=== Use Another Form as the Main Form ===
  
If you determine after a while that you want to have displayed rather a different form or new form for the view at startup, you can under the main menu Project -> Project Settings -> Change the order of the forms:
+
If you determine after a while that you want to have displayed rather a different form or new form for the view at startup, you can under the main menu Project -> Project Options -> Forms:
  
 
[[Image:Projekteinstellungen_Formulare.png]]
 
[[Image:Projekteinstellungen_Formulare.png]]
  
 
Alternatively, you can display, in Mainmenu -> Project -> Show .lpr file, that Project.lpr (Pascal code of the main program) and create the first form to be displayed as the first:
 
Alternatively, you can display, in Mainmenu -> Project -> Show .lpr file, that Project.lpr (Pascal code of the main program) and create the first form to be displayed as the first:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
program Project1;
 
program Project1;
 
...
 
...
Line 262: Line 280:
 
=== Save properties of the shape at end of program ===
 
=== Save properties of the shape at end of program ===
  
In some applications, it is nice if the modified by user settings related to the position and size of the mold are saved at program end and be returned as when the program starts. But Lazarus is innately a fairly easy way using [[TXMLPropStorage]] or [[TINIPropStorage]] available.
+
* [[Remember form position and size#Using_TForm.SessionProperties and TXMLPropStorage]]
 
 
A simple example will illustrate these functions:
 
 
 
* create new application (main menu Project -> New Project -> Application)
 
* pass an TXMLPropStorage component on the form (to find them is on the Misc tab in the component palette)
 
 
 
[[Image:KomponentenpaletteTXMLPropStorage.png]]
 
 
 
* in the Object Inspector: XMLPropStorage1 -> FileName eg "session.xml" set
 
* in the Object Inspector: Form1 -> Edit sessionProperties (the [...] button simply click)
 
* now select each of the Form1 properties Left, Top, Width, Height and add it
 
* confirm with OK and restart project, change and move the shape in size during the next start it again this setting
 
 
 
Further information under [[TXMLPropStorage]]
 
  
 
== Generate the form dynamically ==
 
== Generate the form dynamically ==
Line 282: Line 286:
 
=== Create a Lazarus designed form dynamically ===  
 
=== Create a Lazarus designed form dynamically ===  
  
You does not have all of the forms, that may be called during the term of an application, to be created at startup. Some developers generally keep none of it and delete the code automatically created when you insert a new form in a project from the Projekt.lpr out immediately. As soon as one example, if you want to write a library that contains a couple of GUIs, you can not get around the dynamic creation of forms.
+
You do not have to create all the forms that may be called during the term of an application at startup. Some developers generally keep none of it and delete the code automatically created when you insert a new form in a project from the Projekt.lpr out immediately. If you want to write a library that contains a couple of GUIs, you can not get around the dynamic creation of forms.
  
 
An example:
 
An example:
 +
 
* new application with two forms, Form1 a button (add "Unit2" in the uses clause of unit1)
 
* new application with two forms, Form1 a button (add "Unit2" in the uses clause of unit1)
 
* open the Projekt.lpr (Project -> lpr file.)
 
* open the Projekt.lpr (Project -> lpr file.)
 
* delete the line "Application.CreateForm(TForm2, Form2);"
 
* delete the line "Application.CreateForm(TForm2, Form2);"
 
* in the OnClick event of Button1, Form1 add following code:
 
* in the OnClick event of Button1, Form1 add following code:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 297: Line 303:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
* now simply start
 
* now simply start
  
 
=== Creating a new form dynamically ===
 
=== Creating a new form dynamically ===
  
The following example will demonstrate how new forms by hand the Form Designer, can be generated without.
+
The following example will demonstrate how new forms can be generated in code, without using  the Form Designer,.
 +
 
 +
In this example clicking a button should open another form that itself contains a button. Clicking this second button makes a message appear warning that the form will close; then the form is closed.
  
With a button click, another form should open that contains a button which, when clicking a warning appears that the form will close and then the form is closed.
+
* Create a new application with a form and a button
 +
* In the OnClick event of Button1, in Form1, add the following code:
  
* New application with a form and a button
+
<syntaxhighlight lang=pascal>
* In the OnClick event of Button1, Form1 add following code:
 
<syntaxhighlight>
 
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
var
 
var
Line 320: Line 328:
 
   MyButton.Caption:='Close my form';
 
   MyButton.Caption:='Close my form';
 
   MyButton.SetBounds(10, 10, 200, 30);
 
   MyButton.SetBounds(10, 10, 200, 30);
   MyButton.Parent:=MyForm;            
+
   MyButton.Parent:=MyForm;
  
 
   MyButton.OnClick:=@MyButtonClick;     
 
   MyButton.OnClick:=@MyButtonClick;     
Line 330: Line 338:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
{{Note| If a component has been created with an owner (TButton.Create(Owner: TComponent)), it belongs to the owner,<br>
+
{{Note| If a component has been created with an owner (TButton.Create(Owner: TComponent)), it belongs to the owner, who is then responsible to free the component. Thus MyButton is freed automatically by freeing MyForm.}}
who is then responsible to free the component. Thus MyButton is freed automatically by freeing MyForm.}}
 
  
* now even the event handler of MyButtonClick must be created
+
* now the event handler for MyButton.OnClick must be created. To this end, write in the private section of class TForm1 the following procedure declaration:
* to this end, write in the class private section of TForm1 following procedure and press [Ctrl]+[Shift]+[C] (code completion):
 
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
  TForm1 = class(TForm)
+
TForm1 = class(TForm)
 
...
 
...
  private
+
private
    { private declarations }
+
  { private declarations }
    procedure MyButtonClick(Sender: TObject);
+
  procedure MyButtonClick(Sender: TObject);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* and insert the following code there
+
* press {{keypress|Ctrl]}}+{{keypress|Shift}}+{{keypress|C}} (code completion) and insert the following code as the handler implementation:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.MyButtonClick(Sender: TObject);
 
procedure TForm1.MyButtonClick(Sender: TObject);
 
begin
 
begin
   Showmessage('Attention, my dynamically created form is closed!');
+
   Showmessage('Attention, closing my dynamically created form!');
 
   if Sender is TButton then
 
   if Sender is TButton then
 
     TForm(TButton(Sender).Parent).Close;
 
     TForm(TButton(Sender).Parent).Close;
Line 357: Line 363:
 
* now the project can be compiled and executed
 
* now the project can be compiled and executed
  
A few tips for [[LCL Tips|manual generation of controls]]<br>
+
== See also ==
 
+
* [[LCL Tips|Tips for manual generation of controls]]
 
 
== Further information ==
 
 
 
 
* [[LCL_Tips#Creating_a_GUI_by_code|Example to create a complete GUI application by code]]
 
* [[LCL_Tips#Creating_a_GUI_by_code|Example to create a complete GUI application by code]]
 
* [[Testing, if form exists]]
 
* [[Testing, if form exists]]
Line 368: Line 371:
 
* [[user should not be able to close form]]
 
* [[user should not be able to close form]]
 
* [[LCL_Tips#Creating_a_non-rectangular_window_or_control|Creating a non-rectangular window or control]]
 
* [[LCL_Tips#Creating_a_non-rectangular_window_or_control|Creating a non-rectangular window or control]]
<br>
+
* [[Remember form position and size]]
<br>
+
* [[Form in DLL]]
[[Category:Tutorials]]{{AutoCategory}}
 
--[[User:Michl|Michl]] 15:22, 8 May 2014 (CEST)
 
 
 
[[Category:Code]]
 
[[Category:LCL]]
 
[[Category:Forms]]
 

Latest revision as of 01:42, 16 February 2020

Deutsch (de) English (en) suomi (fi) 中文(中国大陆)‎ (zh_CN)

A short introduction on using Forms in Lazarus.

What is a Form?

The form (class TForm) represents a window or dialog box that is the user interface of an application. It is the container on which all other components (e.g. buttons, labels, edit fields, images, etc.) are inserted .

The First GUI Application

Upon successfully installing Lazarus and starting a new Application Project, an empty form is created. Otherwise you can create a new GUI application under Main menu -> Project -> New Project -> Application OK.

Now you have created a new, fully functional project with a form:

Form1 Designmodus.png

To run the newly created project, you can simply press the F9 key or click with the mouse on the main menu icon Start.png (or Main menu: Run -> Run). The project will be compiled and executed.

Nothing really exciting happens. The form (Form1) changes slightly in appearance and the points from the raster grid (help for positioning of individual components) disappear (as long as the grid points are visible, you also know that it is still in design mode):
Form1 Designmodus.png -> Form1 Runmodus.png

Next, you can place a TButton onto the form. This will be used later to allow the opening of a second form:

Select the TButton from the Standard tab on the Component Palette.

TButtonStandardpalette.png

and click on the form: There is now a button placed on Form1 with the name and caption "Button1".

In order for this button to do something, you have to announce that you intend to do something when you click it. This can be regarded as a simple event. For this you need an event handler that is called after the click. The event handler for a mouse click can be quite easily reached by double-clicking on Button1 (or in the Object Inspector, select Button1 -> Events tab -> OnClick, then click on the [...] button). This creates a procedure TForm1.Button1Click in the code editor, which is always called (at runtime, not in design time) when Button1 is clicked:

NeuesProjekt2.png

To make the application do something after clicking on Button1, add some code between the Begin and End of the procedure TForm1.Button1Click like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption:='My first Form';  //Caption of Form1 is changed (text in formhead)
  //or
  Button1.Caption:='Hello';  //Caption of button is changed (shown text of button)
end;

Now run the form by pressing F9, and notice that when the button is clicked the form caption and the button caption both change.

You can now experiment a little bit and learn how to use the standard components. To get started, I would recommend you to try samples of the following components:

TButton tbutton.png
TLabel tlabel.png
TEdit tedit.png
TMemo tmemo.png
TToggleBox ttogglebox.png
TCheckBox tcheckbox.png
TRadioButton tradiobutton.png
TListBox tlistbox.png
TComboBox tcombobox.png
TScrollBar tscrollbar.png

There is additional helpful information in the Lazarus Tutorial.

The Use of a Second Form

The tutorial shows how to use multiple forms in a project. In this example, only two forms, Form1 (main form) and Form2, are created, but the process is identical for additional forms.

There is a main form with a button, which when clicked opens the new form. The new form also receives a button which when clicked closes the new form and returns you to the main form.

If you have worked through the first tutorial The first GUI app, you will need to delete the code between the begin and end of procedure TForm1.Button1Click. Otherwise you will need to create a new project (application), drop a button on the form, and create the OnClick event handler for this Button1.
Click the new button to enter a caption (visible text on the button). To do this, select the button (just click it once) and in the Object Inspector click Properties -> Caption, and enter the text "Open Form2".

Objektinspektor.png

In the Main menu click File -> New Form, to add a second form (Form2) to the project. Place a button (Button1) on Form2 and create the OnClick event handler for it. Change the caption of this button to "Close".

Now the project has two forms, each of which can be selected and displayed with Main menu Project -> Forms (or key combination Shift + F12). Alternatively, you can select the tab for a Unit in the Source Editor, then use F12 to switch between the code editor and form designer for the form of the associated Unit.

In the source code editor, go to the Form1 associated Unit (Unit1) and add "Unit2" to the Uses clause:

uses
  Classes, SysUtils, ... , Unit2;  // It is important that the individual units are separated by a comma(,)
                                   // and the uses clause ends with a semicolon(;).

Now you can call Unit2 (and thus Form2) from Unit1.

Next, edit the OnClick event of the button belonging to Form1:

unit Unit1;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.ShowModal;  //Displays Form2, the focus is placed on Form2.
end;
unit Unit2;
...
procedure TForm2.Button1Click(Sender: TObject);
begin
  Close;  //Close Form2.
end;

Now you can start the project (Start.png or F9) and open Form2 by clicking the button on Form1.

Difference between Show and ShowModal

Both methods Show and ShowModal make a form visible. To see the difference between Show and ShowModal:

  • Extend the previous example The use of a second form by adding a second TButton to Form1.
  • Select Button1 and change the properties Name to btnShow and Caption to Show.
  • Select Button2 and change the properties Name to btnShowModal and Caption to ShowModal.
  • Create (or modify) the OnClick event handler of the two buttons as follows:
procedure TForm1.btnShowClick(Sender: TObject);
begin
  Form2.Show;
  ShowMessage('Form2 is opened!');
end;

procedure TForm1.btnShowModalClick(Sender: TObject);
begin
  Form2.ShowModal;
  ShowMessage('Form2 is opened and closed again!');
end;
  • Start and watch the difference!
Show

This procedure makes a form visible and continues to execute the code of the calling control (Form1). In our example, the message (ShowMessage) appears almost simultaneously with Form2. You can also continue to use the calling form Form1. For example, it is possible to move it, or click the Show button again (but clicking ShowModal will cause an error).

Instead of using <myForm>.Show;, it is also possible to use <myForm>.Visible:=True;. The method Show does the following:

procedure TCustomForm.Show;
begin
  Visible := True;
  BringToFront;
end;

The only difference between <myForm>.Show;, and <myForm>.Visible:=True; is that the form displayed using Show will be on top of all other forms currently displayed.

ShowModal

ShowModal halts the processing of code in the calling control (Form1) until the newly opened form (Form2) has been closed. In the example, the ShowMessage line immediately following ShowModal is executed only after Form2 is closed. The calling form is frozen: You can neither move Form1 nor click any of its buttons.

ShowModal is a function that returns an integer result, see TModalResult. The returned value allows branching based on how the form was closed.

Two Forms That Can Call Each Other

It is generally good program design to avoid calling the main form from a second form. It is better to return to the main form by closing the second form (as in the previous example) and letting focus revert to the main form. It is, however, possible to call the main from from a sub-form, as this example will show.

In order for Form1 to call Form2, Unit1 must have Unit2 in its Uses clause. Conversely, for Form2 to call Form1, Unit2 must have Unit1 in its Uses clause. This leads to a potential Circular Unit Reference compiler error, with each Unit referring to the other. This example also shows how to avoid the Circular Unit Reference error.

To set up this example, either modify the forms from the previous example (The Use of a Second Form), or create a new project with two forms, each having a single button. Set the caption of Form1.Button1 to "Open Form2", and set the caption of Form2.Button1 to "Open Form1".

If you are modifying the previous example, remove "Unit2" from the Uses clause in Unit1.

Add a Uses clause to the Implementation section of each Unit. Add Unit2 to the Unit1 clause, and add Unit1 to the Unit2 clause, as below:

unit Unit1;
...
implementation

uses
  Unit2;  
...
unit Unit2;
...
implementation

uses
  Unit1;  
...

Now change the code located behind the OnClick event of the button event handler:

unit Unit1;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.Show;  
end;
unit Unit2;
...
procedure TForm2.Button1Click(Sender: TObject);
begin
  Form1.Show; 
end;

Passing Variables to Other Forms

In accordance with the above example The Use of a Second Form, it is possible to declare a global variable in the interface part of Unit2. This would allow access in both Unit1 and Unit2 to one and the same variable (This is because by putting Unit2 in the Uses clause of Unit1, all variables declared in the interface part of Unit2 are also available in Unit1). This practice should be limited to a minimum, as it quickly becomes difficult to remember which individual variables are in scope, potentially increasing errors. It is better to use local variables, define a Property in a Class or, alternatively, as a variable in a class.

In the next project, a second form is opened by clicking on the button. In this case, in the unit of the main form is counted how often the second shape has been shown. In this second form you can ask to see by clicking on the button, how often it has been opened:

  • a new project with two forms, each a button on it and their OnClick events to evaluate.
  • now, in the public part of the Class TForm2 (Unit2) a variable (in this case a constant, which we defined as variable abuse) of type integer with the name Count create:
unit Unit2;
...
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    const Count: Integer = 0;   //here
  end;
  • now even customize the Button event handler accordingly:
unit Unit1;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  inc(Form2.Count);  //Increase the counter by 1 on each call to Form2
  Form2.ShowModal;   
end;
unit Unit2;
...
procedure TForm2.Button1Click(Sender: TObject);
begin
  ShowMessage('Form2 would be displayed '+IntToStr(Count)+' times');
end;

In this way you can in Unit1 access all public variables/properties/functions/procedures (general methods) of Unit2.

Other Form-Related Subjects

Use Another Form as the Main Form

If you determine after a while that you want to have displayed rather a different form or new form for the view at startup, you can under the main menu Project -> Project Options -> Forms:

Projekteinstellungen Formulare.png

Alternatively, you can display, in Mainmenu -> Project -> Show .lpr file, that Project.lpr (Pascal code of the main program) and create the first form to be displayed as the first:

program Project1;
...
  Application.CreateForm(TForm2, Form2);  //Form2 is created in first and shown when the application starts
  Application.CreateForm(TForm1, Form1);

Save properties of the shape at end of program

Generate the form dynamically

Create a Lazarus designed form dynamically

You do not have to create all the forms that may be called during the term of an application at startup. Some developers generally keep none of it and delete the code automatically created when you insert a new form in a project from the Projekt.lpr out immediately. If you want to write a library that contains a couple of GUIs, you can not get around the dynamic creation of forms.

An example:

  • new application with two forms, Form1 a button (add "Unit2" in the uses clause of unit1)
  • open the Projekt.lpr (Project -> lpr file.)
  • delete the line "Application.CreateForm(TForm2, Form2);"
  • in the OnClick event of Button1, Form1 add following code:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2:=TForm2.Create(Nil);  //Form2 is created
  Form2.ShowModal;            //Form2 is displayed
  FreeAndNil(Form2);          //Free Form2 
end;
  • now simply start

Creating a new form dynamically

The following example will demonstrate how new forms can be generated in code, without using the Form Designer,.

In this example clicking a button should open another form that itself contains a button. Clicking this second button makes a message appear warning that the form will close; then the form is closed.

  • Create a new application with a form and a button
  • In the OnClick event of Button1, in Form1, add the following code:
procedure TForm1.Button1Click(Sender: TObject);
var
  MyForm: TForm;
  MyButton: TButton;
begin
  MyForm:=TForm.Create(nil);             
  MyForm.SetBounds(100, 100, 220, 150);  
  MyForm.Caption:='My dynamic created form';

  MyButton:=TButton.create(MyForm);  
  MyButton.Caption:='Close my form';
  MyButton.SetBounds(10, 10, 200, 30);
  MyButton.Parent:=MyForm;

  MyButton.OnClick:=@MyButtonClick;     

  MyForm.ShowModal;                     

  FreeAndNil(MyForm);     
end;
Note-icon.png

Note: If a component has been created with an owner (TButton.Create(Owner: TComponent)), it belongs to the owner, who is then responsible to free the component. Thus MyButton is freed automatically by freeing MyForm.

  • now the event handler for MyButton.OnClick must be created. To this end, write in the private section of class TForm1 the following procedure declaration:
TForm1 = class(TForm)
...
private
  { private declarations }
  procedure MyButtonClick(Sender: TObject);
  • press Ctrl]+ Shift+C (code completion) and insert the following code as the handler implementation:
procedure TForm1.MyButtonClick(Sender: TObject);
begin
  Showmessage('Attention, closing my dynamically created form!');
  if Sender is TButton then
    TForm(TButton(Sender).Parent).Close;
end;
  • now the project can be compiled and executed

See also