Chapter 2
Using Object Pascal with the VCL

This chapter discusses how to use Object Pascal and the object and component library in Delphi applications.

Object Pascal and the VCL

Object Pascal, a set of object-oriented extensions to standard Pascal, is the language of Delphi. The Visual Component Library (VCL) is a hierarchy of classes--written in Object Pascal and tied to the Delphi IDE--that allows you to develop applications quickly. Using Delphi's Component palette and Object Inspector, you can place VCL components on forms and manipulate their properties without writing code.

All VCL objects descend from TObject, an abstract class whose methods encapsulate fundamental behavior like construction, destruction, and message handling. TObject is the immediate ancestor of many simple classes.

Components in the VCL descend from the abstract class TComponent. Components are objects that you can manipulate on forms at design time. Visual components--that is, components like TForm and TSpeedButton that appear on the screen at runtime--are called controls, and they descend from TControl.

Despite its name, the VCL consists mostly of nonvisual objects. The Delphi IDE allows you to add many nonvisual components to your programs by dropping them onto forms. For example, if you were writing an application that connects to a database, you might place a TDataSource component on a form. Although TDataSource is nonvisual, it is represented on the form by an icon (which doesn't appear at runtime). You can manipulate the properties and events of TDataSource in the Object Inspector just as you would those of a visual control.

When you write classes of your own in Object Pascal, they should descend from TObject. By deriving new classes from the VCL's base class (or one of its descendants), you provide your classes with essential functionality and ensure that they work with the VCL.

Using the object model

Object-oriented programming is an extension of structured programming that emphasizes code reuse and encapsulation of data with functionality. Once you create an object (or, more formally, a class), you and other programmers can use it in different applications, thus reducing development time and increasing productivity.

If you want to create new components and put them on the Delphi Component palette, see "Overview of component creation."

What is an object?

An object, or class, is a data type that encapsulates data and operations on data in a single unit. Before object-oriented programming, data and operations (functions) were treated as separate elements.

You can begin to understand objects if you understand Object Pascal records. Records (analogous to structures in C) are made of up fields that contain data, where each field has its own type. Records make it easy to refer to a collection of varied data elements.

Objects are also collections of data elements. But objects--unlike records--contain procedures and functions that operate on their data. These procedures and functions are called methods.

An object's data elements are accessed through properties. The properties of Delphi objects have values that you can change at design time without writing code. If you want a property value to change at runtime, you need to write only a small amount of code.

The combination of data and functionality in a single unit is called encapsulation. In addition to encapsulation, object-oriented programming is characterized by inheritance and polymorphism. Inheritance means that objects derive functionality from other objects (called ancestors); objects can modify their inherited behavior. Polymorphism means that different objects derived from the same ancestor support the same method and property interfaces, which often can be called interchangeably.

Examining a Delphi object

When you create a new project, Delphi displays a new form for you to customize. In the Code editor, Delphi declares a new class type for the form and produces the code that creates the new form instance. The generated code looks like this:

unit Unit1;
interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type
  TForm1 = class(TForm)  { The type declaration of the form begins here }
  private
    { Private declarations }
  public
    { Public declarations }
  end;  { The type declaration of the form ends here }

var
  Form1: TForm1;

implementation  { Beginning of implementation part }
{$R *.DFM}
end.  { End of of implemntation part and unit}

The new class type is TForm1, and it is derived from type TForm, which is also a class.

A class is like a record in that they both contain data fields, but a class also contains methods--code that acts on the object's data. So far, TForm1 appears to contain no fields or methods, because you haven't added to the form any components (the fields of the new object) and you haven't created any event handlers (the methods of the new object). TForm1 does contain inherited fields and methods, even though you don't see them in the type declaration.

This variable declaration declares a variable named Form1 of the new type TForm1.

var
  Form1: TForm1;

Form1 represents an instance, or object, of the class type TForm1. You can declare more than one instance of a class type; you might want to do this, for example, to create multiple child windows in a Multiple Document Interface (MDI) application. Each instance maintains its own data, but all instances use the same code to execute methods.

Although you haven't added any components to the form or written any code, you already have a complete Delphi application that you can compile and run. All it does is display a blank form.

Suppose you add a button component to this form and write an OnClick event handler that changes the color of the form when the user clicks the button. The result might look like this:

Figure 2.1   A simple form

When the user clicks the button, the form's color changes to green. This is the event-handler code for the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Color := clGreen;
end;

Objects can contain other objects as data fields. Each time you place a component on a form, a new field appears in the form's type declaration. If you create the application described above and look at the code in the Code editor, this is what you see:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type
  TForm1 = class(TForm)
    Button1: TButton;  { New data field }
    procedure Button1Click(Sender: TObject);  { New method declaration }
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);  { The code of the new method }
begin
  Form1.Color := clGreen;
end;

end.

TForm1 has a Button1 field that corresponds to the button you added to the form. TButton is a class type, so Button1 refers to an object.

All the event handlers you write in Delphi are methods of the form object. Each time you create an event handler, a method is declared in the form object type. The TForm1 type now contains a new method, the Button1Click procedure, declared within the TForm1 type declaration. The code that implements the Button1Click method appears in the implementation part of the unit.

Changing the name of a component

You should always use the Object Inspector to change the name of a component. For example, suppose you want to change a form's name from the default Form1 to a more descriptive name, such as ColorBox. When you change the form's Name property in the Object Inspector, the new name is automatically reflected in the form's .DFM file (which you usually don't edit manually) and in the Object Pascal source code that Delphi generates:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type
  TColorBox = class(TForm)  { Changed from TForm1 to TColorBox }
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ColorBox: TColorBox;  { Changed from Form1 to ColorBox }

implementation

{$R *.DFM}

procedure TColorBox.Button1Click(Sender: TObject);
begin
  Form1.Color := clGreen;  { The reference to Form1 didn't change! }
end;

end.

Note that the code in the OnClick event handler for the button hasn't changed. Because you wrote the code, you have to update it yourself and correct any references to the form:

procedure TColorBox.Button1Click(Sender: TObject);
begin
  ColorBox.Color := clGreen;
end;

Inheriting data and code from an object

The TForm1 object described in "Examining a Delphi object" seems simple. TForm1 appears to contain one field (Button1), one method (Button1Click), and no properties. Yet you can show, hide, or resize of the form, add or delete standard border icons, and set up the form to become part of a Multiple Document Interface (MDI) application. You can do these things because the form has inherited all the properties and methods of the VCL component TForm. When you add a new form to your project, you start with TForm and customize it by adding components, changing property values, and writing event handlers. To customize any object, you first derive a new object from the existing one; when you add a new form to your project, Delphi automatically derives a new form from the TForm type:

TForm1 = class(TForm)

A derived object inherits all the properties, events, and methods of the object it derives from. The derived object is called a descendant and the object it derives from is called an ancestor. If you look up TForm in the online Help, you'll see lists of its properties, events, and methods, including the ones that TForm inherits from its ancestors. An object can have only one immediate ancestor, but it can have many direct descendants.

Objects, components, and controls

Figure 2.2   A simplified hierarchy diagram

The diagram above is a greatly simplified view of the inheritance hierarchy of the Visual Component Library. Every object inherits from TObject, and many objects inherit from TComponent. Controls, which inherit from TControl, have the ability to display themselves at runtime. A control like TCheckBox inherits all the functionality of TObject, TComponent, and TControl, and adds specialized capabilities of its own.

Scope and qualifiers

Scope determines the accessibility of an object's fields, properties, and methods. All members declared within an object are available to that object and its descendants. Although a method's implementation code appears outside of the object declaration, the method is still within the scope of the object because it is declared within the object's declaration.

When you write code to implement a method that refers to properties, methods, or fields of the object where the method is declared, you don't need to preface those identifiers with the name of the object. For example, if you put a button on a new form, you could write this event handler for the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Color := clFuchsia;
  Button1.Color := clLime;
end;

The first statement is equivalent to

Form1.Color := clFuchsia

You don't need to qualify Color with Form1 because the Button1Click method is part of TForm1; identifiers in the method body therefore fall within the scope of the TForm1 instance where the method is called. The second statement, in contrast, refers to the color of the button object (not of the form where the event handler is declared), so it requires qualification.

Delphi creates a separate unit (source code) file for each form. If you want to access one form's components from another form's unit file, you need to qualify the component names, like this:

Form2.Edit1.Color := clLime;

In the same way, you can access a component's methods from another form. For example,

Form2.Edit1.Clear;

To access Form2's components from Form1's unit file, you must also add Form2's unit to the uses clause of Form1's unit.

The scope of an object extends to the object's descendants. You can, however, redeclare a field, property, or method within a descendant object. Such redeclarations either hide or override the inherited member.

For more information about scope, inheritance, and the uses clause, see the Object Pascal Language Guide.

Private, protected, public, and published declarations

When you declare a field, property, or method, the new member has a visibility indicated by one of the keywords private, protected, public, or published. The visibility of a member determines its accessibility to other objects and units.

For more information about visibility, see the Object Pascal Language Guide.

Using object variables

You can assign one object variable to another object variable if the variables are of the same type or assignment compatible. In particular, you can assign an object variable to another object variable if the type of the variable you are assigning to is an ancestor of the type of the variable being assigned. For example, here is a TDataForm type declaration and a variable declaration section declaring two variables, AForm and DataForm:

type
  TDataForm = class(TForm)
  Button1: TButton;
    Edit1: TEdit;
    DataGrid1: TDataGrid;
    Database1: TDatabase;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  AForm: TForm;
  DataForm: TDataForm;

AForm is of type TForm, and DataForm is of type TDataForm. Because TDataForm is a descendant of TForm, this assignment statement is legal:

AForm := DataForm;

Suppose you write an event handler for the OnClick event of a button. When the button is clicked, the event handler for the OnClick event is called. Each event handler has a Sender parameter of type TObject:

procedure TForm1.Button1Click(Sender: TObject);
begin
...
end;

Because Sender is of type TObject, any object can be assigned to Sender. The value of Sender is always the control or component that responds to the event. You can test Sender to find the type of component or control that called the event handler using the reserved word is. For example,

if Sender is TEdit then
  DoSomething
else
  DoSomethingElse;

Creating, instantiating, and destroying objects

Many of the objects you use in Delphi, such as buttons and edit boxes, are visible at both design time and runtime. Some, such as common dialog boxes, appear only at runtime. Still others, such as timers and datasource components, have no visual representation at runtime.

You may want to create your own objects. For example, you could create a TEmployee object that contains Name, Title, and HourlyPayRate properties. You could then add a CalculatePay method that uses the data in HourlyPayRate to compute a paycheck amount. The TEmployee type declaration might look like this:

type
  TEmployee = class(TObject)
  private
    FName: string;
    FTitle: string;
    FHourlyPayRate: Real;
  public
    property Name: string read FName write FName;
    property Title: string read FTitle write FTitle;
    property HourlyPayRate: Real read FHourlyPayRate write FHourlyPayRate;
    function CalculatePay: Real;
  end;

In addition to the fields, properties, and methods you've defined, TEmployee inherits all the methods of TObject. You can place a type declaration like this one in either the interface or implementation part of a unit, and then create instances of the new class by calling the Create method that TEmployee inherits from TObject:

var
  Employee: TEmployee;
begin
  Employee := TEmployee.Create;
end;

The Create method is called a constructor. It allocates memory for a new instance object and returns a reference to the object.

Components on a form are created and destroyed automatically by Delphi. But if you write your own code to instantiate objects, you are responsible for disposing of them as well. Every object inherits a Destroy method (called a destructor) from TObject. To destroy an object, however, you should call the Free method (also inherited from TObject), because Free verifies that the object still exists before calling Destroy. For example,

Employee.Free

destroys the Employee object and deallocates its memory.

Components and ownership

Delphi has a built-in memory-management mechanism that allows one component to assume responsibility for freeing another. The former component is said to own the latter. The memory for an owned component is automatically freed when its owner's memory is freed. The owner of a component--the value of its Owner property--is determined by a parameter passed to the constructor when the component is created. By default, a form owns all components on it and is in turn owned by the application. Thus, when the application shuts down, the memory for all forms and the components on them is freed.

Ownership applies only to TComponent and its descendants. If you create, for example, a TStringList or TCollection object (even if it is associated with a form), you are responsible for freeing the object.

Note: Don't confuse a component's owner with its parent. See "Parent properties".

Using components

All components share features inherited from TComponent. By placing components on forms, you build the interface and functionality of your application. The standard components included with Delphi are sufficient for most application development, but you can extend the VCL by creating components of your own. For more information about creating custom components, see "Overview of component creation."

Delphi's standard components

The Component palette contains a selection of components that handle a wide variety of programming tasks. You can add, remove, and rearrange components on the palette, and you can create component templates and frames that group several components.

The components on the palette are arranged in pages according to their purpose and functionality. Which pages appear in the default configuration depends on the version of Delphi you are running. Table 2.1 lists typical default pages and the kinds of components they contain.

Table 2.1   Component palette pages 

Page name

Contents

Standard

Standard Windows controls, menus

Additional

Additional controls

Win32

Windows 9x/NT 4.0 common controls

System

Components and controls for system-level access, including timers, multimedia, and DDE

Internet

Components for internet communication protocols and Web applications

Data Access

Nonvisual components for accessing databases tables, queries, and reports

Data Controls

Visual, data-aware controls

Decision Cube

Controls that let you summarize information from databases and view it from a variety of perspectives

QReport

Quick Report components for creating embedded reports

Dialogs

Windows common dialog boxes

Win 3.1

Components for compatibility with Delphi 1.0 projects

Samples

Sample custom components

ActiveX

Sample ActiveX controls

Midas

Components used for creating multi-tiered database applications

The online Help provides information about the components on the default palette. The components on the ActiveX and Samples pages, however, are provided as examples only and are not documented.

Properties common to visual components

All visual components (descendants of TControl) share certain properties including

While these properties are inherited from TControl, they are published--and hence appear in the Object Inspector--only for components to which they are applicable. For example, TImage does not publish the Color property, since its color is determined by the graphic it displays.

Position and size properties

Four properties define the position and size of a control on a form:

These properties aren't accessible in nonvisual components, but Delphi does keep track of where you place the component icons on your forms. Most of the time you'll set and alter these properties by manipulating the control's image on the form or using the Alignment palette. You can, however, alter them at runtime.

Display properties

Four properties govern the general appearance of a control:

Parent properties

To maintain a consistent appearance across your application, you can make any control look like its container--called its parent--by setting the parent... properties to True. For example, if you place a button on a form and set the button's ParentFont property to True, changes to the form's Font property will automatically propagate to the button (and to the form's other children). Later, if you change the button's Font property, your font choice will take effect and the ParentFont property will revert to False.

Note: Don't confuse a component's parent with its owner. See "Components and ownership".

Navigation properties

Several properties determine how users navigate among the controls in a form:

Drag-and-drop properties

Two component properties affect drag-and-drop behavior:

Drag-and-dock properties

The following properties control drag-and-dock behavior.

For more information, see "Implementing drag-and-dock in controls".

Text controls

Many applications present text to the user or allow the user to enter text. The type of control used for this purpose depends on the size and format of the information.

Use this component:

When you want users to do this:

Edit

Edit a single line of text

Memo

Edit multiple lines of text

MaskEdit

Adhere to a particular format, such as a postal code or phone number

RichEdit

Edit multiple lines of text using rich text format

Properties common to all text controls

All of the text controls have these properties in common:

Properties shared by memo and rich text controls

Memo and rich text controls, which handle multiple lines of text, have several properties in common:

At runtime, you can select all the text in the memo with the SelectAll method.

Rich text controls

The rich edit component is a memo control that supports rich text formatting, printing, searching, and drag-and-drop of text. It allows you to specify font properties, alignment, tabs, indentation, and numbering.

Specialized input controls

The following components provide additional ways of capturing input.

Use this component:

When you want users to do this:

ScrollBar

Select values on a continuous range

TrackBar

Select values on a continuous range (more visually effective than scroll bar)

UpDown

Select a value from a spinner attached to an edit component

HotKey

Enter Ctrl/Shift/Alt keyboard sequences

Scroll bars

The scroll bar component is a Windows scroll bar that you can use to scroll the contents of a window, form, or other control. In the OnScroll event handler, you write code that determines how the control behaves when the user moves the scroll bar.

The scroll bar component is not used very often, since many visual components provide scroll bars of their own that don't require additional coding. For example, TForm has VertScrollBar and HorzScrollBar properties that automatically configure scroll bars on the form. To create a scrollable region within a form, use TScrollBox.

Track bars

A track bar can set integer values on a continuous range. It is useful for adjusting properties like volume and brightness. The user moves the slide indicator by dragging it to a particular location or clicking within the bar.

Figure 2.3   Three views of the track bar component

      

Up-down controls

An up-down control consists of a pair of arrow buttons that allow users to change an integer value in fixed increments. The current value is given by the Position property; the increment, which defaults to 1, is specified by the Increment property. Use the Associate property to attach another component (such as an edit control) to the up-down control.

Hot key controls

Use the hot key component to assign a keyboard shortcut that transfers focus to any control. The HotKey property contains the current key combination and the Modifiers property determines which keys are available for HotKey.

Splitter control

A splitter placed between aligned controls allows users to resize the controls. Used with components like panels and group boxes, splitters let you divide a form into several panes with multiple controls on each pane.

After placing a panel or other control on a form, add a splitter with the same alignment as the control. The last control should be client-aligned, so that it fills up the remaining space when the others are resized. For example, you can place a panel at the left edge of a form, set its Alignment to alLeft, then place a splitter (also aligned to alLeft) to the right of the panel, and finally place another panel (aligned to alLeft or alClient) to the right of the splitter.

Set MinSize to specify a minimum size the splitter must leave when resizing its neighboring control. Set Beveled to True to give the splitter's edge a 3D look.

Buttons and similar controls

Aside from menus, buttons provide the most common way to invoke a command in an application. Delphi offers several button-like controls:

Use this component:

To do this:

Button

Present command choices on buttons with text

BitBtn

Present command choices on buttons with text and glyphs

SpeedButton

Create grouped toolbar buttons

CheckBox

Present on/off options

RadioButton

Present a set of mutually exclusive choices

ToolBar

Arrange tool buttons and other controls in rows and automatically adjust their sizes and positions

CoolBar

Display a collection of windowed controls within movable, resizable bands

Button controls

Users click button controls to initiate actions. Double-clicking a button at design time takes you to the button's OnClick event handler in the Code editor.

Bitmap buttons

A bitmap button (BitBtn) is a button control that presents a bitmap image on its face.

Speed buttons

Speed buttons, which usually have images on their faces, can function in groups. They are commonly used with panels to create toolbars.

Check boxes

A check box is a toggle that presents the user with two, or sometimes three, choices.

Radio buttons

Radio buttons present a set of mutually exclusive choices. You can use individual radio buttons or the radio group component, which arranges groups of radio buttons automatically. See "Grouping components" for more information.

Toolbars

Toolbars provide an easy way to arrange and manage visual controls. You can create a toolbar out of a panel component and speed buttons, or you can use the ToolBar component, then right-click and choose New Button to add buttons to the toolbar. The ToolBar component has several advantages: Buttons on a toolbar automatically maintain uniform dimensions and spacing; other controls maintain their relative position and height; controls can automatically wrap around to start a new row when they do not fit horizontally; and the ToolBar offers display options like transparency, pop-up borders, and spaces and dividers to group controls.

Cool bars

A cool bar (or rebar) contains child controls that can be moved and resized independently. Each control resides on an individual band. The user positions the controls by dragging the sizing grip to the left of each band.

The cool bar requires version 4.70 or later of COMCTL32.DLL (usually located in the WINDOWS\SYSTEM or WINDOWS\SYSTEM32 directory) at both design time and runtime.

Handling lists

Lists present the user with a collection of items to select from. Several components display lists:

Use this component:

To display:

ListBox

A list of text strings

CheckListBox

A list with a check box in front of each item

ComboBox

An edit box with a scrollable drop-down list

TreeView

A hierarchical list

ListView

A list of (draggable) items with optional icons, columns, and headings

DateTimePicker

A list box for entering dates or times

MonthCalendar

A calendar for selecting dates

Use the nonvisual TStringList and TImageList components to manage sets of strings and images. For more information about string lists, see "Working with string lists".

List boxes and check-list boxes

List boxes and check-list boxes display lists from which users can select items.

Combo boxes

A combo box combines an edit box with a scrollable list. When users enter data into the control--by typing or selecting from the list--the value of the Text property changes.

Use the Style property to select the type of combo box you need:

Tree views

A tree view displays items in an indented outline. The control provides buttons that allow nodes to be expanded and collapsed. You can include icons with items' text labels and display different icons to indicate whether a node is expanded or collapsed. You can also include graphics, such as a check boxes, that reflect state information about the items.

List views

List views display lists in various formats. Use the ViewStyle property to choose the kind of list you want:

Date-time pickers and month calendars

The DateTimePicker component displays a list box for entering dates or times, while the MonthCalendar component presents a calendar for entering dates or ranges of dates. To use these components, you must have a recent version of COMCTL32.DLL (usually located in the WINDOWS\SYSTEM or WINDOWS\SYSTEM32 directory) at both design time and runtime.

Grouping components

A graphical interface is easier to use when related controls and information are presented in groups. Delphi provides several components for grouping components:

Use this component:

When you want this:

GroupBox

A standard group box with a title

RadioGroup

A simple group of radio buttons

Panel

A more visually flexible group of controls

ScrollBox

A scrollable region containing controls

TabControl

A set of mutually exclusive notebook-style tabs

PageControl

A set of mutually exclusive notebook-style tabs with corresponding pages, each of which may contain other controls

HeaderControl

Resizable column headers

Group boxes and radio groups

A group box is a standard Windows component that arranges related controls on a form. The most commonly grouped controls are radio buttons. After placing a group box on a form, select components from the Component palette and place them in the group box. The Caption property contains text that labels the group box at runtime.

The radio group component simplifies the task of assembling radio buttons and making them work together. To add radio buttons to a radio group, edit the Items property in the Object Inspector; each string in Items makes a radio button appear in the group box with the string as its caption. The value of the ItemIndex property determines which radio button is currently selected. Display the radio buttons in a single column or in multiple columns by setting the value of the Columns property. To respace the buttons, resize the radio group component.

Panels

The panel component provides a generic container for other controls. Panels can be aligned with the form to maintain the same relative position when the form is resized. The BorderWidth property determines the width, in pixels, of the border around a panel.

Scroll boxes

Scroll boxes create scrolling areas within a form. Applications often need to display more information than will fit in a particular area. Some controls--such as list boxes, memos, and forms themselves--can automatically scroll their contents. Scroll boxes give you the additional flexibility to define arbitrary scrolling subregions of a form.

Like panels and group boxes, scroll boxes contain other controls. But a scroll box is normally invisible. If the controls in the scroll box cannot fit in its visible area, the scroll box automatically displays scroll bars.

Tab controls

The tab control component looks like notebook dividers. You can create tabs by editing the Tabs property in the Object Inspector; each string in Tabs represents a tab. The tab control is a single panel with one set of components on it. To change the appearance of the control when the tabs are clicked, you need to write an OnChange event handler. To create a multipage dialog box, use a page control instead.

Page controls

The page control component is a page set suitable for multipage dialog boxes. To create a new page in a page control, right-click the control and choose New Page.

Header controls

A header control is a is a set of column headers that the user can select or resize at runtime. Edit the control's Sections property to add or modify headers.

Visual feedback

There are many ways to provide users with information about the state of an application. For example, some components--including TForm--have a Caption property that can be set at runtime. You can also create dialog boxes to display messages. In addition, the following components are especially useful for providing visual feedback at runtime.

Use this component or property:

To do this:

Label and StaticText

Display non-editable text

StatusBar

Display a status region (usually at the bottom of a window)

ProgressBar

Show the amount of work completed for a particular task

Hint and ShowHint

Activate fly-by or "tool-tip" help

HelpContext and HelpFile

Link context-sensitive online Help

Labels and static-text components

Labels display text and are usually placed next to other controls. The standard label component, TLabel, is a nonwindowed control, so it cannot receive focus; when you need a label with a window handle, use TStaticText instead. Label properties include the following:

Status bars

Although you can use a panel to make a status bar, it is simpler to use the status-bar component. By default, the status bar's Align property is set to alBottom, which takes care of both position and size.

You will usually divide a status bar into several text areas. To create text areas, edit the Panels property in the Object Inspector, setting each panel's Width, Alignment, and Text properties from the Panels editor. The Text property contains the text displayed in the panel.

Progress bars

When your application performs a time-consuming operation, you can use a progress bar to show how much of the task is completed. A progress bar displays a dotted line that grows from left to right.

Figure 2.4   A progress bar

The Position property tracks the length of the dotted line. Max and Min determine the range of Position. To make the line grow, increment Position by calling the StepBy or StepIt method. The Step property determines the increment used by StepIt.

Help and hint properties

Most visual controls can display context-sensitive Help as well as fly-by hints at runtime. The HelpContext and HelpFile properties establish a Help context number and Help file for the control.

The Hint property contains the text string that appears when the user moves the mouse pointer over a control or menu item. To enable hints, set ShowHint to True; setting ParentShowHint to True causes the control's ShowHint property to have the same value as its parent's.

Grids

Grids display information in rows and columns. If you're writing a database application, use the TDBGrid or TDBCtrlGrid component described in "Using data controls". Otherwise, use a standard draw grid or string grid.

Draw grids

A draw grid (TDrawGrid) displays arbitrary data in tabular format. Write an OnDrawCell event handler to fill in the cells of the grid.

String grids

The string grid component is a descendant of TDrawGrid that adds specialized functionality to simplify the display of strings. The Cells property lists the strings for each cell in the grid; the Objects property lists objects associated with each string. All the strings and associated objects for a particular column or row can be accessed through the Cols or Rows property.

Graphic display

The following components make it easy to incorporate graphics into an application.

Use this component

To display:

Image

Graphics files

Shape

Geometric shapes

Bevel

3D lines and frames

PaintBox

Graphics drawn by your program at runtime

Animate

AVI files

Images

The image component displays a graphical image, like a bitmap, icon, or metafile. The Picture property determines the graphic to be displayed. Use Center, AutoSize, Stretch, and Transparent to set display options.

Shapes

The shape component displays a geometric shape. It is a nonwindowed control and cannot receive user input. The Shape property determines which shape the control assumes. To change the shape's color or add a pattern, use the Brush property, which holds a TBrush object. How the shape is painted depends on the Color and Style properties of TBrush.

Bevels

The bevel component is a line that can appear raised or lowered. Some components, such as TPanel, have built-in properties to create beveled borders. When such properties are unavailable, use TBevel to create beveled outlines, boxes, or frames.

Paint boxes

The paint box allows your application to draw on a form. Write an OnPaint event handler to render an image directly on the paint box's Canvas. Drawing outside the boundaries of the paint box is prevented. For more information, see "Overview of graphics programming".

Animation control

The animation component is a window that silently displays an Audio Video Interleaved (AVI) clip. An AVI clip is a series of bitmap frames, like a movie. Although AVI clips can have sound, animation controls work only with silent AVI clips. The files you use must be either uncompressed AVI or compressed using run-length encoding (RLE).

Windows common dialog boxes

The components on the Dialogs page of the Component palette make the Windows common dialog boxes available in Delphi applications. These dialog boxes provide a consistent user interface for standard operations like finding and opening files, setting fonts and colors, and printing. The dialogs do not appear at runtime until activated by a call to their Execute method.

Setting component properties

Published properties can be set at design time in the Object Inspector and, in some cases, with special property editors.

To set properties at runtime, assign them new values in your application source code.

For information about the properties of each component, see the VCL Help.

Using the Object Inspector

When you select a component on a form, the Object Inspector displays its published properties and (when appropriate) allows you to edit them. Use the Tab key to toggle between the Value column and the Property column. When the cursor is in the Property column, you can navigate to any property by typing the first letters of its name. For properties of Boolean or enumerated types, you can choose values from a drop-down list or toggle their settings by double-clicking in Value column. If a plus (+) symbol appears next to a property name, clicking the plus symbol displays a list of subvalues for the property.

By default, properties in the Legacy category are not shown; to change the display filters, right-click in the Object Inspector and choose View. For more information, see "property categories" in the online Help.

When more than one component is selected, the Object Inspector displays all properties--except Name--that are shared by the selected components. If the value for a shared property differs among the selected components, the Object Inspector displays either the default value or the value from the first component selected. When you change a shared property, the change applies to all selected components.

Using property editors

Some properties, such as Font, have special property editors. Such properties appear with ellipsis marks (...) next to their values when the property is selected in the Object Inspector. To open the property editor, double-click in the Value column or click the ellipsis mark. With some components, double-clicking the component on the form also opens a property editor.

Property editors let you set complex properties from a single dialog box. They provide input validation and often let you preview the results of an assignment.

Setting properties at runtime

Any writable property can be set at runtime in your source code. For example, you can dynamically assign a caption to a form:

Form1.Caption := MyString;

Calling methods

Methods are called just like ordinary procedures and functions. For example, visual controls have a Repaint method that refreshes the control's image on the screen. You could call the Repaint method in a draw-grid object like this:

DrawGrid1.Repaint;

As with properties, the scope of a method name determines the need for qualifiers. If you want, for example, to repaint a form within an event handler of one of the form's child controls, you don't have to prepend the name of the form to the method call:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Repaint;
end;

For more information about scope, see "Scope and qualifiers".

Working with events and event handlers

In Delphi, almost all the code you write is executed, directly or indirectly, in response to events. An event is a special kind of property that represents a runtime occurrence, often a user action. The code that responds directly to an event--called an event handler--is an Object Pascal procedure. The sections that follow show how to

Generating a new event handler

Delphi can generate skeleton event handlers for forms and other components. To create an event handler,

  1. Select a component.
  2. Click the Events tab in the Object Inspector. The Events page of the Object Inspector displays all events defined for the component.
  3. Select the event you want, then double-click the Value column or press Ctrl+Enter. Delphi generates the event handler in the Code editor and places the cursor inside the begin...end block.
  4. Inside the begin...end block, type the code that you want to execute when the event occurs.

Generating a handler for a component's default event

Some components have a default event, which is the event the component most commonly needs to handle. For example, a button's default event is OnClick. To create a default event handler, double-click the component in the Form Designer; this generates a skeleton event-handling procedure and opens the Code editor with the cursor in the body of the procedure, where you can easily add code.

Not all components have a default event. Some components, such as the Bevel, don't respond to any events. Other components respond differently when you double-click on them in the Form Designer. For example, many components open a default property editor or other dialog when they are double-clicked at design time.

Locating event handlers

If you generated a default event handler for a component by double-clicking it in the Form Designer, you can locate that event handler in the same way. Double-click the component, and the Code editor opens with the cursor at the beginning of the event-handler body.

To locate an event handler that's not the default,

  1. In the form, select the component whose event handler you want to locate.
  2. In the Object Inspector, click the Events tab.
  3. Select the event whose handler you want to view and double-click in the Value column. The Code editor opens with the cursor at the beginning of the event-handler body.

Associating an event with an existing event handler

You can reuse code by writing event handlers that respond to more than one event. For example, many applications provide speed buttons that are equivalent to drop-down menu commands. When a button initiates the same action as a menu command, you can write a single event handler and assign it to both the button's and the menu item's OnClick event.

To associate an event with an existing event handler,

  1. On the form, select the component whose event you want to handle.
  2. On the Events page of the Object Inspector, select the event to which you want to attach a handler.
  3. Click the down arrow in the Value column next to the event to open a list of previously written event handlers. (The list includes only event handlers written for events of the same name on the same form.) Select from the list by clicking an event-handler name.

The procedure above is an easy way to reuse event handlers. Action lists, however, provide a more powerful tool for centrally organizing the code that responds to user commands. For more information about action lists, see "Using Actions".

Using the Sender parameter

In an event handler, the Sender parameter indicates which component received the event and therefore called the handler. Sometimes it is useful to have several components share an event handler that behaves differently depending on which component calls it. You can do this by using the Sender parameter in an if...then...else statement. For example, the following code displays the title of the application in the caption of a dialog box only if the OnClick event was received by Button1.

procedure TMainForm.Button1Click(Sender: TObject);
begin
if Sender = Button1 then  
  AboutBox.Caption := 'About ' + Application.Title  
else AboutBox.Caption := '';  
AboutBox.ShowModal;
end;

Displaying and coding shared events

When components share events, you can display their shared events in the Object Inspector. First, select the components by holding down the Shift key and clicking on them in the Form Designer; then choose the Events tab in the Object Inspector. From the Value column in the Object Inspector, you can now create a new event handler for, or assign an existing event handler to, any of the shared events.

Associating menu events with event handlers

Delphi's Menu Designer, along with the MainMenu and PopupMenu components, make it easy to supply your application with drop-down and pop-up menus. For the menus to work, however, each menu item must respond to the OnClick event, which occurs whenever the user chooses the menu item or presses its accelerator or shortcut key. This section explains how to associate event handlers with menu items. For information about the Menu Designer and related components, see "Creating and managing menus".

To create an event handler for a menu item,

  1. Open the Menu Designer by double-clicking on a MainMenu or PopupMenu object.
  2. Select a menu item in the Menu Designer. In the Object Inspector, make sure that a value is assigned to the item's Name property.
  3. From the Menu Designer, double-click the menu item. Delphi generates an event handler in the Code editor and places the cursor inside the begin...end block.
  4. Inside the begin...end block, type the code that you want to execute when the user selects the menu command.

To associate a menu item with an existing OnClick event handler,

  1. Open the Menu Designer by double-clicking on a MainMenu or PopupMenu object.
  2. Select a menu item in the Menu Designer. In the Object Inspector, make sure that a value is assigned to the item's Name property.
  3. On the Events page of the Object Inspector, click the down arrow in the Value column next to OnClick to open a list of previously written event handlers. (The list includes only event handlers written for OnClick events on this form.) Select from the list by clicking an event handler name.

Deleting event handlers

When you delete a component using the Form Designer, Delphi removes the component from the form's type declaration. It does not, however, delete any associated methods from the unit file, since these methods may still be called by other components on the form. You can manually delete a method--such as an event handler--but if you do so, be sure to delete both the method's forward declaration (in the interface section of the unit) and its implementation (in the implementation section); otherwise you'll get a compiler error when you build your project.

Using helper objects

The VCL includes a variety of nonvisual objects that simplify common programming tasks. This section describes a few Helper objects that facilitate

Working with lists

Several VCL objects provide functionality for creating and managing lists:

For more information about these objects, see the VCL Reference in the online Help.

Working with string lists

Applications often need to manage lists of character strings. Examples include items in a combo box, lines in a memo, names of fonts, and names of rows and columns in a string grid. The VCL provides a common interface to any list of strings through an object called TStrings and its descendant TStringList. In addition to providing functionality for maintaining string lists, these objects allow easy interoperability; for example, you can edit the lines of a memo (which are an instance of TStrings) and then use these lines as items in a combo box (also an instance of TStrings).

A string-list property appears in the Object Inspector with TStrings in the Value column. Double-click TStrings to open the String List editor, where you can edit, add, or delete lines.

You can also work with string-list objects at runtime to perform such tasks as

Loading and saving string lists

String-list objects provide SaveToFile and LoadFromFile methods that let you store a string list in a text file and load a text file into a string list. Each line in the text file corresponds to a string in the list. Using these methods, you could, for example, create a simple text editor by loading a file into a memo component, or save lists of items for combo boxes.

The following example loads a copy of the WIN.INI file into a memo field and makes a backup copy called WIN.BAK.

procedure EditWinIni;
var
  FileName: string;  { storage for file name }
begin
  FileName := 'C:\WINDOWS\WIN.INI';  { set the file name }
  with Form1.Memo1.Lines do 
  begin
    LoadFromFile(FileName);  { load from file }
    SaveToFile(ChangeFileExt(FileName, '.BAK'));  { save into backup file }
  end;
end;

Creating a new string list

A string list is typically part of a component. There are times, however, when it is convenient to create independent string lists, for example to store strings for a lookup table. The way you create and manage a string list depends on whether the list is short-term (constructed, used, and destroyed in a single routine) or long-term (available until the application shuts down). Whichever type of string list you create, remember that you are responsible for freeing the list when you finish with it.

Short-term string lists

If you use a string list only for the duration of a single routine, you can create it, use it, and destroy it all in one place. This is the safest way to work with string lists. Because the string-list object allocates memory for itself and its strings, you should use a try...finally block to ensure that the memory is freed even if an exception occurs.

  1. Construct the string-list object.
  2. In the try part of a try...finally block, use the string list.
  3. In the finally part, free the string-list object.

The following event handler responds to a button click by constructing a string list, using it, and then destroying it.

procedure TForm1.Button1Click(Sender: TObject);
var
  TempList: TStrings;  { declare the list }
begin
  TempList := TStringList.Create;  { construct the list object }
  try
    { use the string list }
  finally
    TempList.Free;  { destroy the list object }
  end;
end;

Long-term string lists

If a string list must be available at any time while your application runs, construct the list at start-up and destroy it before the application terminates.

  1. In the unit file for your application's main form, add a field of type TStrings to the form's declaration.
  2. Write an event handler for the main form's OnCreate event. (OnCreate is the default event for a form, so just double-click on the form to generate a skeleton event handler.) The OnCreate event handler, which executes before the form appears, should create a string list and assign it to the field you declared in the first step.
  3. Write an event handler that frees the string list for the form's OnDestroy event.

This example uses a long-term string list to record the user's mouse clicks on the main form, then saves the list to a file before the application terminates.

unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
    ClickList: TStrings;  { declare the field }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ClickList := TStringList.Create;  { construct the list }
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ClickList.SaveToFile(ChangeFileExt(Application.ExeName, '.LOG'));  { save the list }
  ClickList.Free;  { destroy the list object }
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  ClickList.Add(Format('Click at (%d, %d)', [X, Y]));  { add a string to the list }
end;

end.

Manipulating strings in a list

Operations commonly performed on string lists include

Counting the strings in a list

The read-only Count property returns the number of strings in the list. Since string lists use zero-based indexes, Count is one more than the index of the last string.

Accessing a particular string

The array property Strings contains the strings in the list, referenced by a zero-based index. Since Strings is the default property for string lists, you can omit the Strings identifier when accessing the list; thus

StringList1.Strings[0] := 'This is the first string.';

is equivalent to

StringList1[0] := 'This is the first string.';

Finding the position of a string in the list

To locate a string in a string list, use the IndexOf method. IndexOf returns the index of the first string in the list that matches the parameter passed to it, and returns -1 if the parameter string is not found. IndexOf finds exact matches only; if you want to match partial strings, you must iterate through the string list yourself.

For example, you could use IndexOf to determine whether a given file name is found among the Items of a list box:

if FileListBox1.Items.IndexOf('WIN.INI') > -1 ...

Iterating through strings in a list

To iterate through the strings in a list, use a for loop that runs from zero to Count - 1.

This example converts each string in a list box to uppercase characters.

procedure TForm1.Button1Click(Sender: TObject);
var
  Index: Integer;
begin 
  for Index := 0 to ListBox1.Items.Count - 1 do 
    ListBox1.Items[Index] := UpperCase(ListBox1.Items[Index]);
end;

Adding a string to a list

To add a string to the end of a string list, call the Add method, passing the new string as the parameter. To insert a string into the list, call the Insert method, passing two parameters: the string and the index of the position where you want it placed. For example, to make the string "Three" the third string in a list, you would use

Insert(2, 'Three');

To append the strings from one list onto another, call AddStrings:

StringList1.AddStrings(StringList2);  { append the strings from StringList2 to StringList1 }

Moving a string within a list

To move a string in a string list, call the Move method, passing two parameters: the current index of the string and the index you want assigned to it. For example, to move the third string in a list to the fifth position, you would use

Move(2, 4)

Deleting a string from a list

To delete a string from a string list, call the list's Delete method, passing the index of the string you want to delete. If you don't know the index of the string you want to delete, use the IndexOf method to locate it. To delete all the strings in a string list, use the Clear method.

This example uses IndexOf and Delete find and delete a string.

with ListBox1.Items do
begin
  if IndexOf('bureaucracy') > -1 then 
    Delete(IndexOf('bureaucracy'));
end;

Copying a complete string list

You can use the Assign method to copy strings from a source list to a destination list, overwriting the contents of the destination list. To append strings without overwriting the destination list, use AddStrings. For example,

Memo1.Lines.Assign(ComboBox1.Items);    { overwrites original strings }

copies the lines from a combo box into a memo (overwriting the memo), while

Memo1.Lines.AddStrings(ComboBox1.Items);   { appends strings to end }

appends the lines from the combo box to the memo.

When making local copies of a string list, use the Assign method. If you simply assign one string-list variable to another--

StringList1 := StringList2;

--the original string-list object will be lost, often with unpredictable results.

Associating objects with a string list

In addition to the strings stored in its Strings property, a string list can maintain references to objects, which it stores in its Objects property. Like Strings, Objects is an array with a zero-based index. The most common use for Objects is to associate bitmaps with strings for owner-draw controls.

Use the AddObject or InsertObject method to add a string and an associated object to the list in a single step. IndexOfObject returns the index of the first string in the list associated with a specified object. Methods like Delete, Clear, and Move operate on both strings and objects; for example, deleting a string removes the corresponding object (if there is one).

To associate an object with an existing string, assign the object to the Objects property at the same index. You cannot add an object without adding a corresponding string.

The Windows registry and INI files

The Windows system registry is a hierarchical database where applications store configuration information. The VCL object TRegistry supplies methods that read and write to the registry.

Until Windows 95, most applications stored configuration information in initialization files, usually named with the extension .INI. The VCL provides objects that facilitate maintenance and migration of programs that use INI files. Use

Using streams

Use specialized stream objects to read or write to storage media. Each descendant of TStream implements methods for accessing a particular medium, such as disk files, dynamic memory, and so on. TStream descendants include TFileStream, TStringStream, TMemoryStream, TBlobStream, and TWinSocketStream. In addition to methods for reading and writing, these objects permit applications to seek to an arbitrary position in the stream. Properties of TStream provide information about the stream, such as size and current position.

Using data modules and remote data modules

A data module is like a special form that contains nonvisual components. All the components in a data module could be placed on ordinary forms alongside visual controls. But if you plan on reusing groups of database and system objects, or if you want to isolate the parts of your application that handle database connectivity and business rules, then data modules provide a convenient organizational tool.

There are two types of data module: standard and remote. To create a single- or two-tiered application, use a standard data module. If you have the Client/Server or Enterprise edition of Delphi and are creating a multi-tiered application, you can add a remote data module to your application server; see "Adding a remote data module to an application server project".

Creating and editing data modules

To create a data module, choose File|New and double-click on Data Module. Delphi opens an empty data module in the Data Module Designer, displays the unit file for the new module in the Code editor, and adds the module to the current project. When you reopen an existing data module, Delphi displays its components in the Data Module Designer.

The Data Module Designer is divided into two panes. The left pane displays a hierarchical tree view of the components in the module. The right pane has two tabs: Components and Data Diagram. The Components page shows the components as they would appear on a form. The Data Diagram page shows a graphical representation of internal relationships among the components, such as master-detail links and lookup fields.

Figure 2.5   A simple data module

You can add components to a data module by selecting them on the Component palette and clicking in the Tree or Components view of the Data Module Designer. When a component is selected in the Data Module Designer, you can edit its properties in the Object Inspector just as you would if the component were on a form. For more information about the Data Module Designer, see the online Help.

Creating business rules in a data module

In a data module's unit file, you can write methods, including event handlers for the components in the module, as well as global routines that encapsulate business rules. For example, you might write a procedure to perform month-, quarter-, or year-end bookkeeping; you could call such a procedure from an event handler for a component in the module or from any unit that uses the module.

Accessing a data module from a form

To associate visual controls on a form with a data module, you must first add the data module to the form's uses clause. You can do this in several ways:

Adding a remote data module to an application server project

Some versions of Delphi allow you to add remote data modules to application server projects. A remote data module has an interface that clients in a multi-tiered application can access across networks. To add a remote data module to a project, choose File|New, select the Multitier page in the New Items dialog box, and double-click the desired type of module (Remote Data Module, MTS Data Module, or CORBA Data Module) to open the Remote Data Module wizard. Once you add a remote data module to a project, you use it just like a standard data module.

For more information about multi-tiered database applications, see "Creating multi-tiered applications."

Using the Object Repository

The Object Repository (Tools|Repository) makes it easy share forms, dialog boxes, frames, and data modules. It also provides templates for new projects and wizards that guide the user through the creation of forms and projects. The repository is maintained in DELPHI32.DRO (by default in the BIN directory), a text file that contains references to the items that appear in the Repository and New Items dialogs.

Sharing items within a project

You can share items within a project without adding them to the Object Repository. When you open the New Items dialog box (File|New), you'll see a page tab with the name of the current project. This page lists all the forms, dialog boxes, and data modules in the project. You can derive a new item from an existing item and customize it as needed.

Adding items to the Object Repository

You can add your own projects, forms, frames, and data modules to those already available in the Object Repository. To add an item to the Object Repository,

  1. If the item is a project or is in a project, open the project.
  2. For a project, choose Project|Add To Repository. For a form or data module, right-click the item and choose Add To Repository.
  3. Type a description, title, and author.
  4. Decide which page you want the item to appear on in the New Items dialog box, then type the name of the page or select it from the Page combo box. If you type the name of a page that doesn't exist, Delphi creates a new page.
  5. Choose Browse to select an icon to represent the object in the Object Repository.
  6. Choose OK.

Sharing objects in a team environment

You can share objects with your workgroup or development team by making a repository available over a network. To use a shared repository, all team members must select the same Shared Repository directory in the Environment Options dialog:

  1. Choose Tools|Environment Options.
  2. On the Preferences page, locate the Shared Repository panel. In the Directory edit box, enter the directory where you want to locate the shared repository. Be sure to specify a directory that's accessible to all team members.

The first time an item is added to the repository, Delphi creates a DELPHI32.DRO file in the Shared Repository directory if one doesn't exist already.

Using an Object Repository item in a project

To access items in the Object Repository, choose File|New. The New Items dialog appears, showing all the items available. Depending on the type of item you want to use, you have up to three options for adding the item to your project:

Copying an item

Choose Copy to make an exact copy of the selected item and add the copy to your project. Future changes made to the item in the Object Repository will not be reflected in your copy, and alterations made to your copy will not affect the original Object Repository item.

Copy is the only option available for project templates.

Inheriting an item

Choose Inherit to derive a new class from the selected item in the Object Repository and add the new class to your project. When you recompile your project, any changes that have been made to the item in the Object Repository will be reflected in your derived class, in addition to changes you make to the item in your project. Changes made to your derived class do not affect the shared item in the Object Repository.

Inherit is available for forms, dialog boxes, and data modules, but not for project templates. It is the only option available for reusing items within the same project.

Using an item

Choose Use when you want the selected item itself to become part of your project. Changes made to the item in your project will appear in all other projects that have added the item with the Inherit or Use option. Select this option with caution.

The Use option is available for forms, dialog boxes, and data modules.

Using project templates

Templates are predesigned projects that you can use as starting points for your own work. To create a new project from a template,

  1. Choose File|New to display the New Items dialog box.
  2. Choose the Projects tab.
  3. Select the project template you want and choose OK.
  4. In the Select Directory dialog, specify a directory for the new project's files.

Delphi copies the template files to the specified directory, where you can modify them. The original project template is unaffected by your changes.

Modifying shared items

If you modify an item in the Object Repository, your changes will affect all future projects that use the item as well as existing projects that have added the item with the Use or Inherit option. To avoid propagating changes to other projects, you have several alternatives:

Specifying a default project, new form, and main form

By default, when you choose File|New Application or File|New Form, Delphi displays a blank form. You can change this behavior by reconfiguring the Repository:

  1. Choose Tools|Repository
  2. If you want to specify a default project, select the Projects page and choose an item under Objects. Then select the New Project check box.
  3. If you want to specify a default form, select a Repository page (such as Forms), them choose a form under Objects. To specify the default new form (File|New Form), select the New Form check box. To specify the default main form for new projects, select the Main Form check box.
  4. Click OK.

Adding custom components to the IDE

You can install custom components--written by yourself or third parties--on the Component palette and use them in your applications. To write a component, see "Creating custom components". To install an existing component, see "Installing component packages".