Inside Delphi 2006 (Wordware Delphi Developers Library)
Forms are the most important container controls in VCL Forms applications because they represent main or secondary application windows and contain all other controls like buttons, images, and menus. Delphi's TForm class is an extremely powerful class that encapsulates an enormous number of properties, methods, and events.
Form Fundamentals
This part of the chapter deals with properties and events of the TForm class that enhance the quality of the user interface, either visually or practically.
Control Focus
The ActiveControl property can be used to specify which control should have the focus. When a control has focus, it can receive keyboard input. Only one control at a time can have the focus.
When you add controls to the Designer Surface, the focus may be given to a control that doesn't need it at application startup. For instance, the form shown in Figure 12-1 is designed to acquire the user's name, but instead of the more important text box, the Add button has the focus. In previous versions of the Windows operating system, or in XP and later versions that use the Classic visual style, the control that has the focus usually displays a dotted rectangle on its surface. Operating systems that use visual styles to display controls use the visual style's custom image to identify a focused control.
To transfer the initial focus to the text box, select the Edit1 component from the drop-down list of the form's ActiveControl property.
You can also change the focus of a control in code by using the SetFocus method. To change the initial focus of a control using the SetFocus method, place the SetFocus call in the OnShow event of the form.
Listing 12-1: Assigning focus to a control
procedure TForm1.FormShow(Sender: TObject); begin Edit1.SetFocus; end;
Alpha Blending
The AlphaBlend property specifies whether the form is translucent and is used in conjunction with the AlphaBlendValue property. The value of the AlphaBlendValue property is only relevant if the AlphaBlend property is set to True.
The AlphaBlendValue property accepts values in the 0 to 255 range. When the AlphaBlendValue property is set to 255, the form is opaque; when the AlphaBlendValue property is set to 0, the form is completely transparent.
Alpha-blended forms require more memory and more CPU power than standard forms. Besides using more resources, alpha-blended forms are only available in Windows NT 5.0 (Windows 2000) and later versions of the operating system.
You should think twice before enabling alpha blending on the main form because such forms are slower and the user may have trouble seeing the contents of the form.
If you really want to have an alpha-blended form in your application, you should only enable alpha blending on forms that are used a short period of time, like the splash screen or the application's About box.
Form Style
To completely configure a form and make it look and work like you want it to, you usually have to modify three properties: BorderIcons, BorderStyle, and Position.
The Position property specifies the initial position of the form. You can use the Position property to, for instance, place the form exactly where you want it to be, allow the operating system to place the form where it wants the form to be, or center the form on the screen.
The BorderIcons property is a set property that specifies which icons appear on the title bar of the form. The most important value in the BorderIcons set is the biSystemMenu value. If biSystemMenu is included in the BorderIcons set, the title bar displays the application icon, the Close button, and the system menu when you click the application icon. The biMinimize and biMaximize values display the Minimize and Maximize buttons, respectively. If you only include the biMinimize value, the Maximize button is also displayed, but it is disabled. The biHelp value displays the Help button. The Help button is only intended to be displayed on dialog boxes and its purpose is to provide context-sensitive help for the controls on the form.
The BorderStyle property specifies both the visual appearance of the form and the form's behavior. The default setting, bsSizeable, defines a form that has a thick border and enables the user to resize the form. The bsSingle value defines a form with a thin border that cannot be resized. The bsSizeToolWin and bsToolWindow values enable us to define toolbar windows, windows that look like standard windows but have thinner title bars.
The most extreme of all values is the bsNone value, which produces a non-resizable form that has no title bar and no borders (see Figure 12-5). This setting is very useful if you want to create a custom user interface.
Although the bsNone value gives you complete control over the user interface, the form cannot be moved or resized. If you want to use a form without the system title bar in your applications, you need to write the code that allows the user to move your custom form on the screen. There are several ways to provide this functionality, and the way that requires the least technical knowledge is the one that uses the form's mouse events to move the form.
Basically, what we have to do is:
-
Check whether the user pressed the left mouse button.
-
Move the window while the left mouse button is pressed.
-
Stop moving the window when the user releases the left mouse button.
When the user presses a mouse button over the form, the form fires the OnMouseDown event. An empty handler of the OnMouseDown event looks like this:
Listing 12-2: An empty OnMouseDown event handler
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin end;
The OnMouseDown event handler provides a lot of information. The Sender parameter identifies the component that called the procedure. The Button parameter is an enumeration that identifies which button was pressed:
type TMouseButton = (mbLeft, mbRight, mbMiddle);
The Shift parameter is a set parameter that identifies which system keys and which mouse buttons were pressed when the event occurred, and the X and Y parameters hold pixel coordinates of the mouse pointer.
Inside the OnMouseDown event handler, you have to test whether the user pressed the left mouse button and save the X and Y coordinates for later use. To save the X and Y coordinates, you should declare two integer variables in the private section of the form. In addition to the integer variables, you have to declare a Boolean variable that will be used to identify whether or not the user can move the window.
Listing 12-3: Saving initial mouse coordinates
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private { Private declarations } FOldX: Integer; FOldY: Integer; FMoving: Boolean; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button = mbLeft then begin FMoving := True; FOldX := X; FOldY := Y; end; end; end.
The position of the window has to be updated every time the user moves the mouse pointer. Every time the position of the mouse changes while the mouse pointer is over the form, the form fires the OnMouseMove event.
Listing 12-4: An empty OnMouseMove event handler
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin end;
In the OnMouseMove event handler, we have to test if the left mouse button is pressed and update the position of the form. Since the OnMouseMove event doesn't provide the button information, we have to use the FMoving variable to determine if we have to update the form's position. To calculate the new position of the form, we only have to get the difference between the old mouse coordinates and the new mouse coordinates provided in the X and Y parameters of the OnMouseMove event.
Listing 12-5: Moving the form in the OnMouseMove event
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if FMoving = True then begin Left := Left + (X - FOldX); Top := Top + (Y - FOldY); end; end;
Finally, we need to write code that stops moving the window when the user releases the left mouse button. When a button is released over a form, the form fires an OnMouseUp event. The OnMouseUp event provides the same parameters as the OnMouseDown event, but we actually don't need parameters. We only have to set the FMoving variable to False, and the user won't be able to move the window until he or she presses the left mouse button again.
Listing 12-6: Finishing the moving process
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FMoving := False; end;
Now that you can move the borderless form on the screen, you can create custom user interfaces, like the one displayed in Figure 12-6.
It's actually pretty easy to create partially transparent forms like the one displayed in Figure 12-6. The first thing you need to have is the bitmap that you want to display on the form. Next, you need to define a transparent color, that is, a color that will be treated as transparent. In this example, magenta (fuchsia, RGB(255, 0, 255)) was selected as the transparent color.
Next, you have to place the bitmap on the form. The easiest way to display an image on the form is to use the TImage component, which can be found in the Additional category.
When you add a TImage component to the form, the TImage component is displayed as an empty frame. Set the TImage component's Left and Top properties to 0 and set the AutoSize property to True. When AutoSize is True, the component will be automatically resized when you assign an image to it. To assign an image to the Image component, you have to use the Picture property. When you select the Picture property in the Object Inspector, the Object Inspector displays a button with three dots. This button is only displayed for properties that have custom property editors. Click on the button to display the Picture Editor. You can use the Load button to select and load an image and the Clear button to remove the image from the form. Note that images assigned to the Picture property are saved in the form file and are included in the final executable.
After you've loaded the image, you have to tell the form that you want to treat a color as transparent and you have to specify the exact color that is to be treated as transparent. You can do this by setting the form's TransparentColor property to True and the TransparentColorValue property to the color that identifies the transparent sections in the image. In this case, the color that identifies the transparent sections in the image is clFuchsia.
That's it. When you run the application, you should be able to see a partially transparent form. But if you try to move the form, you'll notice that the code no longer works. Actually, the code still works, but it's never called because the TImage component covers the surface of the form. When a mouse button is pressed or released, or when the mouse pointer is moved, the TImage component receives the notification of these events, not the form. Thus, the TImage component fires its OnMouseMove, OnMouseDown, and OnMouseUp events.
What we have to do now is move the form in response to the mouse events of the TImage component. But we don't have to rewrite the code that moves the form because Delphi enables us to assign a single event handler to more events. Thus, to enable the user to move the form, we have to call the existing event handlers in response to the TImage's mouse events. To do this, select the TImage component, display the Events tab on the Object Inspector, and assign appropriate event handlers to the OnMouseDown, OnMouseMove, and OnMouseUp events (see Figure 12-9).
Useful Events
Most applications have to execute some code at application startup. This code usually includes reading user settings or something similar. The best event for code that needs to execute only once and only at application startup is the OnCreate event. For code that executes only once you should avoid the OnActivate event, since it fires every time the form regains focus (after losing it).
Usually, the OnCreate event is good enough for startup code. But there are some methods that may cause errors when called in the OnCreate event. One of them is the SetFocus method. For instance, if you try to focus a text box in the OnCreate event, you will receive an error, as shown in Figure 12-10.
The solution to this problem is really simple. If a method causes trouble in the OnCreate event, call the method inside the OnShow event.
Finally, to execute a piece of code when the application shuts down, write the code in the OnDestroy event handler.
The following example shows how to use the OnCreate and OnDestroy events. The application opens a text file in the OnCreate event and enables the user to add text to the file as long as the application is running. When the user closes the application, the application closes the file in the OnDestroy event. In this case, the OnClose event can also be used.
Listing 12-7: Using the OnCreate and OnDestroy events
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) Label1: TLabel; Edit1: TEdit; WriteButton: TButton; CloseButton: TButton; procedure WriteButtonClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); procedure CloseButtonClick(Sender: TObject); private { Private declarations } FFile: TextFile; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.CloseButtonClick(Sender: TObject); begin Close; end; procedure TMainForm.FormCreate(Sender: TObject); begin AssignFile(FFile, 'C:\Something.txt'); {$I-} Rewrite(FFile); {$I+} { Disable the Write button if the file can't be opened. } if IOResult <> 0 then WriteButton.Enabled := False; end; procedure TMainForm.FormDestroy(Sender: TObject); begin { If the Write button is enabled, the file is opened and it needs to be closed. } if WriteButton.Enabled then CloseFile(FFile); end; procedure TMainForm.WriteButtonClick(Sender: TObject); begin if Edit1.Text <> '' then WriteLn(FFile, Edit1.Text); end; end.