'; zhtm += ''; zhtm += '

' + pPage + ''; zhtm += '
'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Teach Yourself Borland Delphi 4 in 21 Days -- Ch 8 -- Creating Applications in Delphi


Teach Yourself Borland Delphi 4 in 21 Days

Previous chapterNext chapterContents


- 8 -

Creating Applications in Delphi


Delphi provides a variety of tools that aid you in creating forms, dialog boxes, and applications. Today you learn about the following:

For starters, I'll spend some time discussing the Object Repository, which is where Delphi stores any prebuilt forms, applications, or other objects for you to reuse.

Following that discussion, you meet the wizards. Wizards provide a series of dialog boxes that guide you step by step through the creation process. You provide the details, and Delphi builds the form or application based on the information you provided. The wizards are a powerful tool for rapid application development. Later in the day I'll tell you how you can use resources in your Delphi applications. Finally, I'll close the day by talking about packages in Delphi.

Working with the Object Repository

The Object Repository is the means by which you can select predefined objects to use in your applications.

The Object Repository enables you to do the following:

That's just a sampling of what the Object Repository provides. There are other objects you can create in addition to those listed here.

Object Repository Pages and Options

The Object Repository is displayed automatically whenever you choose File | New from the main menu. Figure 8.1 shows the Object Repository window as it initially appears when you choose File | New with no project open.


NOTE: Strange as it might seem, the Object Repository is titled New Items, and the Object Repository configuration dialog box is titled Object Repository. To say that this is confusing is a bit of an understatement.

FIGURE 8.1. The Object Repository window.

The Object Repository has several pages, each of which contains different objects that you can incorporate into your applications. As you can see from Figure 8.1, the New tab is what is initially selected when the Object Repository is displayed. Table 8.1 lists the Repository pages and a description of the items you find on each page.

TABLE 8.1. THE OBJECT REPOSITORY PAGES.

Page/Tab Description
New Enables you to create a new application, form, or unit for use in your application. Also enables you to create advanced objects such as packages, DLLs, components, NT service applications, Web server applications, and data modules.
ActiveX Enables you to create new ActiveX controls, type libraries, COM objects, ActiveForms, and other ActiveX objects.
Multitier Enables you to create CORBA and MTS objects and data modules (Client/Server version only).
Forms Enables you to create standard forms from prebuilt forms such as an About box, a dual list box, tabbed pages, or QuickReports.
Dialogs Presents choices of several basic dialog box types from which you can choose. Also contains the Dialog Wizard.
Projects Displays full projects that you can choose from to initially set up an application. Also contains the Application Wizard.
Data Modules Enables you to choose from data modules in your application.
Business Includes wizards for database forms, database Web applications, reports, and charts, and a Decision Cube sample application.


NOTE: If you invoke the Object Repository when you already have a project open, you will see an additional tab in the Object Repository. The tab will have the name of your project on it. Clicking this tab will display a page that contains all the objects currently in the project. This enables you to quickly reuse a form or other object by simply selecting it from the Object Repository.

Across the bottom of each page you see three radio buttons. These buttons, labeled Copy, Inherit, and Use, determine how the selected object is implemented. Depending on the object selected, some of the radio buttons (or all) might be disabled. For example, all three radio buttons are always grayed out when the New page is displayed. This is because Copy is the only option available for objects on this page, so Delphi grays out all choices and applies the Copy option automatically.


NOTE: The Object Repository is sometimes called the Gallery.

The Copy Button

When you choose the Copy radio button, Delphi creates a copy of the selected object and places it in your application. At this point you are free to modify the object in any way you choose. The original object in the Repository is not altered when you make changes to the new object in your application.

To illustrate, let's say you have an often used form (a form in the traditional sense, not in the Delphi sense) printed on paper--a work schedule, for example. Let's say that you want to fill in that form with scheduling information. You wouldn't modify the original form because it would then be unusable for future reuse. Instead, you would put the original form in the copy machine, make a copy, and then return the original to some location for safekeeping.

You would then fill out the copy of the form as needed. Making a copy of an object in the Repository works in exactly the same way. You are free to modify the copy in any way you choose and the original remains safely tucked away. Making a copy is the safest method of object usage.

The Inherit Button

The Inherit method of usage is similar to Copy, but with one important distinction: The new object is still tied to the base object. If you modify the base object, the newly created object will be updated to reflect the changes made to the base object. The inverse is not true, however. You can modify the new object without it having any effect on the base object.

To illustrate this type of object usage, consider the following scenario: Frequently, information managers create a spreadsheet in a spreadsheet program and use the contents of that spreadsheet in a word-processing program to present a report.

They usually opt to link the data to the spreadsheet when pasting from the Clipboard or importing the spreadsheet into the word processor. That way, when changes are made to the spreadsheet, the word-processing document is automatically updated to reflect the new data. In the same way, changes made to a base form will automatically be reflected in all forms inherited from the base form. Use the Inherit option when you want to have several forms based on a common form that might change at some point. Any changes in the base form will be reflected in all inherited forms.

The Use Button

The Use option is not common. When you use an object, you are opening that object directly for editing. Select this option when you have saved an object in the Repository and you want to make permanent changes to that object. In the section about the Inherit option, I said that changes made to a base form would be reflected in all inherited forms. If you wanted to make changes to a base form, you would open it in the Object Repository with the Use option.

Using the Object Repository

Exactly what takes place when you select an object from the Object Repository depends on several factors. The factors include the type of object selected, whether a project is currently open, and the usage type you select (Copy, Inherit, or Use).

If you have an application open and you choose to create a new application from the Object Repository, you are prompted to save the current project (if necessary) before the new project is displayed.


TIP: Choosing File | New Application from the main menu is a shortcut for starting a new application. It is equivalent to choosing New from the main menu and then choosing the Application object from the Object Repository. Similarly, the New Form item on the main menu is a shortcut for its equivalent in the Object Repository.

Creating a new form from the Object Repository is treated differently based on whether a project is open at the time. If a project is open, the new form is added to the application as a form/unit pair. If no project is open, a new form and unit are created as a standalone form. A form created outside of a project must be added to a project before it can be used at runtime. Use this option when creating a new base form to add to the Object Repository.

If you choose to create a new unit or text file, the new file is simply created in the Code Editor (and, in the case of a new unit, added to the current project). You might create a new text file for several reasons. For example, let's say you want to implement a configuration file (an .ini file) in your application. You could create a new text file in the Object Repository to initially create the configuration file. Create a new unit any time you want to start a new source file for your application that is not associated with a form (an include file, for example).

Choosing a new DLL results in a new project being created with the project set up for a DLL target. Creating a new component or thread object results in a dialog box being presented that asks for more information about the object you are creating.

The Object Repository Views

The actual Object Repository window is a Win32 list view control similar to the right side of Windows Explorer (where the files are listed). As such, it has several views that you can choose from: Large Icons, Small Icons, List, and Details. By default, the view is set to Large Icons. To change the Object Repository view, right-click on the Object Repository and choose the view you want from the Object Repository context menu. Figure 8.2 shows the Object Repository with the Forms page selected and the view set to Details.

FIGURE 8.2. The Object Repository in Details view.

The Object Repository context menu also shows several sorting options. You can sort by object name, description, date, or author.


TIP: When the Object Repository is in the Details view, you can click a column header (Name, Description, Date, or Author) to instantly sort by that category.

Creating New Objects from the Object Repository

Certainly the most basic use of the Object Repository is creating a new object using an object from the Repository. To illustrate, you can create a simple application with a main form, an About dialog box, and a second form. Follow these steps:

1. Ensure that no other application is open. Choose File | New from the main menu. The Object Repository is displayed.

2. Click the Application icon and click OK to create a new application. A new application is created and a blank form is displayed.

3. Place two buttons on the form. Change the Caption property of one of the buttons to About... and the Caption property of the other button to Display Form2. Change the Name properties if desired.

4. Choose File | New from the main menu. The Object Repository is displayed again.

5. Click the Forms tab in the Object Repository.

6. Choose the About box object. Ensure that the Copy radio button is selected and click OK to create a new About Box form. The About box is displayed. Change any properties as needed.

7. Modify the About box as desired. (Enter your own information, change the icon, size, position, and so on.)

8. Select File | New from the main menu again. The Object Repository is displayed for the third time.

9. Click the Forms tab and choose the Dual list box object. Click OK to close the Object Repository. A dual list box form is displayed. (I had you choose this one just so you could see it.)

10. Write event handlers for the two buttons that display the About box and the second form as required. Don't forget to add the units for the About box and the second form in the uses clause of your main form.

11. Compile, run, and test the program.

No, this program doesn't do anything, but it does illustrate how you can use the Object Repository to quickly prototype an application. As time goes on, you will add your own custom objects to the Object Repository and then you can really be effective! Let's look at that next.

Adding Objects to the Object Repository

The Object Repository wouldn't be nearly as effective if you couldn't add your own objects to it. But you can add your own objects and you should. Adding frequently used objects to the Object Repository makes you a more efficient and, therefore, a more valuable programmer. There is no point in reinventing the wheel.

After you create an application, form, or other object, save it to the Repository so that you can reuse it whenever you want. Of course, you don't want to save every form you ever create in the Object Repository, just the ones you will reuse most often.

You can set out to create an object with the express purpose of adding it to the Repository, or you can add an object to the Repository during the normal course of application development. (The term object is pretty broad, so I'll use a specific example in order for this to make sense.) Let's say that you create an About box form while creating an application. Suddenly it dawns on you that you'd like to save this About box to use in all your programs. After all, it has your company name, logo, and all the copyright information all laid out just the way you like it, so it'd be a shame to have to re-create the same About box for every application you write. No problem--just add it to the Repository.

To add a form to the Object Repository, first save the form (if you don't save the form, you will be prompted to save it before continuing). Next, right-click anywhere on the form and choose Add To Repository from the Form Designer context menu. When you do, the Add To Repository dialog box is displayed as shown in Figure 8.3.

FIGURE 8.3. The Add To Repository dialog box.

The Forms list box on the left side of this dialog box lists the current forms as well as any other objects in the application (such as data modules). First, select the form that you want to add to the Object Repository.


NOTE: The active form in the Form Designer will already be selected in the Forms list box in the Add To Repository dialog box.

Now enter the object's title. This is the title that will appear below the icon in the Object Repository. The Description field is used to give further information about the object. This description is displayed when the Object Repository view is set to display all object details (refer to Figure 8.2). The Author field is where you type your name as the author of the object. You can enter your personal name, a company name, or any other identifying name.


NOTE: Most of the prebuilt objects in the Object Repository that come with Delphi have "Borland" as the author name (the exceptions are the QuickReport and TeeChart objects).

The Page field is used to select the Object Repository page where the new object will be placed. You can choose from one of the existing pages or simply type the name of a new page in the Page field. If a page with the name you type doesn't exist, Delphi will create a new page with that name. Near the bottom of the dialog box is a button labeled Browse that you can use to select the icon used to represent the object.


You can choose icons from the Borland Shared Files\Images\Icons directory or the Delphi 4\Objrepos directory. The icons in the Delphi 4\Objrepos directory are the icons used by Delphi for the items it places in the Object Repository.

After you fill in all the fields and select an icon, click OK to add the object to the Repository. The object is added to the Object Repository on the page you specified. You can now reuse that object any time you want. As you can see, adding an object to the Object Repository is nearly as easy as using an object.


When you add an object to the Object Repository, Delphi makes an entry in the Object Repository file that describes the object. This information includes the pathname where the form and source file for the object are located. If you move or delete an object's form or source file, you will not be able to use the object from the Object Repository.

Adding Projects to the Object Repository

Adding projects to the Object Repository is not much different than adding individual forms. To add a project to the Object Repository, choose Project | Add to Repository from the main menu. The Add To Repository dialog box is displayed just like it is when you add objects to the Repository, except the Forms list box is not displayed. Fill in any required information (title, description, author, and so on) and click OK and the project is added to the Repository.

After you are familiar with Delphi, you should create an application shell that has the features you use most often in your applications. Each time you start a new standard application, make a copy of the shell from the Object Repository. This way you can have your menus, toolbar, About box, and other standard dialog boxes all set up and ready to go in a matter of seconds. After the new application has been created, it can then be modified as with any project. You can add new forms, delete any unwanted forms, and so on.

Object Repository Housekeeping

You can manage the pages and objects in the Object Repository by using the Object Repository configuration dialog box.

To view the Object Repository configuration dialog box, choose Tools | Repository from the main menu or, if you have the Object Repository open, choose Properties from the Object Repository context menu. The configuration dialog box is displayed as shown in Figure 8.4.

This dialog box enables you to delete objects and pages from the Object Repository, move objects from one page to another, change the order of pages in the Object Repository, and more. The list of pages in the Object Repository is displayed in the list box labeled Pages on the left side of the dialog box. When you select one of the pages in the Pages list, the list box on the right (labeled Objects) displays the objects contained on that page.

FIGURE 8.4. The Object Repository configuration dialog box.



NOTE: The Pages list box has two important items of note. First, notice that the New page, which is always the first page displayed when the Object Repository is invoked, is not listed here. (The ActiveX and Multitier pages aren't listed in the Pages list box, either.) The New page is fixed and cannot be altered. Also notice that there is an item labeled [Object Repository]. This item is actually a list of all items on all pages of the Repository.


Managing Objects

Before you can edit, delete, or move an object, you must first select it. To select an object, click the object in the Objects list box. After you select an object, you can edit it by clicking the Edit Object button. Editing an object enables you to change the object's name, description, and author information, as well as the page on which the object is displayed.


TIP: To quickly edit an object, double-click it in the Objects list box.

You can delete an object by selecting it and then clicking the Delete Object button. You are prompted for confirmation before the object is removed from the page and from the Repository.


When an object is deleted from the Object Repository, it is removed from the Object Repository file and no longer shows up on any page in the Object Repository. However, the actual form file and source file that describe the object are not deleted from your hard drive.

An object can be moved from one page to another by simply dragging the object from the Objects list box to the Pages list box. Drop the object on the page on which you want the object to be located, and the object is moved.

Managing Pages

The previous section deals with editing, deleting, and moving individual objects. You can also add, delete, or remove Object Repository pages through the Object Repository configuration dialog box. Before you can delete a page, you must first delete all the objects on the page. After a page is empty, you can remove the page by clicking on the page name in the Pages list box and then clicking the Delete Page button. After checking to be sure the page is empty, Delphi deletes the page from the Object Repository.

A new page can be added by clicking the Add Page button. A dialog box pops up asking for the name of the new page. Just supply a new page name and when you click OK, the new page appears in the Pages list box. Renaming a page works essentially the same way. When you select a page and click the Rename Page button, a dialog box appears prompting you for the new page name.

The order in which the pages appear in the Object Repository can be changed. To change a page's position in the page order, click the page to highlight it and then click the up or down arrow button underneath the Pages list box to move the page up or down in the list. You can also drag a page to its new location if you want.

Setting Default Forms and Projects

The Object Repository configuration dialog box enables you to set three default objects:

You will notice that, depending on the object you select, one or two check boxes appear beneath the Objects list box. If you select a form, the New Form and Main Form check boxes appear. If you select a project, the New Project check box appears.

Making a form or project the default is easy. Let's say you create a main form that you want to be the default main form when a new application is created. Select the form from the Objects list box and click the Main Form check box at the bottom of the screen. When you click OK, that form will now be the default. Similarly, if you have a project that you want to be the default project, first locate it in the Object Repository configuration dialog box, click on it, and then check the New Project check box. From that point on, when you choose File | New Application from the main menu, the project you set as the default will appear.





NOTE: If you aren't careful, you can accidentally select a form as the default form for a new application. If this happens, be sure you check each form in the Object Repository configuration dialog box. One form will have the Main Form check box checked. Clear the check box and all will be back to normal. This also applies to the default project. Check the Projects page for any items that have the New Project check box checked.

Building Forms and Applications with the Wizards

Delphi has two built-in wizards designed to guide you through the application creation process. The Dialog Wizard aids you in creating dialog boxes, and the Application Wizard helps you create the basic layout of an application. These wizards are discussed in the following sections.

Using the Dialog Wizard

Truthfully, there isn't very much for a dialog box wizard to do because dialog boxes of any real value will need to be customized with the Form Designer. The Dialog Wizard is started from the Object Repository. First, choose File | New from the main menu to display the Object Repository. Next, switch to the Dialogs page and then double-click the Dialog Wizard icon. The Dialog Wizard is displayed as shown in Figure 8.5.

FIGURE 8.5. The Dialog Wizard.

You can choose to create a single-page dialog box or a tabbed (multipage) dialog box. The icon on the left side of the dialog box shows you what the dialog box looks like at each step. If you choose to create a single-page dialog box, when you click the Next button, you will see the next page of the Dialog Wizard (see Figure 8.6).

FIGURE 8.6. The second page of the Dialog Wizard.

This page enables you to choose whether you want buttons on the dialog box and, if so, whether you want them on the right side or the bottom of the dialog box. This is the last page of the Dialog Wizard when creating a single-page dialog box. After choosing the button layout you want, click the Finish button to have Delphi create the dialog box for you.

The new dialog box is displayed on the Form Designer complete with the features you chose through the wizard. It also has its BorderStyle property set to bsDialog, which is customary for forms used as dialog boxes. After the Dialog Wizard has created the basic dialog box, you can go to work with the Form Designer to add functionality to the dialog box.

If you choose to create a tabbed dialog box, the second page of the dialog box looks like the one shown in Figure 8.7. (Figure 8.7 shows the dialog box after page names have been added.)

This page has a multiline edit control in which you can enter the names of the individual tabs you want to see on the dialog box. Enter the text for each tab on a separate line, as illustrated in Figure 8.7. When you click the Next button, you will see the last page of the Dialog Wizard as you saw in Figure 8.6. Choose the location of the buttons, if any, and click the Finish button to have Delphi create the tabbed dialog box.

FIGURE 8.7. The Dialog Wizard creating a tabbed dialog box.



NOTE: The Dialog Wizard is most useful when creating tabbed dialog boxes. When creating single-page dialog boxes, it is easier to choose one of the pre-defined dialog boxes from the Object Repository rather than going through the Dialog Wizard.

Creating Applications with the Application Wizard

The Application Wizard is a useful tool that can help you quickly set up the shell of an application. To create a new application using the Application Wizard, choose File | New from the main menu. When the Object Repository appears, click the Projects tab and then double-click the Application Wizard icon.


NOTE: The New Application item on the main menu creates a new application based on the current default project setting. It doesn't start the Application Wizard as you might expect.

Let's walk through the Application Wizard one page at a time.

Page One: Selecting the Menus

When you start the Application Wizard, the first page is displayed as shown in Figure 8.8.

This page enables you to select the items you want on your application's main menu. You can choose to add a File menu, an Edit menu, a Window menu, and a Help menu. Place a check in the box for each menu item you want to appear on your menu bar.


NOTE: The Window menu is usually reserved for MDI applications. You probably won't put a Window menu on your SDI application's menu bar unless you have a specialty application that requires it.

FIGURE 8.8. Page one of the Application Wizard.



NOTE: The menus added by the Application Wizard are a reasonable representation of the menu items that are most commonly used in Windows applications. Remember that the Application Wizard is intended to give you a head start in creating your application. It is up to you to take the basic structure and modify it to make a working application.

After you have chosen the menus you want for your application, click the Next button to move to the next page.

Page Two: Setting the File Dialog Filters

If you chose to add a File menu to your application, the next page displayed will look like the one shown in Figure 8.9.

FIGURE 8.9. Setting filters for the file dialog boxes.

This page enables you to set the filters that your application's File Open and File Save dialog boxes will use. (Figure 8.9 shows the dialog box after the filters have been added.) Click the Add button to add a new filter. A dialog box is displayed asking for the description and the filter. Enter the filters exactly as you do when setting the Filter property for the common file dialog box components. Enter the textual description and then the actual file mask (*.bmp, for example). The Edit, Delete, Up, and Down buttons can be used as necessary to change, delete, or move the filter in the list.


NOTE: Pages two and three will be displayed only if you previously selected menus on page one of the Application Wizard. More specifically, page two will be displayed only if you selected a File menu on page one.

Page Three: Setting Up the Speedbar

Page three of the Application Wizard aids you in setting up a speedbar (also called a toolbar) for your application. This is possibly the most useful feature of the Application Wizard (not that the other features aren't useful). You can quickly lay out your speedbar through this page. Figure 8.10 shows the third page of the Application Wizard after a speedbar has been created.

FIGURE 8.10. Setting up the speedbar.

The list box on the left side of the page, labeled Menus, shows the four menus for which you can add buttons. When you choose one of the menus, the available buttons for that menu are displayed in the list box to the right of the Menus list box (labeled Available Commands). To add a speedbar button, click the button in the Available Commands list box and then click the Insert button. The button will be added to the sample speedbar at the top of the page.

The Space button can be used to add a separator to the speedbar. Adding separators visually distinguishes groups of buttons. Continue to add buttons and spaces as needed until the speedbar is complete. If you decide to remove a button, just click it in the sample speedbar and then click the Remove button.


NOTE: If you elected not to add a particular menu to your application, no buttons will be shown for that menu group. For example, if you did not add a Window menu, the Available Commands list box will be empty when you click on the Window item in the Menus list box.



TIP: Some specialty applications have a speedbar but don't have a menu. To create a speedbar with the Application Wizard, you must first have created a menu. To work around this, tell the Application Wizard that you want a menu and then build the speedbar. After the application has been generated, you can delete the MainMenu component from the application to remove the menu.


Page Four: Setting the Final Options

The fourth and last page of the Application Wizard enables you to set the program name, the path where the project should be stored on disk, and a few final options. Figure 8.11 shows the last page of the Application Wizard.

FIGURE 8.11. The final Application Wizard settings.

The first field on this page is where you specify the name of the application. This is not the name as it appears on the Project Options dialog box, but the filename that Delphi will use to save the project. You still need to set the application name in the Project Options dialog box. The second field is used to specify the directory in which the project should be saved. If you don't know the exact path, click the Browse button to the right of this field and choose the path from the Select Directory dialog box.


NOTE: You can use the Select Directory dialog box to create a directory as well as to select a directory. Click the Browse button to display the Select Directory dialog box. Enter the path for the directory you want to create and then click OK or press Enter. Delphi will prompt you to create the new directory if the directory you entered doesn't exist.

The bottom half of the final page gives you three additional options. If you are creating an MDI application, click the check box marked Create MDI Application. (MDI applications were discussed on Day 4, "The Delphi IDE Explored.") The remaining two check boxes enable you to implement a status bar and hint text for your components.

When you are sure you have made all the choices for your new application, click the Next button. Delphi creates the application based on the options you specified. Delphi writes as much code as possible for the application. This doesn't amount to a lot of code, but some of the basic code is already written for you. For example, if you chose a File menu, the FileOpenClick handler has been written and looks like this:

procedure TMainForm.FileOpen(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    { Add code to open OpenDialog.FileName }
  end;
end;

The code to execute the File Open dialog box is in place; you only have to write the code that actually deals with the returned filename.


TIP: After you create an Application Wizard project, you can choose Project | Add to Repository to save the project for later use. This will save you the trouble of going through the Application Wizard to create your basic application. You might want to add an About box before saving the project to the Repository.

Using the wizards is fast and easy. You will still need to write the program, of course, but Delphi gives you a head start by saving you from the tedium of creating the basic application elements. As RAD-friendly as Delphi is overall, the wizards simplify this process even more. The Delphi wizards are sort of like RAD on RAD!


NOTE: Delphi provides wizards other than the Dialog Wizard and the Application Wizard. For example, the Database Form Wizard (discussed on Day 17, "Building Database Forms") is used to create database forms, and the ActiveX Control Wizard (discussed on Day 15, "COM and ActiveX") aids in the creation of ActiveX controls. These are specialized wizards, so I didn't cover them in this chapter.

Adding Methods and Data Fields to Code

As you know by now, Delphi is a great tool for quickly creating the UI (user interface) portion of a Windows application. It creates event handlers for you so that you can begin entering code to drive your application. It won't be long, however, before you find the need to start adding more complicated code to your applications.

Part of that means adding your own data fields and methods to the code that Delphi generates. For example, a simple application might contain two dozen event handlers of various types. Delphi creates all these event handlers for you; you simply fill in the blanks with working code. To make the application a viable, working application, however, you might have to write another two-dozen methods of your own.

Adding your own methods and data fields to code generated by Delphi is not a difficult task, but you need to know the rules or you can get into trouble.

How Delphi Manages Class Declarations

As you know, when you create a new form in the Form Designer, Delphi creates the unit's source file automatically. When Delphi creates the class declaration, it essentially creates two sections. The first section is the part of the class declaration that Delphi manages. The second section is the part that you manage.

On Day 6, "Working with the Form Designer and the Menu Designer," you created the ScratchPad program. If you did the exercises at the end of that chapter, you also created an About box for the program and added a few more buttons. Listing 8.1 contains the main form's class declaration as it appears after adding these enhancements.

Keep in mind that the individual component declarations appear in the order the components were placed on the form. Your class declaration should have the same components as shown in Listing 8.1, but they might not be in the same order.

LISTING 8.1. THE class DECLARATION FOR ScratchPad'S MAIN FORM.

TMainForm = class(TForm)
    StatusBar1: TStatusBar;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    Memo: TMemo;
    MainMenu: TMainMenu;
    FileMenu: TMenuItem;
    FileNew: TMenuItem;
    FileOpen: TMenuItem;
    FileSave: TMenuItem;
    FileSaveAs: TMenuItem;
    N1: TMenuItem;
    FilePrint: TMenuItem;
    FilePrintSetup: TMenuItem;
    N2: TMenuItem;
    FileExit: TMenuItem;
    Edit1: TMenuItem;
    EditReplace: TMenuItem;
    EditFind: TMenuItem;
    N4: TMenuItem;
    EditPaste: TMenuItem;
    EditCopy: TMenuItem;
    EditCut: TMenuItem;
    N5: TMenuItem;
    EditUndo: TMenuItem;
    Help1: TMenuItem;
    HelpAbout: TMenuItem;
    HelpContents: TMenuItem;
    EditSelectAll: TMenuItem;
    N3: TMenuItem;
    EditWordWrap: TMenuItem;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    MemoPopup: TPopupMenu;
    PopupCut: TMenuItem;
    PopupCopy: TMenuItem;
    PopupPaste: TMenuItem;
    procedure FileExitClick(Sender: TObject);
    procedure EditCutClick(Sender: TObject);
    procedure EditCopyClick(Sender: TObject);
    procedure EditPasteClick(Sender: TObject);
    procedure FileNewClick(Sender: TObject);
    procedure FileSaveClick(Sender: TObject);
    procedure FileOpenClick(Sender: TObject);
    procedure FileSaveAsClick(Sender: TObject);
    procedure EditUndoClick(Sender: TObject);
    procedure EditSelectAllClick(Sender: TObject);
    procedure EditWordWrapClick(Sender: TObject);
    procedure HelpAboutClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

It is important to understand that the section between the first line of the class declaration and the private keyword should be considered off-limits. As they say, "Don't go there." Leave this section to Delphi to manage.


CAUTION: Placing any code in the Delphi-managed section of a form's class declaration can cause problems with your program. In some cases, you might just get compiler errors. In other cases, your program might be beyond repair (unusual but possible). Get in the habit of avoiding this section of the class declaration like the plague.

You can safely place any of your own class data fields or method declarations in either the private or the public section of the class declaration. You could add a protected section and place data fields or methods there too, of course.


A WORD ABOUT STATUS BARS AND HINTS

In a moment you're going to add support for hint text displayed in the status bar of the ScratchPad program. Before you do, though, you need a brief primer on how hint text is handled.

When the Application object's ShowHint property is set to True (the default) and the mouse cursor is placed over a component that also has its ShowHint property set to True, a hint event is triggered. The Application object has an event called OnHint that occurs whenever a hint event is triggered. The Application's Hint property will contain the hint text for the control that generated the hint event. An application can use the OnHint event to display the hint on a status bar.

The problem is that you can't directly access the OnHint event of the Application object. What you can do, however, is reassign the value of OnHint to point to one of your own methods. Then, when the hint event occurs, the event gets rerouted to your own OnHint handler. To do that, you have to write your own event handler for the OnHint event. Let's do that next.


Adding a Method to Your Code

To illustrate adding a method to an application, let's implement hint text for the ScratchPad program you wrote earlier. First, reopen the ScratchPad program.

What you do in this series of steps is assign hint text to each of the toolbar buttons and prepare the status bar to receive the hints. Remember, the toolbar buttons you placed on the toolbar on Day 6 are just for show right now, but that doesn't prevent you from adding hints to them. Do the following:

1. Ensure that the ScratchPad main form is visible. Click the first button on the main form's toolbar.

2. Locate the Hint property in the Object Inspector and type the following for the hint text:

Open|Open an Existing File


3. Change the ShowHint property to True.

4. Repeat steps 2 and 3 for any other buttons on the toolbar, adding whatever hint text you like for each button.

5. Click the status bar component along the bottom of the main form. Change the SimplePanel property to True. This enables the full status bar to display a text string through the SimpleText property.

Okay, now everything is ready to go, so it's time you did what you came here for. You're going to create your own OnHint handler and then name the method MyOnHint. Let's take this one step at a time. First, add the method declaration to the class declaration. Here goes:

1. Switch to the Code Editor and be sure the SPMain.pas file is visible.

2. Scroll down through the class declaration for the TScratchPad class until you locate the private section. Add this line of code after the private keyword:

procedure MyOnHint(Sender : TObject);


To give you perspective, the last few lines of the class declaration should now look like this:

private
  { Private declarations }
  procedure MyOnHint(Sender : TObject);
public
  { Public declarations }
end;

Okay, so far, so good. Now you've added the method declaration for your new method. Two more steps and you'll be done. First, you need to add the actual method to the implementation section. After that, you need to assign your new method to the Application object's OnHint event. Follow these steps:

1. Scroll to the bottom of the implementation section.

2. Enter the following code (just above the unit's final end keyword):

procedure TMainForm.MyOnHint(Sender: TObject);
begin
  StatusBar.SimpleText := Application.Hint;
end;


3. Go to the Object Inspector. Select the main form, ScratchPad, from the Object Selector.

4. Switch to the Events page in the Object Inspector and double-click in the Value column next to the OnCreate event. The Code Editor is displayed and is ready for you to type code.

5. Enter a line of code so that the FormCreate method looks like this:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnHint := MyOnHint;
end;


6. Compile and run the program. The long hint text you entered will appear in the status bar when you place the mouse cursor over a toolbar button. The short hint text will be displayed in a tooltip when you pause over the button.

Step 2 sets the hint text (from the Hint property of the Application object) to the SimpleText property of the StatusBar component. Step 5 takes the method you created in step 2 and assigns it to the OnHint event of the Application class. Each time an OnHint event occurs, the MyOnHint method is called and the hint text is displayed in the status bar.


NOTE: In the preceding example of implementing status bar hints, I took you the long way around. I wanted to show you how to add a method to your form and how to assign a method to an event. There is an easier way to implement status bar text, though. Simply set the status bar's AutoHint property to True. You still have to specify each component's hint text, but the rest is automatic. The AutoHint property is new in Delphi 4.

Adding a Class Data Field

Adding class data fields to a class generated in Delphi works in exactly the same way. All you have to do is ensure that you add the data field to the private or public section of the class declaration as you did earlier when adding a method to the class. You can also place data field declarations in the protected section if you have created one for your class.

Deleting Delphi-Generated Code

There might be times when you need to delete code that Delphi generated in your application. For example, you might have a button on a form that, due to design changes, is no longer needed. To delete the button, of course, all you have to do is select the button in the Form Designer and press the Delete button on the keyboard. No more button. Delphi deletes the button, but the OnClick handler associated with that button is still in the code.

Delphi knows that the button associated with that OnClick handler is gone, but it still doesn't delete the event handler because it is possible that other components are using the same event handler. It's up to you to delete the event handler if you want it removed from your code.

The actual deletion of the event handler is a trivial task. Simply remove the code from the event handler and save or compile the project. Delphi will remove any empty event handlers it finds.


NOTE: Some might say that if you are unsure about an event handler being used by other components, just leave it in the code. That's a bad solution, in my opinion. You need to take the responsibility for knowing what is in your code and getting rid of any unused methods. Although unused code doesn't hurt anything, it leads to a larger .exe file. In some cases, unused code can lead to performance degradation. Be diligent in paring your programs of unused or inefficient code.

Creating Component Templates

New Term: A component template is a component or group of components that you modify as desired and then save for later reuse.

Component templates enable you to create, save, and reuse groups of components. In fact, a component template doesn't have to be a group of components at all--it can be a single component. A quick example would probably help you see how useful component templates can be. But first, a quick lesson on the Windows edit control.

The standard Windows single-line edit control, like all Windows controls, has certain predefined behaviors. One of those behaviors deals with the way the Enter key is handled. If the user presses the Enter key when in an edit control, Windows looks for a default button on the window. If a default button is found, Windows essentially clicks the button.

What does this mean to you? Let's say you have several edit controls on a form and a default button such as an OK button (or any button with the Default property set to True). When you press the Enter key when an edit control has focus, the form will close. If there is no default button on the form, Windows will just beep. Although this is standard Windows behavior, many users find it annoying and confusing. What many users prefer, particularly when working with a form that has several edit fields, is that the Enter key moves focus to the next control rather than closing the form.

The solution to this problem is really pretty simple. All you have to do is provide an event handler for the OnKeyPress event and add code so that it looks like this:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = Char(VK_RETURN) then begin
    Key := #0;
    PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
  end;
end;

This code first checks to see whether the key pressed was the Enter key (a virtual key code of VK_RETURN). If so, it sets the value of Key to #0. This eliminates the beep that Windows emits when the Enter key is pressed in an edit control. The next line posts a Windows WM_NEXTDLGCTL message to the form. This message sets focus to the next control in the tab order. That's all there is to it.

After you have written the code for your new Edit component, you can save it as a component template. When you do, all the code is saved as well. Any code templates you create go into the Templates page of the Component palette. Let's create a component template so that you can see how it works. Perform these steps:

1. Place an Edit component on a blank form. Change its Name property to EnterAsTab and clear its Text property.

2. Switch to the Events page in the Object Inspector and create an event handler for the OnKeyPress event. Enter this code in the event handler:

if Key = Char(VK_RETURN) then begin
  Key := #0;
  PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
end;


3. Be sure the Edit component is selected and choose Component | Create Component Template from the main menu. The Component Template Information dialog box is displayed.

4. Enter TEnterAsTab in the Component Name field. The Component Template Information dialog box should now look like the one shown in Figure 8.12.

FIGURE 8.12. The Component Template Information dialog box.

5. Click OK to save the component template.

Now your Component palette has a tab called Templates. Switch to the Templates tab (you might have to scroll the Component palette tabs to find it), select your new component, and place it on the form. You will see that the code for the OnKeyPress event handler was included when the component was placed on the form.


TIP: If you have several of these components on a form, the code for the OnKeyPress event handler would be repeated for every EnterAsTab component on the form. Rather than duplicating code, you can just place one EnterAsTab component on the form. Any other components could be standard Edit components that have their OnKeyPress events hooked up to the OnKeyPress event handler for the EnterAsTab component.

One of the biggest advantages of component templates is that the code written for each component's event handlers is saved along with the component. Component templates enable you to have a collection of customized components at your disposal: common dialog boxes with predefined filters and titles, speed buttons with glyphs already included, list boxes or combo boxes that automatically load items from a file, or any of a number of other possibilities.

Although the concept of a component template works for a single component, it makes even more sense when dealing with multiple components. If you have a group of components that you place on your forms over and over again, you can create a component template from those components. After you have created a component template, reusing a group of components is only a click away.


NOTE: There are certainly some similarities between component templates and saving forms in the Object Repository. Use component templates for groups of components that you typically use as part of a larger form. Use the Object Repository to save entire forms that you want to reuse.

Using Resource Files

New Term: Every Windows program uses resources. Resources are those elements of a program that support the program but are not executable code.

A typical Windows program's resources include


NOTE: Version information can be easily added to your Delphi projects through the Version Info tab of the Project Options dialog box. The Project Options dialog box is discussed in detail tomorrow.

Resources are generally contained in a resource script file (a text file with an .rc extension). The resource script file is compiled by a resource compiler and then bound to the application's .exe file during the link phase.

Resources are usually thought of as being bound to the executable file. Some resources, such as bitmaps, string tables, and wave files, can be placed in external files (.bmp, .txt, and .wav) or they can be bound to the .exe and contained within the application file. You can opt to do it either way. Placing resources in the .exe file has two main advantages:

The downside to this approach is that your .exe will be slightly larger. The program file won't be any larger than the combined external resource files plus the executable, but the extra size could result in slightly longer load times for the program.

Your exact needs will determine whether you decide to keep your resources in external files or have your resources bound to the .exe. The important point to remember is that you can do it either way (or even both ways in the same program).

Resources in Delphi

A traditional Windows program will almost always contain at least one dialog box and an icon. A Delphi application, however, is a little different. First of all, there are no true dialog boxes in a Delphi application, so there are no dialog box resources per se (Delphi forms are stored as resources, but they are RCDATA resources and not dialog box resources).

A Delphi application does have a traditional icon resource, though. Delphi takes care of creating the resource file for the icon for you when you create the application. Similarly, when you choose bitmaps for speed buttons, Image components, or BitBtn components, Delphi includes the bitmap file you chose as part of the form's resource. The form and all its resources are then bound to the program file when the application is built. It's all more or less handled for you automatically.

There are times, however, when you will want to implement resources aside from the normal Delphi processes. For example, if you want to do animation, you will have to have a series of bitmaps that can be loaded as resources for the fastest possible execution speed. In that kind of situation, you are going to need to know how to bind the resources to your Delphi program file.

The act of binding the resource file to the executable is trivial, actually. It's much more difficult to actually create the resources. Creating basic resources such as bitmaps, icons, and cursors is not difficult with a good resource editor, but creating professional quality 3D bitmaps and icons is an art in itself. How many times have you seen a fairly decent program with really awful bitmap buttons? I've seen plenty. (Sorry, I'm getting off-track here.) You can create bitmaps, icons, and cursors with the Delphi Image Editor. (The Image Editor is discussed on Day 11, "Delphi Tools and Options.")

If you are going to create string resources, user data resources, wave file resources, or other specialty resources, you will probably need a third-party resource editor.


NOTE: If you have an old copy of Borland Pascal lying around, you can use the Resource Workshop from that product to edit specialty resources. After creating the resources, you will have an .rc file that you can compile into a .res file using the Borland Resource Compiler (BRCC32.EXE). The Borland Resource Compiler comes with Delphi. Technically, you could create the .rc file with any text editor and compile it with the Resource Compiler, but in reality it is much easier to use a resource editor.

Compiling Resource Files

After you create a resource file, you need to compile it with the resource compiler. You can do this in one of two ways:

Either way you end up with a .res file that you link to your application (I'll discuss that in just a bit). Project groups are discussed in detail tomorrow.

Compiling from the Command Line

To compile a resource file from the command line, simply open a command prompt box in Windows and enter a line similar to this:

brcc32 jjres.rc

This assumes, of course, that your Delphi 4\Bin directory is on the system path. If not, you'll have to type the full path to BRCC32.EXE. The resource compiler is very fast, so you might not even notice that the resource script was compiled.

Using a Batch File Project

Adding a batch file project to your project group is just as simple as compiling from the command line and has the added benefit of ensuring that the resource file will always be up to date. To get an idea how a batch file project works, perform these steps:

1. Create a new application. This application is just to add perspective to the process.

2. Choose View | Project Manager to open the Delphi Project Manager.

3. Click the Add New Project button on the Project Manager toolbar. The Object Repository is displayed.

4. Double-click the Batch File icon to create a new batch file project. The batch file project is added to the Project Manager as Project2.

5. Right-click on the batch file node and choose Save. Save the file as test.bat.

6. Right-click the batch file node again and choose Edit | Options. The Batch File Options dialog box is displayed.

7. Enter the following text in the Commands memo field:

del myfile.res
brcc32 myfile.rc


Figure 8.13 shows the Batch File Options dialog box after performing this step.

FIGURE 8.13. The Batch File Options dialog box.

8. Click OK to close the Batch File Options dialog box.

What you did in this exercise is create a batch file that will execute when the project group is compiled. The batch file commands you entered in step 7 delete a file called myfile.res and then call the Delphi resource compiler to build myfile.rc. Compiling myfile.rc with the resource compiler will produce a file called myfile.res.

Presumably the next project in the project group would be the project that uses myfile.res. You might be wondering why I delete the myfile.res first. I delete the file so that I am certain the resource compiler will build the file. If the resource compiler fails to create the resource file, any subsequent projects that use that resource file will fail to compile and a compiler error will alert me that something has gone wrong building the resource file.


NOTE: The book's code for today includes a project group that uses a batch file project in exactly this way. You can download the code from http://www.mcp.com/info.

Linking Resource Files to Your Executable

After you compile the resource script file, you must bind the compiled resource file to the program's executable file. You do this with the $R compiler directive. For example, to bind the binary resources contained in myfile.res, you place this line near the top of your main form's unit:


{$R myfile.res}

That's all there is to it. As long as the specified file exists, Delphi will bind the compiled resources to the executable file at link time.

A Sample Program Using Resources

Listing 8.2 contains the main form unit for a program called Jumping Jack. This program shows a simple animation with sound effects. The main form contains just two buttons, an Image component and a Label component. The Jumping Jack program illustrates several aspects of using resources in a Delphi application. Specifically, it shows how to load a bitmap stored as a resource, how to load and display a string resource, and how to play wave audio contained as a resource. Listing 8.3 is a partial listing of the resource file that is used by the Jumping Jack program. Examine the listings, and then I'll discuss what the program does.


TIP: Download this project from http://www.mcp.com/info to examine the resource file and the project group.

LISTING 8.2. JJMain.pas.

unit JmpJackU;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls, MMSystem;
{$R JJRES.RES}
const
  IDS_UP = 101;
  IDS_DOWN = 102;
type
  TMainForm = class(TForm)
    Image: TImage;
    Label1: TLabel;
    Start: TButton;
    Stop: TButton;
    procedure FormCreate(Sender: TObject);
    procedure StartClick(Sender: TObject);
    procedure StopClick(Sender: TObject);
  private
    { Private declarations }
    Done : Boolean;
    procedure DrawImage(var Name: string);
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  Image.Picture.Bitmap.
    LoadFromResourceName(HInstance, `ID_BITMAP1');
end;
procedure TMainForm.StartClick(Sender: TObject);
var
  S       : string;
  ResName : string;
  I       : Integer;
  Buff    : array [0..9] of Char;
begin
  { When the Start button is clicked the animation }
  { loop starts. The bitmap resources are named    }
  { ID_BITMAP1 through ID_BITMAP5 so we'll start   }
  { with a string called `ID_BITMAP' and append    }
  { the last digit when needed. }
  S := `ID_BITMAP';
  { A flag to let us know when we're done. }
  Done := False;
  { Start the loop and keep looping until the }
  { `Stop' button is pressed. }
  while not Done do begin
    { Loop through the five bitmaps starting with }
    { 1 and ending with 5. }
    for I := 1 to 5 do begin
      { Append the value of `I' to the end of the string }
      { to build a string containing the resource name.  }
      ResName := S + IntToStr(I);
      { Call a method to display the bitmap. }
      DrawImage(ResName);
    end;
    { Load the `Up' string resource using the WinAPI }
    { function LoadString, display the string, and   }
    { tell Windows to repaint the Label. }
    LoadString(HInstance, IDS_UP, Buff, SizeOf(Buff));
    Label1.Caption := Buff;
    Label1.Refresh;
    { Play the `up' sound using the WinAPI function }
    { PlaySound. Play it asynchronously. }
    PlaySound(`ID_WAVEUP',
      HInstance, SND_ASYNC or SND_RESOURCE);
    { Pause for a moment at the top of the jump. }
    Sleep(200);
    { Repeat all of the above except in reverse. }
    for I := 5 downto 1 do begin
      ResName := S + IntToStr(I);
      DrawImage(ResName);
    end;
    PlaySound(`ID_WAVEDOWN',
      HInstance, SND_ASYNC or SND_RESOURCE);
    LoadString(HInstance, IDS_DOWN, Buff, SizeOf(Buff));
    Label1.Caption := Buff;
    Label1.Refresh;
    Sleep(200);
  end;
end;
procedure TMainForm.StopClick(Sender: TObject);
begin
  { Stop button pressed, so tell the loop to }
  { stop executing. }
  Done := True;
end;
procedure TMainForm.DrawImage(var Name : string);
begin
  { Load the bitmap from a resource }
  { using the name passed to us. }
  Image.Picture.Bitmap.
    LoadFromResourceName(HInstance, name);
  { Must pump the message loop so that Windows }
  { gets a chance to display the bitmap. }
  Application.ProcessMessages;
  { Take a short nap so the animation doesn't
  { go too fast. }
  Sleep(20);
end;
end.

LISTING 8.3. JJRes.rc.

STRINGTABLE
BEGIN
 101, "Up"
 102, "Down"
END
ID_WAVEUP   WAVE "up.wav"
ID_WAVEDOWN WAVE "down.wav"
ID_BITMAP1 BITMAP LOADONCALL MOVEABLE DISCARDABLE IMPURE
BEGIN 
 `42 4D 76 02 00 00 00 00 00 00 76 00 00 00 28 00'
 `00 00 20 00 00 00 20 00 00 00 01 00 04 00 00 00'
 `00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00'
 remainder of bitmap resources follow

New Term: The main form's class declaration declares a Boolean data field called Done that is used to determine when to stop the animation. The DrawImage method is used to display the bitmap in the Image component.

In Listing 8.2, notice that two Windows API functions are used to load the string and wave file resources. In the StartClick method, the LoadString function loads a string resource into a text buffer based on the numerical identifier of the string (refer to Listing 8.3 to see how the string resources are created). The string is then assigned to the Caption property of the Label component on the form.

The PlaySound function is used to play a wave file contained as a resource. The SND_ASYNC flag used with the PlaySound function tells Windows to play the sound and immediately return control to the program (playing sounds is discussed in detail on Day 12, "Graphics and Multimedia Programming"). This enables the animation to continue while the sound is being played. The SND_RESOURCE flag tells Windows that the sound is contained as a resource and not as a file on disk. Both the LoadString and PlaySound functions use the HInstance global variable to tell Windows to look in the executable file for the resources. The bitmap resources are loaded using the VCL method LoadFromResourceName.

The first five lines of Listing 8.3 illustrate how a string table looks in a resource script file. Creating string tables is very easy with any text editor. Following that, a WAVE resource is created for each of the two wave files, which were previously recorded and reside in the project's directory. When the resource compiler sees the WAVE declaration, it reads the individual sound files and compiles them into the binary resource file.


NOTE: As you can see from Listing 8.3, you can create some types of resources easily with a text editor. If you have bitmaps and wave audio stored as external files, you can include them in an .RC file as illustrated in Listing 8.3 and have them compiled into the binary resource file using the resource compiler. Later, the binary resource file can be bound to your application's executable file.

Listing 8.3 is a partial listing. Bitmaps created with a traditional resource editor are often contained in the resource file as numerical data. The resource descriptions for bitmaps can get very long. The rest of the bitmap resource descriptions for the Jumping Jack bitmaps require about 200 lines of resource code, so I decided not to list them all. Figure 8.14 shows Jumping Jack in mid-stride.

FIGURE 8.14. Jumping Jack in action.

Creating additional resources for your programs is not rocket science, but it is not exactly trivial, either. It takes some time to realize how it all fits together. You might never need to add additional resources to your applications. If you do, though, it's good to have an idea where to begin. If this section left you a little dazed and confused, don't worry. Over time, it all starts to make sense.


NOTE: Bitmaps, icons, and cursors found in other programs are usually copyrighted material. Don't use resources from any copyrighted program without permission. Further, assume all programs are copyrighted unless they are specifically said to be freeware. You are free to use the bitmaps, icons, and cursors that are provided with Delphi (in the Common Files\Borland Shared Files\Images directory) in your applications without permission from Borland.

Using Packages

After your application is written you can deploy it in one of two ways. (Deploying means the act of distributing your application to your users.) You might be distributing your application to the general public or possibly to users within your company. Either way, you need to know what options are available to you. Essentially you have two choices: static linking or dynamic linking using packages. In this section, I'll discuss those options so that you can make an informed choice on how to deploy your application. I'll start with a discussion of packages and then I'll discuss deployment options.

What's a Package?

Before I discuss the options available to you, it is a good idea to define what a package is.

A package is a piece of compiled code that resides in a file with an extension of BPL.

That explanation probably doesn't tell you enough, so let me explain further. When you take off the wrappings, a package is, essentially, just a DLL with an extension of .bpl rather than the traditional .dll extension. (There's a bit more to it than that, but that description is close enough for the purposes here.) There are two types of packages in Delphi: runtime packages and design packages. I'll go over each of these two types next so that you can get an understanding of how packages work.

Runtime Packages

A runtime package contains code your application needs to run. Although Delphi provides many different packages, the primary package is called VCL40.BPL. This package contains all the base VCL code in one package. If you choose to use packages in your application, your application will load a package called VCL40.BPL and call routines from that package as needed. If your application is a database application, it will also load VCLDB40.BPL and call routines from that package as needed. There are other Delphi packages in addition to the two mentioned here.

In addition to the VCL packages, your application might require other packages. This will be the case if you are using any third-party components or any components that you write yourself. You will have to check the documentation for any third-party components to find out which packages your application requires to run. I'm getting ahead of myself a little, so let me tell you about design packages and then I'll get back to deploying applications that use packages.

Design Packages

To explain design packages it might be a good idea to first give you a short tutorial on component design. Most components created for Delphi include a runtime package and a design package. The runtime package contains all the code needed for a component to operate. The design package contains the code needed for the component to operate on a form at design time, including property editors and component editors.

The design package has a Requires list that tells Delphi which packages it requires to operate. The design package almost always requires code from the runtime package and probably code from one or more VCL packages as well. It's important to understand that one package (both runtime and design) can contain the code for several components. It's not necessary to have a separate package for each component.

Because design packages contain just the code needed to display components at design time, they are usually much smaller than their runtime counterparts. Design packages are used by Delphi only at design time--they are not needed for your application to operate.

Static Linking Versus Dynamic Linking

Now that you know a little about packages, you can learn about static linking versus dynamic linking.

Static Linking

When an application uses static linking of the VCL, it doesn't use packages at all. Any code your application requires to run is linked directly into your application's executable file. Your application is a standalone program and doesn't require any supporting files (packages or DLLs).


NOTE: There are exceptions to every rule. The statement that a statically linked application doesn't require any supporting DLLs makes a couple of assumptions. The first assumption is that the application is not a database application. A Delphi database application needs the Borland Database Engine (BDE) to operate. The BDE is primarily a collection of DLLs, so a database application will require DLLs to operate, even if the application is statically linked. The second assumption is that the application doesn't use any ActiveX controls. ActiveX controls are actually a form of DLL, so if your application uses ActiveX controls, it is no longer a standalone application.

Although Delphi gives you a choice of linking options, static linking is the default. Static linking has two primary advantages over dynamic linking:

Static linking has one major drawback but it only shows up in applications that use many user-defined DLLs. The drawback is that the VCL and RTL code is duplicated in every module (the main application itself) and in every DLL. This means that code is duplicated unnecessarily.

For example, let's say that every module requires a minimum of 200KB of VCL base code and RTL code. Now let's say that you have a main application and 10 supporting DLLs. That means that 2200KB of code is used (11 modules ¥ 200KB each) when only 200KB is actually required. The application and DLLs are all statically linked and can't share the VCL and RTL code among themselves.

Dynamic Linking

Dynamic linking refers to a scenario in which an application dynamically loads its library code at runtime. In the case of a Delphi application, this means that any required packages are loaded at runtime. Required packages will certainly include one or more VCL packages and might require third-party packages as well.


NOTE: The loading of packages by your application is automatic. You don't have to write code to load the packages. Delphi takes care of that for you. Choosing dynamic linking over static linking doesn't require any changes to your code. It does require changes to the way you deploy your application, which I tell you about shortly.

Dynamic linking has one primary advantage over static linking: Several modules can share code from a central location (the packages). Remember earlier when I gave an example of an application and 10 supporting DLLs? Using dynamic linking, the application and all DLLs can all share code from the VCL packages. Each module will be at least 200KB smaller because all the base code is contained in the runtime DLLs. This is an advantage when your overall product contains several applications or many DLLs.

Dynamic linking comes with a couple of problems. The first problem is that the packages and DLLs that you need to ship with your application can be quite large. The primary VCL package, VCL40.BPL, is 1.8MB alone. Your application might require other packages besides the base VCL package. This means that your application will require a minimum of 1.8MB of DLLs to run.

A second problem with dynamic linking is more subtle and more troublesome. The problem could be summed up in one word: versioning. To explain the problem, let me give you a possible scenario. Let's say you create an application using Delphi 4.02 (assuming a couple of revisions to Delphi) and that you use dynamic linking, which requires you to ship the VCL packages and RTL DLL. Your customer installs your application on his or her machine and everything works fine.

Meanwhile, I build an application using Delphi 4.0 (I'm too cheap to pay the shipping for the update) and I also use dynamic linking. Your customer buys my application and installs it. My installation program is homemade and doesn't play by the rules, so I overwrite the packages and DLLs that your application installed. Suddenly your application quits working because my packages are older than yours and the two are not compatible. Do you see the problem?

In reality, commercial software companies such as Inprise prevent this problem by naming their packages and DLLs by different names for each release of a product and by embedding version information in the packages and DLLs. (A good installation program will check the version number and only install a package if the package is newer than any existing packages on the user's system.) But packages from Borland aren't the real problem.

The real problem comes when using components from companies that are less careful about how they do business. If you buy a component package from Billy Bob's Software Company, you are trusting that Billy Bob knows what he is doing when it comes to creating packages. That might or might not be a good assumption. Let's face it, with the boom of the Internet, components are available from a wide variety of sources. You don't know what you are getting in a lot of cases, so be careful when purchasing inexpensive or freeware components.

So Which Is Better?

I can hear you thinking, "So should I use static linking or dynamic linking?" The answer to that question depends on the type of applications you write. In general, if you are writing a single small or medium-sized application, you should use static linking. If you are writing very large applications or applications with a large number of DLLs, you should probably use dynamic linking.

A simple case study might help put this in perspective. On Day 6 you created the ScratchPad program. That program compiles to around 365KB (give or take a few KB) when static linking is used. If you link ScratchPad using packages, you can get the EXE size down to around 21KB, but you have to ship 1.8MB of packages. As you can see, dynamic linking is not a good choice in this case.

Using Runtime Packages in Your Applications

If you choose to use dynamic linking, you need to change only one setting in the project options. Here's what you need to do:

1. Choose Project | Options from the main menu to bring up the Project Options dialog box.
2. Click the Packages tab and check the Build with runtime packages option near the bottom of the page (you can ignore the top of the page that deals with design packages).

3. Click OK to close the Project Options dialog box.

4. Rebuild the project.

That's all there is to it. Remember, using dynamic linking doesn't require any changes to your code.

Deploying Applications Using Packages

Deploying an application that uses dynamic linking requires you to know which packages your application uses. If you followed the steps in the previous section, you can be assured that you need VCL40.BPL at a minimum. You might need other VCL packages as well depending on the components your application uses.

To find out for sure, you have to run a utility such as TDUMP.EXE and examine the imports that your EXE references. TDUMP can be found in your \Delphi 4\Bin directory. To run TDUMP, just open a command-prompt box and switch to the directory where your application resides. Then type the following at the command line (assuming that \Delphi 4\Bin is on your path, you don't have to type the path to TDUMP):

tdump myproject.exe

Get ready on the Pause button because TDUMP will start spitting out information right away. Somewhere along the line you will see some lines like this:

Imports from Vcl40.bpl
    System::initialization() __fastcall
    System::Finalization() __fastcall
    System::RegisterModule(System::TLibModule*) __fastcall

This might be repeated several times. You will have to watch for any files with a .BPL extension and make note of their filenames. When you are done, you will have a list of packages that you must ship with your application.


NOTE: The output from TDUMP can be redirected to a text file for easier viewing. For example:

tdump myproject.exe > dump.txt



Now you can open DUMP.TXT in the Delphi Code Editor and view the contents.




NOTE: You can save yourself a lot of time and trouble by getting a good installation program. InstallShield Express comes with Delphi Professional and Client/Server versions, so you might already have an installation program that you can use. I also like Wise Install from Great Lakes Business Solutions. The better installation programs figure out which packages your application requires and automatically includes them in the installation. I don't recommend writing your own installation program under any circumstances. There are just too many issues that you can fail to take into account when writing an installation program.

Most of the time you probably won't use runtime packages in your applications. On the other hand, sometimes packages are just what you need.

Summary

The Object Repository is a great tool for reusing previously created forms, dialog boxes, projects, and other objects. The capability to add your own objects to the Repository is a huge benefit to you.

The Dialog Wizard and Application Wizard take it a step further and guide you through the creation process. The Application Wizard, in particular, is a very useful tool. In the middle of the chapter you learned how to add data fields and methods to the classes that Delphi generates.

Toward the end of the chapter I touched on the different types of resources that you might need to incorporate into your applications and how to add them to your Delphi projects. At the end of the chapter I talked about packages. Packages give you flexibility in deciding how to deploy your applications and also make installing custom components easier.

Workshop

The Workshop contains quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you have learned. You can find the answers to the quiz questions in Appendix A, "Answers to the Quiz Questions."

Q&A

Q When would I use the Use option of the Object Repository?

A When you have an object stored in the Object Repository that you want to update or make other changes to.

Q Is there a limit to the number of objects that can be stored in the Object Repository?

A Technically, you can store as many objects as you like. Remember, though, that the purpose of the Object Repository is to help you quickly locate and reuse your forms, dialog boxes, and other objects. If you put too many seldom-used objects in the Object Repository, you will start to lose efficiency because it takes longer to find the specific object you are looking for. It also takes longer for the Object Repository to load and display all those objects.

Q I've got a bunch of old objects in the Object Repository that I don't use any more. How can I get rid of them?

A Choose Tools | Repository from the main menu. The Object Repository configuration dialog box is displayed. To remove an object, first select the object in the Objects list box and then click the Delete Object button. The object will be removed from the Object Repository.

Q I had an object stored in the Object Repository. Now, when I try to use that object, I get a message box that says, Unable to find both a form and a source file. What's the problem?

A You have either moved or deleted the source or form file for the object. The Object Repository keeps track of the directory where the object is stored. If you move or delete the object, the Object Repository is unable to find the object and reports an error.

Q Can I add objects to the New page of the Object Repository?

A No. The New page of the Object Repository is fixed. It cannot be deleted or modified. You'll have to place your objects on another page.

Q I added a method to my main form class. Now I can't compile. What's the problem?

A You probably added the method declaration to the Delphi-managed section of the class declaration accidentally. Be sure that the declaration for your method is in either the public or the private section of the class declaration (or the protected section if you have one).

Q I have a resource editor that enables me to decompile resources contained in other programs. This lets me "borrow" bitmaps and other resources from other programs. Is this okay?

A The short answer is, "No." You should assume all resources in other programs to be copyrighted material that cannot be freely copied. Consult a lawyer for a qualified opinion.

Q I have a lot of bitmaps and sound files that go with my application. Can I put all those resources in a file other than the program's executable file?

A Yes. You can store your resources in a dynamic link library (DLL).

Quiz

1. When do you use the Inherit option when selecting an object in the Object Repository?

2. What is the procedure for saving a project to the Object Repository?

3. What happens to inherited forms when you change the base form?
4. Where in the form's class declaration do you place user method declarations?

5. Where do you place the method definition (the method itself) when you add your own methods to Delphi code?

6. How can you determine who wrote a particular object in the Object Repository?

7. Where do you add and delete pages in the Object Repository?

8. Is it easier to create a basic application from scratch or by using the Application Wizard?

9. Which is better for small applications: static linking or dynamic linking using packages?

10. Can you create a resource script file containing a string table with a text editor?

Exercises

1. Create a new form. Add several components of your choosing to the form. Save the form to the Forms page of the Object Repository with the name BaseForm.

2. Start a new application. Choose File | New to view the Object Repository. Switch to the Forms page. Click the Inherit radio button. Choose the BaseForm object you created in exercise 1 and add it to the application. (Be sure you used the Inherit option.) Save the project and close it.

3. Open the BaseForm object you created in exercise 1. Delete all components on the form and save the form.

4. Reopen the project you created in exercise 2. Display the new form you created in that exercise. Note that the components are all gone. (Remember, you inherited this object, so all changes made to the base form were also made to the inherited form.)

5. Choose Tools | Repository from the main menu. Delete the BaseForm created earlier.

6. Create a project using the Application Wizard. Use all menu options and make the application an MDI application.

7. Add a multipage dialog box to the application you created in exercise 6. Use the Dialog Wizard.

8. Use the Object Repository to add an About box to the program you created in exercise 6.

9. Create a simple program and build it. Run Windows Explorer and examine the size of the .EXE created by Delphi. Now change the project options to use runtime packages. Rebuild the program. Check the size of the .EXE now. What is the difference in size?

10. Write "I will not borrow bitmaps from other programs." 100 times on the blackboard.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.