Inside Delphi 2006 (Wordware Delphi Developers Library)
To add a new form to the project, select File ® New ® Form - Delphi for Win32. The IDE automatically displays the second form on the Designer Surface. You can add components to the form and write code for the form, but you can't access the form from other units, at least not yet. To access the second form from the main form, you have to add the form to the uses list of the main form.
To add the second form's unit to the uses list of the main form, do the following:
-
Click on the tab of the main form to select it.
-
Select File ® Use Unit... to display the Use Unit dialog (see Figure 12-15).
Figure 12-15: The Use Unit dialog box displays available units. -
In the Use Unit dialog box, select the unit and click OK to add it to the uses list of the selected form.
When you click OK in the Use Unit dialog box, the IDE adds the selected unit to the uses list of the main form. But that uses list resides in the implementation section of the unit. The IDE does this to avoid circular references between units. The uses list in the interface section of the unit should only contain references to essential units or units that contain code that you have to use in the interface section.
implementation uses Unit2; {$R *.dfm} end.
If you're building a C++Builder VCL Forms application that has multiple forms, use the File ® Include Unit Hdr option to include another form's unit. The IDE will display the Use Unit dialog box, and when you click OK, it will add the #include directive after the #pragma hdrstop directive:
#include <vcl.h> #pragma hdrstop #include "Unit1.h" #include "Unit2.h"
Displaying the Form
The TForm class provides two methods for displaying forms: Show and ShowModal. The Show method simply displays the form. It is used when you want to display and work with several forms at the same time.
The ShowModal method displays a modal form. A form displayed with the ShowModal method allows the user to work with that form only. To access other forms in the application, the user first has to close the modal form.
Here is the code for the Show and ShowModal buttons:
Listing 12-10: Displaying the form
procedure TForm1.ShowButtonClick(Sender: TObject); begin Form2.Show; Caption := 'Form2.Show'; end; procedure TForm1.ShowModalButtonClick(Sender: TObject); begin Form2.ShowModal; Caption := 'Form2.ShowModal'; end;
When the user clicks the Show button to display the form, the form is displayed and the "Form2.Show" message is assigned to the Caption property of the main form. But when the user clicks the ShowModal button to display the form, the ShowModal method displays the form but doesn't return until the user closes the form. Therefore, the "Form2.ShowModal" message is not displayed when the form is displayed but when the form closes.
Accessing components on another form is as simple as accessing components on the same form. The only difference is that when you access a component on another form you have to write a fully qualified name:
FormName.ComponentName.Property
For instance, to copy the text typed in a text box to a text box on another form, we have to do the following:
-
Add a TEdit component to both forms.
-
Write the following code in the OnChange event of the TEdit component on the main form:
procedure TForm1.Edit1Change(Sender: TObject); begin Form2.Edit1.Text := Edit1.Text; end;
Figure 12-17: Accessing a component on another form
The Global Application Object
The global Application object represents the application and is the main reason we can work with forms without having to either create or destroy them. The Application object is an instance of the TApplication class that encapsulates methods, properties, and events related to the entire application.
When you add a form to the project at design time, the IDE creates the form and the code that automatically creates the form at application startup. That code resides in the main project file. To see the code that actually makes the application work, select Project ® View Source. Listing 12-11A shows the main project file of a Delphi VCL Forms application, and Listing 12-11B shows the main project file of a C++Builder VCL Forms application.
There are two differences between Delphi and C++Builder project files, apart from the syntax. The first difference is that execution in C++Builder project files doesn't begin in the main begin-end block but inside the WinMain function. The WinMain function replaces the main() function in Win dows-based applications (VCL, generic, or other).
The second difference is that the C++Builder project file contains some exception handling code that checks for errors. Chapter 13 covers exception handling.
Listing 12-11A: The main project file of a Delphi VCL Forms application
program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}, Unit2 in 'Unit2.pas' {Form2}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end.
Listing 12-11B: The main project file of a C++Builder VCL Forms application
//$$---- EXE CPP ---- //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", Form1); USEFORM("Unit2.cpp", Form2); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->CreateForm(__classid(TForm2), &Form2); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } //---------------------------------------------------------------------------
The application uses the CreateForm method to create the forms automatically at startup. The CreateForm method accepts two parameters: the name of the class that is to be instantiated and the actual form variable. In C++Builder, the reserved word __classid must be used to work with classes directly.
When all forms are created, the application calls the Run method to start the application, and usually this involves displaying the main form. The main form of the application is the first form created with a call to the CreateForm method.
Creating Modal Forms
The ability to automatically instantiate forms at application startup enables us to quickly build application prototypes or, in some cases, to build entire applications without having to worry about creating and destroying the forms.
But in large applications that contain a large number of forms, automatic form creation is actually pretty undesirable. One of the reasons is application speed at startup. In an application that contains a large number of forms, it may take a while to create all the forms and display the main form on the screen. The longer it takes for an application to load, the less the users will like it.
Another, more serious reason why we should avoid automatic form creation in large applications is memory consumption. Since our application is only one of many that are concurrently executed in the OS, you should use system resources judiciously. Therefore, having all forms created at application startup is not the best solution.
If it's possible, you should create a form dynamically only when you need it and remove it from memory as soon as you're done with it. The following code shows how to dynamically create, display, and destroy a form.
Listing 12-12: Dynamically creating an empty form
procedure TForm1.Button1Click(Sender: TObject); var F: TForm; begin F := TForm.Create(Self); F.ShowModal; F.Free; end;
To create a new form, you have to call the form's constructor and define the form's owner. The owner of the form is usually another form or the application itself. If you want to make the application the owner of the new form, pass the Application object to the constructor.
When you display the form using the ShowModal method, further statements aren't executed until the form is closed. When the form is closed, the Free method is called and the form is released from memory.
When you dynamically create a modal form, the owner component isn't absolutely necessary because the form gets immediately released from memory when the user closes it. Thus, you can pass nil to the form's constructor to create the form a bit faster.
Listing 12-13: Creating a form without an owner
procedure TForm1.Button1Click(Sender: TObject); var F: TForm; begin F := TForm.Create(nil); F.ShowModal; F.Free; end;
The previous two examples show how to dynamically create a generic, empty form. But to dynamically create a form that belongs to the project, you first have to remove it from the auto-create list. There are two ways to remove a form from the auto-create list: by editing the main project file or by using the Project Options dialog box.
To manually remove the form from the auto-create list, do the following:
-
Open the main project file (Project ® View Source).
-
Remove the CreateForm call that creates the form you want to dynamically create later.
The Project Options dialog box provides a user-friendly way to manage auto-created and available forms. Forms listed under "Available forms" aren't automatically created at application startup, but they can be dynamically created.
To dynamically create the second form, do the following:
-
Add the second form to the uses list of the main form (File ® Use Unit).
-
Add a button to the main form and write the following code in the OnClick event handler of the button.
Listing 12-14: Dynamically creating a modal form
procedure TForm1.Button1Click(Sender: TObject); begin Form2 := TForm2.Create(Self); Form2.ShowModal; Form2.Free; end;
Creating Modal Forms in C++
Forms, like all other components, have to be created using the new operator. To delete a dynamically created form (component), you don't call Free like you do in Delphi. Instead, you use the delete operator:
void __fastcall TForm1::CreateFormButtonClick(TObject *Sender) { TForm* form = new TForm(this); form->ShowModal(); delete form; }
Creating Modeless Forms
Dynamic creation of a modeless form (a form displayed with the Show method) is more involved than the dynamic creation of a modal form.
To see how to dynamically create a modeless form, first create a new VCL Forms application project and add a second form to it. After you've added the second form, remove it from the auto-create list in the Project Options dialog box and add it to the uses list of the main form.
When creating a modeless form, you mustn't call the Free method after you call Show because modeless forms allow further statements to be executed. Thus, if you call Free immediately after Show, the modeless form will be created, displayed, and automatically released from memory.
Listing 12-15: Creating a modeless form
procedure TForm1.Button1Click(Sender: TObject); begin Form2 := TForm2.Create(Self); Form2.Show; end;
To release a modeless form from memory immediately after the user closes it, you have to do it in the form's OnClose event handler. Select the second form in the IDE and double-click the OnClose event in the Object Inspector to have Delphi generate the empty handler. To free the modeless form from memory, assign the value caFree to the Action parameter of the OnClose event.
Listing 12-16: Releasing a modeless form from memory
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end;
The caFree value is one of the values in the TCloseAction enumeration that enables us to define what should happen when a form is closed. When you assign caFree to the Action parameter, the form is closed and immediately released from memory.
Although the code that creates and destroys the modeless form works great, there is a small problem: Another instance of the TForm2 class is created every time the user clicks the button and the Form2 variable only points to the form that was created last.
If you only want to have a single copy of the modeless form (which is almost always the case), you have to determine whether the form exists before you call the constructor. The best way to see if a form (or any other object) is nil is to use the Assigned function, but you can also perform the test manually by comparing the form (object) variable to nil. Listings 12-17A and 12-17B show how to do this in both Delphi and C++.
Listing 12-17A: Creating the form only if it doesn't exist
procedure TForm1.Button1Click(Sender: TObject); begin { if not created, create the form } if not Assigned(Form2) then Form2 := TForm2.Create(Self); Form2.Show; end;
Listing 12-17B: Creating the form only if it doesn't exist, C++ version
void __fastcall TForm1::Button1Click(TObject *Sender) { if(Form2 == NULL) Form2 = new TForm2(this); Form2->Show(); }
In this case, the constructor is called only when the form doesn't exist, but the Show method is always executed, regardless of the call to the constructor. This way, if the user tries to create the form after it's already been created, the constructor is skipped, but the Show method is called to display and activate the existing form.
We are still not finished. Currently, the code only works once. If you create the form once, then close it and try to create it again, you will get an error because when an object is destroyed, the object variable isn't set to nil. So, select the second form in the IDE and change the OnClose event handler to this:
Listing 12-18A: The proper way to destroy a modeless form
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; Form2 := nil; end;
Listing 12-18B: The proper way to destroy a modeless form, C++ version
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action) { Action = caFree; Form2 = NULL; }