Chapter 43
Making a dialog box a component

You will find it convenient to make a frequently used dialog box into a component that you add to the Component palette. Your dialog box components will work just like the components that represent the standard Windows common dialog boxes. The goal is to create a simple component that a user can add to a project and set properties for at design time.

Making a dialog box a component requires these steps:

  1. Defining the component interface
  2. Creating and registering the component
  3. Creating the component interface
  4. Testing the component

The Delphi "wrapper" component associated with the dialog box creates and executes the dialog box at runtime, passing along the data the user specified. The dialog-box component is therefore both reusable and customizable.

In this chapter, you will see how to create a wrapper component around the generic About Box form provided in the Delphi Object Repository.

Note: Copy the files ABOUT.PAS and ABOUT.DFM into your working directory.

There are not many special considerations for designing a dialog box that will be wrapped into a component. Nearly any form can operate as a dialog box in this context.

Defining the component interface

Before you can create the component for your dialog box, you need to decide how you want developers to use it. You create an interface between your dialog box and applications that use it.

For example, look at the properties for the common dialog box components. They enable the developer to set the initial state of the dialog box, such as the caption and initial control settings, then read back any needed information after the dialog box closes. There is no direct interaction with the individual controls in the dialog box, just with the properties in the wrapper component.

The interface must therefore contain enough information that the dialog box form can appear in the way the developer specifies and return any information the application needs. You can think of the properties in the wrapper component as being persistent data for a transient dialog box.

In the case of the About box, you do not need to return any information, so the wrapper's properties only have to contain the information needed to display the About box properly. Because there are four separate fields in the About box that the application might affect, you will provide four string-type properties to provide for them.

Creating and registering the component

Creation of every component begins the same way: create a unit, derive a component class, register it, compile it, and install it on the Component palette. This process is outlined in "Creating a new component".

For this example, follow the general procedure for creating a component, with these specifics:

The resulting unit should look like this:

unit AboutDlg;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;
type
  TAboutBoxDlg = class(TComponent)
  end;
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('Samples', [TAboutBoxDlg]);
end;
end.

The new component now has only the capabilities built into TComponent. It is the simplest nonvisual component. In the next section, you will create the interface between the component and the dialog box.

Creating the component interface

These are the steps to create the component interface:

  1. Including the form unit
  2. Adding interface properties
  3. Adding the Execute method

Including the form unit

For your wrapper component to initialize and display the wrapped dialog box, you must add the form's unit to the uses clause of the wrapper component's unit.

Append About to the uses clause of the AboutDlg unit.

The uses clause now looks like this:

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms
  About;

The form unit always declares an instance of the form class. In the case of the About box, the form class is TAboutBox, and the About unit includes the following declaration:

var
  AboutBox: TAboutBox;

So by adding About to the uses clause, you make AboutBox available to the wrapper component.

Adding interface properties

Before proceeding, decide on the properties your wrapper needs to enable developers to use your dialog box as a component in their applications. Then, you can add declarations for those properties to the component's class declaration.

Properties in wrapper components are somewhat simpler than the properties you would create if you were writing a regular component. Remember that in this case, you are just creating some persistent data that the wrapper can pass back and forth to the dialog box. By putting that data in the form of properties, you enable developers to set data at design time so that the wrapper can pass it to the dialog box at runtime.

Declaring an interface property requires two additions to the component's class declaration:

Interface properties of this sort do not need access methods. They use direct access to their stored data. By convention, the class field that stores the property's value has the same name as the property, but with the letter F in front. The field and the property must be of the same type.

For example, to declare an integer-type interface property called Year, you would declare it as follows:

type
  TMyWrapper = class(TComponent)
  private
    FYear: Integer;                                { field to hold the Year-property 
data }
  published
    property Year: Integer read FYear write FYear;        { property matched with storage }
  end;

For this About box, you need four string-type properties--one each for the product name, the version information, the copyright information, and any comments.

type
  TAboutBoxDlg = class(TComponent)
  private
    FProductName, FVersion, FCopyright, FComments: string;               { declare fields }

  published
    property ProductName: string read FProductName write FProductName;
    property Version: string read FVersion write FVersion;
    property Copyright: string read FCopyright write FCopyright;
    property Comments: string read FComments write FComments;
  end;

When you install the component onto the Component palette and place the component on a form, you will be able to set the properties, and those values will automatically stay with the form. The wrapper can then use those values when executing the wrapped dialog box.

Adding the Execute method

The final part of the component interface is a way to open the dialog box and return a result when it closes. As with the common-dialog-box components, you will use a boolean function called Execute that returns True if the user clicks OK, or False if the user cancels the dialog box.

The declaration for the Execute method always looks like this:

type
  TMyWrapper = class(TComponent)
  public
    function Execute: Boolean;
  end;

The minimum implementation for Execute needs to construct the dialog box form, show it as a modal dialog box, and return either True or False, depending on the return value from ShowModal.

Here is the minimal Execute method for a dialog-box form of type TMyDialogBox:

function TMyWrapper.Execute: Boolean;
begin
  DialogBox := TMyDialogBox.Create(Application);                     { construct the form }
  try
    Result := (DialogBox.ShowModal = IDOK);     { execute; set result based on how closed }
  finally
    DialogBox.Free;                                                 { dispose of the form }
  end;
end;

Note the use of a try..finally block to ensure that the application disposes of the dialog-box object even if an exception occurs. In general, whenever you construct an object this way, you should use a try..finally block to protect the block of code and make certain the application frees any resources it allocates.

In practice, there will be more code inside the try..finally block. Specifically, before calling ShowModal, the wrapper will set some of the dialog box's properties based on the wrapper component's interface properties. After ShowModal returns, the wrapper will probably set some of its interface properties based on the outcome of the dialog box execution.

In the case of the About box, you need to use the wrapper component's four interface properties to set the contents of the labels in the About box form. Because the About box does not return any information to the application, there is no need to do anything after calling ShowModal. Write the About-box wrapper's Execute method so that it looks like this:

Within the public part of the TAboutDlg class, add the declaration for the Execute method:

type 
  TAboutDlg = class(TComponent)
public
  function Execute: Boolean;
end;

function TAboutBoxDlg.Execute: Boolean;
begin
  AboutBox := TAboutBox.Create(Application);                        { construct About box }
  try
    if ProductName = '' then                            { if product name's left blank... }
      ProductName := Application.Title;                { ...use application title instead }
    AboutBox.ProductName.Caption := ProductName;                      { copy product name }
    AboutBox.Version.Caption := Version;                              { copy version info }
    AboutBox.Copyright.Caption := Copyright;                        { copy copyright info }
    AboutBox.Comments.Caption := Comments;                                { copy comments }
    AboutBox.Caption := 'About ' + ProductName;                   { set About-box 
caption }
    with AboutBox do begin
      ProgramIcon.Picture.Graphic := Application.Icon;                        { copy icon }
      Result := (ShowModal = IDOK);                              { execute and set result }
    end;
  finally
    AboutBox.Free;                                                 { dispose of About box }
  end;
end;

Testing the component

Once you have installed the dialog-box component, you can use it as you would any of the common dialog boxes, by placing one on a form and executing it. A quick way to test the About box is to add a command button to a form and execute the dialog box when the user clicks the button.

For example, if you created an About dialog box, made it a component, and added it to the Component palette, you can test it with the following steps:

  1. Create a new project.
  2. Place an About-box component on the main form.
  3. Place a command button on the form.
  4. Double-click the command button to create an empty click-event handler.
  5. In the click-event handler, type the following line of code:
    AboutBoxDlg1.Execute;
    
  6. Run the application.

When the main form appears, click the command button. The About box appears with the default project icon and the name Project1. Choose OK to close the dialog box.

You can further test the component by setting the various properties of the About-box component and again running the application.