Difference between revisions of "Form Tutorial"

From Lazarus wiki
Jump to navigationJump to search
(added "Difference between Show and ShowModal")
m (Started cleaning up English grammar a bit. Only got to "Difference between Show and ShowModal" this evening. Will work on this more later.)
Line 1: Line 1:
 
{{Form Tutorial}}
 
{{Form Tutorial}}
 
<br>
 
<br>
A small introduction about how to use Forms in Lazarus.
+
A short introduction on using Forms in Lazarus.
  
 
== What is a form ==
 
== What is a form ==
Line 9: Line 9:
 
== The first GUI application ==
 
== The first GUI application ==
  
Even after you have successfully installed Lazarus, after the start of Lazarus a new project with an empty form is created. Otherwise you can create a new [[Overview_of_Free_Pascal_and_Lazarus#GUI_apps|GUI application]] under Mainmenu -> 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 Mainmenu -> New Project -> New Application.
  
 
Now you have already created a new, fully functional project with a form:
 
Now you have already created a new, fully functional project with a form:
Line 16: Line 16:
  
  
To start 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.
+
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.
  
It happens nothing really exciting, the form (Form1) changed but slightly their appearance and the points from the raster grid (help for positioning of individual components) lose (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) changed slightly changes 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]]  
  
Line 28: Line 28:
 
[[Image:TButtonStandardpalette.png]]
 
[[Image:TButtonStandardpalette.png]]
  
and click on the form: There is a button placed on Form1 with the name and caption "Button1".
+
and click on the form: There is now a button placed on Form1 with the name and caption "Button1".
  
In order for this button also gets a sense that you have to announce that he intended to do something when you click it. Click this can be regarded as a simplified event. For this you need [[Event_order | event handler]] that are called after the jump.
+
In order for this button to do something, you have to announce that you intended 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 in which you double-click on the pasted Button1 (or in the Object Inspector -> Button1 -> Tab Events -> onclick on the button [...] clicks). There a procedure TForm1.Button1Click is now created in the code editor, which is always called (at runtime, not in desingn time) when Button1 is clicked:
+
The event handler for a mouse click can be quite easily reached by double-clicking on the pasted Button1 (or in the Object Inspector -> Button1 -> Tab Events -> onclick on the button [...] clicks). Now a procedure TForm1.Button1Click is created in the code editor, which is always called (at runtime, not in design time) when Button1 is clicked:
  
  
Line 37: Line 37:
  
  
Thus the application after clicking on Button1 gets to do something, you simply add between the begin and end of the procedure TForm1.Button1Click some code like:
+
Thus the application after clicking on Button1 gets to do something, you simply add some code between the begin and end of the procedure TForm1.Button1Click like this:
 
<syntaxhighlight>
 
<syntaxhighlight>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
Line 46: Line 46:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
Now just start (by pressing [F9]), and be glad ...
+
Now just run (by pressing [F9]), and be happy...
  
 
== The use of a second form ==
 
== The use of a second form ==
  
The tutorial shows how one can use multiple forms in a project. In the example, only two forms Form1 (main form) and Form2 can be created for other Forms can be identical procedures.
+
The tutorial shows how one can 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, whose click through to the new form opens. The new form also receives a button whose click through to the new form again closes and you return 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 tutorial [[Form Tutorial#The first GUI app | The first GUI app]], you would need to delete the code between the begin and end of procedure TForm1.Button1Click. Otherwise you would need a new project (application) to create, drop a button on the form and create the OnClick event handler of 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>
 
Click this button to enter yet another caption (visible text on the button). For this purpose, select the button (just click once) and the Object Inspector Properties -> Caption to enter text "open Form2".
 
Click this button to enter yet another caption (visible text on the button). For this purpose, select the button (just click once) and the Object Inspector Properties -> Caption to enter text "open Form2".
  
Line 61: Line 61:
  
  
In the main menu item File -> new form, you can now add a second form (Form2) to the project. In this, you have to place also a button (Button1) and create the OnClick event handler of it. The caption of this button you change analogous to the above "Close"
+
In the main menu item File -> new form, you can now add a second form (Form2) to the project. In this, you also have to place a button (Button1) and create the OnClick event handler for it. The caption of this button you change analogous to the above "Close"
 
<br>
 
<br>
 
<br>
 
<br>
Now the project has two forms, each can be selected and displayed with main menu -> Forms (or key combination [Shift] + [F12]). In the alternative, you can also in the source editor tab for the particular form of the associated [[Unit]], and show with [F12] the appropriate form (with [F12] to switch between the code editor and form designer).
+
Now the project has two forms, each can be selected and displayed with main menu -> Forms (or key combination [Shift] + [F12]). Alternatively, you can show with [F12] the appropriate form (with [F12] to switch between the code editor and form designer) in the source editor tab for the particular form of the associated [[Unit]].
  
 
In the code editor, you can now go on to the Form1 associated [[Unit]] (Unit1) and add the uses clause "Unit2":
 
In the code editor, you can now go on to the Form1 associated [[Unit]] (Unit1) and add the uses clause "Unit2":
 
<syntaxhighlight>
 
<syntaxhighlight>
 
uses
 
uses
   Classes, SysUtils, ... , Unit2;  //it is important that the individual units are equipped with a comma separated
+
   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 from Unit1, Unit2 and thus Form2.
+
Now you can call Unit2 (and thus Form2) from Unit1.
  
  
Next, edit nor the OnClick event of the button belonging to the form:
+
Next, edit the OnClick event of the button belonging to the form:
 
<syntaxhighlight>
 
<syntaxhighlight>
 
unit Unit1;
 
unit Unit1;

Revision as of 04:25, 12 June 2014

Deutsch (de) English (en) suomi (fi) 日本語 (ja) 中文(中国大陆)‎ (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 forms the user interface of an application. It is the container on which all other components (eg 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 Mainmenu -> New Project -> New Application.

Now you have already 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: Start -> Run). The project will be compiled and executed.

Nothing really exciting happens. The form (Form1) changed slightly changes 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 later allow the opening of a second form:

Select the TButton from the standard 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 intended 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 the pasted Button1 (or in the Object Inspector -> Button1 -> Tab Events -> onclick on the button [...] clicks). Now a procedure TForm1.Button1Click is created in the code editor, which is always called (at runtime, not in design time) when Button1 is clicked:


NeuesProjekt2.png


Thus the application after clicking on Button1 gets to do something, you simply 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 just run (by pressing [F9]), and be happy...

The use of a second form

The tutorial shows how one can 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 this button to enter yet another caption (visible text on the button). For this purpose, select the button (just click once) and the Object Inspector Properties -> Caption to enter text "open Form2".

Objektinspektor.png


In the main menu item File -> new form, you can now add a second form (Form2) to the project. In this, you also have to place a button (Button1) and create the OnClick event handler for it. The caption of this button you change analogous to the above "Close"

Now the project has two forms, each can be selected and displayed with main menu -> Forms (or key combination [Shift] + [F12]). Alternatively, you can show with [F12] the appropriate form (with [F12] to switch between the code editor and form designer) in the source editor tab for the particular form of the associated Unit.

In the code editor, you can now go on to the Form1 associated Unit (Unit1) and add the uses clause "Unit2":

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 the form:

unit Unit1;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.ShowModal;  //displays the 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 and open Form1 using Form2 button click.

Difference between Show and ShowModal

Both methods Show and ShowModal make a form visible. A small example, you can learn what is the difference of this:

  • Extend the previous example The use of a second form und add to the Form1 still a second TButton.
  • 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 works the code of the calling control (Form1) further down. In our example, the message (ShowMessage) appears almost simultaneously to the Form2. You can also continue to use the calling form Form1, for example, move it, or again click the button Show (but not ShowModal).
You could instead <myForm>.Show; also <myForm>.Visible:=True; write. The method Show does nothing else:

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

The only difference would be that if the form would be covered by a others, this would remain in front.

ShowModal:
Through this method, the code processing of the calling control (Form1) is interrupted and further processed, when the newly opened form has been closed again. In our example, the communication (ShowMessage) is issued only if the form Form2 was closed again. The calling form Form1 is how being frozen. You can neither move it nor click buttons.
ShowModal is a function and returns an integer result, see TModalResult. So, lets you depending, as the form has been closed, perform various other code.

Two Forms that can call each other

Generally it is cheap (good program design), if not their main form is called of a form. It is better this again return according to previous example, from a main form from, always call and subsequent Forms by the closing of the Main form. Thus, one escapes the problem of a circular Unit - reference. That it theoretically still goes, I will show here.

Either you can modify the forms from the example The use of a second form or create a new project with two forms and each with a button. Caption by Form1.Button1 should "open Form2" loud, which of Form2.Button1 would have to be changed "open Form1" after.

This time you can not simply into the existing uses-clause of Unit2 (as in the previous example - in the interface part of the Unit) insert "Unit1", otherwise a circular unit reference would arise. "Unit2" thus would also have to be removed from the interface uses clause of Unit1.

Instead, you add a uses clause in the implementation of parts of the Unit1:

unit Unit1;
...
implementation

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

uses
  Unit1;  
...

Now you can still change the 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 Example The use of a second form could be in the interface part of the Unit "Unit2" a 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).

It is better to use local variables, define it as 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 Settings -> Change the order of the 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

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.

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)

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

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.

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 by hand the Form Designer, can be generated without.

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.

  • New application with a form and a button
  • In the OnClick event of Button1, Form1 add 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;

Light bulb  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 even the event handler of MyButtonClick must be created
  • to this end, write in the class private section of TForm1 following procedure and press [Ctrl]+[Shift]+[C] (code completion):
  TForm1 = class(TForm)
...
  private
    { private declarations }
    procedure MyButtonClick(Sender: TObject);
  • and insert the following code there
procedure TForm1.MyButtonClick(Sender: TObject);
begin
  Showmessage('Attention, my dynamically created form is closed!');
  if Sender is TButton then
    TForm(TButton(Sender).Parent).Close;
end;
  • now the project can be compiled and executed

A few tips for manual generation of controls


Further information




--Michl 15:22, 8 May 2014 (CEST)