REKLAMA

delphi4.zip

Jak bezboleśnie przejść z Turbo Pascala na Delphi 7.0? Szukam książki!

Rozmumiem, że jesteś na poziomie programowania proceduralnego, Delphi to język programowania zorientowany obiektowo, dlatego proponuję również książkę z innej beczki, tj. "Symfonia C++" Jerzego Grębosza, chyba tom trzeci. Autor w bardzo przystępny sposób wyjaśnia zawiłości programowania. Podaje ciekawe przykłady. Pojęcia takie jak polimorfizm, hermetyzacja, dziedziczenie, klasy, obikety, metody itp. - są tym dodatkiem do Pascala, który tworzy Delphi. Polecam również książkę "Programowanie w Delphi 5.0" Andzreja Pasławskiego. Gdzie autor nie rozwodzi się nad Pascalem, tylko szybko przechodzi do Delphi i programowaniu w Windowsie. Aha, jeszcze załączam kilka kursów programowania w Delphi.


Pobierz plik - link do postu
  • delphi4.zip
    • !info!.txt
    • Delphi4
      • Book
        • apb
          • apb.htm
        • apa
          • apa.htm
        • ch15
          • ch15.htm
        • apc
          • apc.htm
        • ch09
          • ch09.htm
        • ch14
          • ch14.htm
        • ch17
          • ch17.htm
        • ch20
          • ch20.htm
        • ch21
          • ch21.htm
        • ch02
          • ch02.htm
        • ch12
          • ch12.htm
        • ch19
          • ch19.htm
        • ch01
          • ch01.htm
        • ch11
          • ch11.htm
        • index.html
        • ch10
          • ch10.htm
        • fm
          • fm.htm
        • ch16
          • ch16.htm
        • ch18
          • ch18.htm
        • button
          • previous.gif
          • contents.gif
          • next.gif
          • sams.gif
        • ch04
          • ch04.htm
        • index.htm
        • ch13
          • ch13.htm
        • ch07
          • ch07.htm
        • copy.htm
        • ch08
          • ch08.htm
        • ch06
          • ch06.htm
        • ch03
          • ch03.htm
        • ch05
          • ch05.htm


delphi4.zip > copy.htm

Teach Yourself Borland Delphi 4 in 21 Days -- Copyright --






Teach Yourself Borland Delphi 4 in 21 Days






& #169;Copyright, Macmillan Computer Publishing. All rights
reserved.


No part of this book may be used or reproduced in any form
or by any means, or stored in a database or retrieval system without prior written
permission of the publisher except in the case of brief quotations embodied in critical
articles and reviews.
For information, address Macmillan Publishing, 201 West
103rd Street, Indianapolis, IN 46290.


This material is provided & quot;as is & quot; without any
warranty of any kind.







& #169; Copyright, Macmillan Computer Publishing. All rights reserved.


delphi4.zip > ch13.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 13 -- Beyond the Basics









Teach Yourself Borland Delphi 4 in 21 Days







- 13 -


Beyond the Basics



Creating Window Decorations

Toolbars
The CoolBar Component
The ToolBar Component
Dockable Toolbars
Status Bars

Adding Functionality with Command Enabling

Command Enabling with TActionList and TAction
Implementing Command Enabling

Printing in Delphi Applications

The Common Printing Dialog Boxes
Printing the Easy Way
Printing via QuickReport
Printing the Hard Way
Putting It to Work
Printing a Bitmap

Using Cursors

Cursor Basics
Loading and Using Stock Cursors
Loading and Using Custom Cursors

Summary
Workshop

Q & amp;A
Quiz
Exercises









Today you learn ways to turn a good Windows application into a great Windows application.
Specifically, I discuss the following:


Window decorations: toolbars and status bars

Command enabling

Printing in Delphi applications

Using cursors


This discussion continues tomorrow as you look at implementing advanced Windows
programming features in your Delphi applications.

Creating Window Decorations
No, I'm not talking about pretty lights around the front window of your house.
What I am talking about are features such as toolbars and status bars. These features
are often called window decorations . This section deals with these types of
decorations and how to implement them in your application.

Toolbars
A toolbar (also called a control bar or a speedbar) is almost standard equipment
for Windows programs today. Users expect certain amenities, and a toolbar is one
of them. A top quality toolbar should have the following features and capabilities:


Buttons representing tasks that are also available on the application's menus.

Enabling and disabling of buttons when appropriate.

Tooltips describing a button's function.

Additional hint text that is displayed in the application's status bar.

Docking capability.

Other controls such as combo boxes or drop-down menu buttons.


Some of the features in this list are optional features that not every toolbar
needs to have. With Delphi, implementing these toolbar features is easy to accomplish.
A little later in the chapter I talk about command enabling in the section & quot;Adding
Functionality with Command Enabling. & quot; I'll save discussion of command enabling
for toolbar buttons for that time.





NOTE: It is considered good practice to place on the toolbar only buttons
that have corresponding menu items. The toolbar is an alternative to using the menu.
It should not contain items found nowhere else in the program.





On Day 8, & quot;Creating Applications in Delphi, & quot; I said that the easiest
way to construct a toolbar is to use the Application Wizard. Even if you already
have a partially written application, you can still use the Application Wizard to
create a toolbar. Just generate an application with the Application Wizard, copy
the panel with the toolbar to the Clipboard, reopen your original application (don't
bother saving the Application Wizard application), and paste the toolbar into your
application from the Clipboard. Slick and easy.
However, the Application Wizard doesn't give you everything you could possibly
need in a toolbar. Most notably, the Application Wizard uses the old method of creating
a toolbar--with a panel and speed buttons. The preferred way of creating a toolbar
is to use the ToolBar and CoolBar components (found on the Win32 tab of the Component
palette). Let's take a look at those components next.





NOTE: The CoolBar and ToolBar components require version 4.72.2106.4 or
later of COMCTL32.DLL, which should have been installed as part of the Delphi installation.
If you don't have the latest version of this DLL, you can find it at Microsoft's
Web site (http://www.microsoft.com). When you deploy your applications using these
components, you should install COMCTL32.DLL version 4.70 or later. Be sure you are
using a good installation program so that you don't overwrite a new version of this
DLL when you install your application.





The CoolBar Component
The CoolBar component is an encapsulation of the Win32 cool bar (sometimes
called a rebar). This component is a specialized container control. Most of the time
the CoolBar is used as a container for toolbars, but its use isn't limited strictly
to toolbars.

Cool Bar Bands
A cool bar has bands that can be moved and resized at runtime. The bands show
a sizing grip on the left side, giving the user a visual cue that the band can be
moved or sized. Cool bar bands are represented by the TCoolBand class. A cool bar
band can contain only one component. Usually that component is a toolbar, but it
can be a combo box or any other component. Let's do an exercise to better understand
how the CoolBar component works:



1. Start with a new application and drop a CoolBar component on the form.


2. Drop a ComboBox component on the cool bar. Delphi creates a new band
to hold the combo box. Notice that the combo box fills the width of the cool bar.


3. Drop another ComboBox component on the cool bar. Another band is created
and is placed below the first band.


4. Place your mouse cursor between the sizing grip and the combo box on
the second band. The cursor will change to a pointing hand, indicating that you can
move the band. (You can also use the sizing grip to drag the band.) Drag the band
up into the band above it. As you do, the first band will shrink to make room for
the band you are dragging. Drop the band near the middle of the cool bar. You can
now use the sizing grips to resize either band.


5. Place a Panel component on the cool bar. A new band is created to hold
the panel.


6. Select the cool bar and change its AutoSize property to True.


7. Place a Memo component on the form below the cool bar. Set its Align
property to alClient.



Now your form looks like Figure 13.1.
FIGURE 13.1. The
form with a cool bar and three bands.
Now run the program. Experiment with the cool bar bands. Drag them up or down
or resize them. Notice that as you drag the bands up or down, the cool bar resizes
as needed and the memo always fills the remaining client area.
Cool bar bands are accessed through the Bands property. This property is a TCoolBands,
which is a list of TCoolBand components. If you want to hide the second band, you
can do this:

CoolBar.Bands[1].Visible := False;

You can add bands in two ways. As you have already seen, you can create a band
by dropping any component on the cool bar, but you can also use the Bands Editor.
To invoke the Bands Editor, double-click the cool bar or click the ellipsis button
next to the Bands property in the Object Inspector. You add bands by clicking the
Add button; you delete bands by clicking the Delete button. The Move Up and Move
Down buttons enable you to change the order of the bands.





NOTE: If the AutoSize property is set to True, you will have to
turn it off temporarily if you want to add new bands by dropping components on the
cool bar. Set the AutoSize property to False, make the cool bar higher, drop a component
on the cool bar, and set the AutoSize property to True again.




NOTE: When you select a band in the Bands Editor, the Object Inspector
displays the band's properties. Figure 13.2 shows the Bands Editor and the Object
Inspector when a band is selected.





FIGURE 13.2. The
cool bar Bands Editor.
The Bitmap property enables you to set a background bitmap for a band. To select
an image that will appear to the left of the band, you use ImageIndex. ImageIndex
requires the ImageList property of the cool bar to be set to a valid TImageList.
You can set a band's minimum height and width through the MinHeight and MinWidth
properties. To make a band immovable, set the FixedSize property to True.

Other CoolBar Properties
A cool bar can be either vertical or horizontal. By default the Align property
is set to alTop. To make a vertical cool bar, change the Align property to alRight
or alLeft. Some components, when placed on a cool bar, automatically orient themselves
based on whether the cool bar is vertical or horizontal. Another way to change the
orientation of the cool bar is by setting the Vertical property.
The Bitmap property enables you to set a background bitmap for the cool bar. The
bitmap you choose will be tiled to fill the cool bar's background. Note that this
sets the background bitmap for the cool bar itself, not for any individual bands
on the cool bar (as discussed in the previous section). You use the ImageList property
to set the image list that the bands will use to display an image to the left of
any band that has its ImageIndex property set.
The AutoSize property determines whether the cool bar will resize itself when
bands are moved. You saw the effect of the AutoSize property in the preceding exercise.





NOTE: Check out the TControlBar component on the Additional tab of the
Component palette. TControlBar is a native VCL component that works very much like
a cool bar. This component does not rely on COMCTL32.DLL as does TCoolBar, so it
is less susceptible to the whims of Microsoft.





The ToolBar Component
The ToolBar component encapsulates the Win32 toolbar control. The toolbar will
automatically arrange and size the controls placed on the toolbar so that they all
have a consistent height. You can use the ToolBar component with or without a cool
bar. If you have only a single toolbar, use the toolbar without a cool bar. If you
have multiple toolbars that you want to enable the user to move, place two or more
toolbars on a cool bar.
Creating a toolbar and adding buttons to it is very easy. If your toolbar buttons
will have glyphs (and most do), you have to use an ImageList component for the glyphs.
To illustrate how to build a toolbar with the ToolBar component, let's again go back
to the ScratchPad program. You'll tear it apart and put it back together.

Removing the Old Toolbar
If you recall, the toolbar you created for ScratchPad originally was just a placeholder.
The first thing you need to do is get rid of the old toolbar by performing these
steps:



1. Click the Memo component and change its Align property to alNone. Drag
down the top of the Memo to make room for the new toolbar.


2. Click the toolbar component and delete it.



Adding a New Toolbar
Now you can start adding components back again. The first thing to do is add a
cool bar and a toolbar. You don't really need a cool bar at this stage because you
have only one toolbar, but you might want to add another toolbar later, so it's best
to plan ahead. Perform these steps:



1. Drop a CoolBar component on the form. It automatically aligns itself
to the top of the form. Change its Name property to CoolBar.


2. Drop a ToolBar component on the cool bar. Change its Name property
to MainToolBar.


3. Double-click the EdgeBorders property in the Object Inspector to show
all the edge border items. Change the ebTop style to False (all EdgeBorders styles
should now be False).


4. Change the Flat property to True. This gives the toolbar buttons a
flat appearance until the cursor passes over them.


5. Click the cool bar and change the AutoSize property to True. The cool
bar sizes itself to the size of the toolbar.


6. Click the Memo component and change its Align property back to alClient.



Adding Buttons to the Toolbar
Now you begin adding buttons to the toolbar; you will add several buttons and
a few spacers. At first the buttons won't have glyphs on them, but you'll take care
of that later. For now, follow these steps:



1. Right-click the toolbar and choose New Button. A button is placed on
the toolbar. Change the button's Name property to FileNewBtn. Set the Hint property
to New|Create A New File and the ShowHint property to True. (Remember when you wrote
the hint code on Day 8? That code is still in the program, so the new hints will
work immediately.)


2. Right-click on the toolbar and again choose New Button. A second button
is placed on the toolbar to the right of the first button. Change its Name property
to FileOpenBtn. Set the Hint property to Open|Open An Existing File and the ShowHint
property to True.


3. Add another button. Change this button's Name property to FileSaveBtn.
Set the Hint property to Save|Save A File and the ShowHint property to True.








TIP: Buttons and spacers added to the toolbar always appear to the right
of the toolbar's last control. You can't insert a button at a specific location in
the toolbar, but after a button or spacer is added, you can drag it to a different
location on the toolbar. The existing buttons will make room for the new button.






That finishes the first set of buttons (except for the glyphs). You are about
to add a second set of buttons, but before you do, there needs to be a little separation
between the first set of buttons and the second. Back to work:



1. Right-click on the toolbar again, but this time choose New Separator.
A separator is added to the toolbar.


2. Add another button. This time change the Name property to EditCutBtn
and the Hint property to Cut|Cut To Clipboard.


3. Add buttons for Copy and Paste. Change their Name and Hint properties
as appropriate.


4. Add another separator.


5. Add a button called HelpAboutBtn. Change its Hint property to About|About
ScratchPad.


6. Select the Cut, Copy, Paste, and Help buttons (use Shift+-Click to
select each button). Change the ShowHint property to True. It will be changed for
all buttons selected.



Your form now looks like the one shown in Figure 13.3.


Figure 13.3. The
ScratchPad main form after adding a toolbar.



Making the Toolbar Buttons Functional
You now have a good start on the toolbar, but the toolbar buttons don't do anything
because you haven't assigned any event handlers to their OnClick events. Let's do
that next.



1. Click the FileNewBtn (the first button) and select the Events page
in the Object Inspector. Click the drop-down arrow next to the OnClick event and
choose FileNewClick. The button is now hooked up to the FileNewClick event handler.


2. Repeat step 1 for each remaining button, being careful to select the
appropriate OnClick handler for each button (FileOpenClick, FileSaveClick, EditCutClick,
and so on).


3. If you haven't yet created an About Box for ScratchPad, create one.
When you are done, create an event handler for the Help|About menu item. Hook the
OnClick event of the HelpAboutBtn to the event handler for the Help|About menu item.



Adding Bitmaps to the Toolbar Buttons
Obviously this toolbar is missing something. You need to add glyphs to the toolbar
buttons. To do so, you must add an ImageList component to the form by following these
steps:



1. Place an ImageList component on the form. (You find it on the Win32
tab of the Component palette.) Change the Name property to ImageList.


2. Right-click the ImageList component's icon on your form and choose
ImageList Editor. The ImageList Editor is displayed. (You can also double-click the
ImageList icon on your form to display the ImageList Editor.)


3. Click the Add button. Navigate to the Common Files\Borland Shared\Images\
Buttons directory. Select the FILENEW.BMP file and click Open.


A message box appears and asks whether you want to separate the bitmap into two
images. What is happening here is that the image list's Width and Height properties
are both set to 16. The bitmap you have selected is wider than 16 pixels, so it has
to be split into two images or shrunk to fit. If you recall, the button bitmaps that
come with Delphi are a single bitmap with two images. The first image is the normal
button bitmap, and the second image is for the disabled button. You will have the
ImageList Editor split the bitmap into two images, and then you will delete the second
part of the image.


4. Click Yes to have the ImageList Editor split the bitmap into two images.
The ImageList Editor now shows two images. You need only the first of these images,
so click on the second image (the disabled button image) and click the Delete button.


5. Click the Add button again. This time choose the FILEOPEN.BMP file.
Click Yes again when prompted to split the bitmap into two images. Click on the disabled
image for this bitmap and delete it. Figure 13.4 shows the image editor as it looks
just before deleting the second image.



FIGURE 13.4. The
ImageList Editor after adding three images.




6. Repeat step 5 for each remaining button (File Save, Cut, Copy, Paste,
and About). Use any bitmaps you like, but make certain you delete the extra bitmap
each time you add an image to the list. Also make sure that the images in the ImageList
editor follow the order of the buttons on the toolbar. When you are done, you will
have seven images in the image list, numbering from 0 to 6.


7. Click OK to close the ImageList Editor.








TIP: You can select multiple images in the ImageList Editor's Add Images
dialog box and add them all to the image list at one time.





Now you are ready to hook the image list to the toolbar. Click on the toolbar.
Locate the Images property in the Object Inspector and choose ImageList from the
drop-down list. If you did everything right, your buttons now have glyphs. You probably
didn't notice, but each time you added a toolbar button, Delphi automatically incremented
the ImageIndex property for the button. Because you created the buttons and images
in the same order, the glyphs on the buttons should be correct. If a button is wrong,
you can either change the button's ImageIndex property or go back to the ImageList
Editor and change the order of the images in the image list.





TIP: To rearrange the images in an image list, drag them to a new position
in the ImageList Editor.





Disabled Button Glyphs
Right now you have glyphs only for the buttons in the enabled state. You also
need glyphs that will be displayed when the buttons are disabled. You aren't disabling
the toolbar buttons yet, but you will be before the day is done. There are two ways
to implement the disabled button glyphs:


Let the toolbar create the disabled state glyphs automatically.

Create a second image list that contains disabled state bitmaps for the buttons.


Certainly the easier of these two methods is to let the toolbar create the disabled
state buttons automatically. Most of the time this is sufficient. Sometimes, however,
the algorithm for creating the disabled buttons glyphs doesn't work well. (The glyphs
end up losing definition and don't look quite right.) This happens with buttons that
don't have enough contrasting colors. In that case, you can create a second image
list that contains the disabled button glyphs. Set the DisabledImages property of
the toolbar to the image list containing the disabled glyphs, and the rest is automatic.
For ScratchPad you're going to go with the automatic disabling of toolbar buttons,
so nothing further is required.
Poor old ScratchPad is back together again. This is a good time to save the project.
After saving the project, click the Run button and give the program a workout. Click
the buttons and see whether they do what they are supposed to do. If all went well,
you have a working program again. If your program won't compile, review the steps
and try to fix the problem. If all else fails, you can refer to the ScratchPad project
of the book's code, available at http://www.mcp.com/info (Day 13).

Toolbar Tips and Hints
I covered nearly everything there is to be said about tooltips and hints in the
discussion of components on Day 7, & quot;VCL Components & quot;; again on Day 8, & quot;Creating
Applications in Delphi, & quot; when adding hint text support for the ScratchPad program;
and again today when rebuilding the toolbar.
There is one issue I didn't talk about, and that is changing the tooltip properties.
The TApplication class has four properties that control how the tooltips behave.
Table 13.1 lists these properties and their descriptions.

TABLE 13.1. TApplication PROPERTIES PERTAINING TO TOOLTIPS.



Property
Description


Hintcolor
Sets the background color of the Tooltip window. Default: CLINFOBK


HintHidePause
Controls how long to wait (in milliseconds) before hiding the tooltip if the mouse
cursor remains stationary over the component. Default: 2500 milliseconds


HintPause
Controls the interval between the time the mouse cursor is paused over a component
and the time the tooltip appears (in milliseconds). Default: 500 milliseconds


HintShortPause
Controls how long to wait before showing tooltips after they have already been shown--for
example, when the user is roaming over a group of toolbar buttons. Default: 50 milliseconds



The default values for these properties are sufficient for most applications.
Still, if you need to change any hint properties, you have that option.





HOUSE RULES: TOOLBARS AND HINTS


Don't use tooltips on controls where they will be in the way when the user wants
to read the text in the control. In particular, don't use tooltips for edit controls
and combo boxes. At a minimum, give the user the option to turn off tooltips for
those types of controls.

Keep your tooltip hint (the short hint) brief and to the point.

Make your status bar hint (the long hint) more descriptive and meaningful.

Consider giving your users the option of turning off hints altogether.








Adding Other Controls to Toolbars
Because the ToolBar component is so versatile, nothing special needs to be done
to add other types of controls to your toolbar. The most common type of control to
add to a toolbar is a combo box. You can use a toolbar combo box to select a font,
a configuration option, a zoom setting. . . the possibilities are endless.
To add a component to the toolbar, select the component from the Component palette
and drop it on the toolbar. The toolbar will take care of aligning the component.
Add spacers as necessary to separate components visually. When the component is on
the toolbar, you deal with it just as you would a component on a form. I could try
to complicate this for you, but the truth is, it's just that simple. If you've never
tried to implement a combo box on a toolbar using the Windows API, you cannot appreciate
how much work Delphi saves you. Take my word for it--it's significant.
Toolbars come in many shapes and sizes, and Delphi makes creating and implementing
toolbars very easy. With Delphi, you no longer have the excuse, & quot;That's too
hard! & quot; In fact, you might even enjoy creating toolbars with Delphi.

Dockable Toolbars
Dockable toolbars are common in many Windows programs. Dockable toolbars are a
paradox of sorts. On the one hand, they are a cool feature and most power users expect
a good application to have dockable toolbars. On the other hand, I doubt anyone actually
uses the docking capability of most dockable toolbars. Still, dockable toolbars
are easy enough to implement in Delphi, so you might as well provide them.





NOTE: The docking features discussed in this section apply to any windowed
control, not just to toolbars.





Creating a Dockable Toolbar
Making a toolbar dockable requires two steps:


Set the DragKind property to dkDock.

Set the DragMode property to dmAutomatic.


After you set these two properties, you can drag your toolbar around the screen.
Dragging the toolbar around the screen doesn't get you very much, though. In order
for dockable toolbars to make sense, you have to have a target for the drop part
of the drag-and-drop equation.

Dock Sites
A dockable toolbar needs to have a place to dock. As Roger Waters said, & quot;Any
fool knows a dog needs a home. & quot; Home for a dockable toolbar is a dock site .
A dock site is any windowed component that has its DockSite property set to True.
Components that are typically used as dock sites are TCoolBar, TControlBar, TPageScroller,
TPanel, and TPageControl. There are other controls that have a DockSite property,
but those controls are less likely to be used as dock sites.
An exercise helps illustrate the use of dock sites. Follow these steps:



1. Drop a CoolBar component on a blank form. Set its DockSite property
to True.


2. Drop a ToolBar on the CoolBar. Set the toolbar's DragKind property
to dkDock and its DragMode property to dmAutomatic. Create a few buttons on the toolbar
so that you can better see the toolbar.


3. Place a second CoolBar on the form. Change its Align property to alBottom
and its DockSite property to True.


4. Place a third CoolBar on the form. Change its Align property to alLeft
and its DockSite property to True. Size the CoolBar so that its width is 40 pixels
or so.



Now run the program. Drag the toolbar from dock site to dock site. Notice how
the toolbar changes its orientation when you drag it to the cool bar on the left
side of the form.
Let's experiment some more. Set each of the CoolBar components' AutoSize properties
to True. This will cause each CoolBar to change its size based on the controls it
contains. Now run the program again and move the toolbar to the individual dock sites.
Notice that each cool bar is nearly invisible until the toolbar is docked to the
site. Then the cool bar expands to contain the toolbar.

Floating Toolbars
A toolbar can be made into a floating toolbox by simply dragging the toolbar off
its dock site and dropping it anywhere (anywhere other than another dock site, that
is). The toolbar becomes a floating window. You can specify the type of window that
should host the floating dock site by setting the FloatingDockSiteClass property
to the name of the class you want to act as the parent to the floating toolbar. For
example, suppose that you design a form that has all the characteristics you want
for a custom floating toolbox, and that the form is called MyToolBox. In that case,
you could cause this form to be the host for a floating toolbar with this code:

ToolBar.FloatingDockSiteClass := TMyToolBox;

When the toolbar is undocked and the mouse button released, Delphi will automatically
create an instance of the TMyToolBox class and place the toolbar in the form for
that class. To dock the floating toolbox again, simply drop it on any dock site.
To make a dock site accept a floating toolbox, you must respond to the OnDockOver
and OnDockDrop events for the dock site. In the OnDockDrop event handler, call the
ManualDock method of the toolbar to cause the toolbar to dock.

Status Bars
A status bar is another feature that makes an application more marketable. Not
all applications benefit from a status bar, but many can. The VCL StatusBar component,
which encapsulates the Win32 status bar control, makes creating status bars a breeze.
First, take a quick look at the major properties of the StatusBar component, listed
in Table 13.2.

TABLE 13.2. StatusBar PROPERTIES.



Property
Description


AutoHint
Automatically displays hints in the status bar when the mouse cursor passes over
any component whose Hint property has been set.


Panels
For status bars with multiple panels. This property defines the individual panels.


SimplePanel
Determines whether the status bar shows a simple panel or multiple panels.


SimpleText
The text for the status bar's simple panel.


Property
Description


SizeGrip
Determines whether the status bar displays the sizing grip in the lower-right corner.
The sizing grip provides an area that the user can drag to size the window. The absence
of the sizing grip doesn't prevent the window from being sized, but the presence
of the size grip makes sizing a window easier.


UseSystemFont
Always uses the current system font, overriding the settings of the Font property.
This is particularly useful for users who use Plus pack themes.



As you can see from this table, a status bar can be a simple status bar or have
multiple panels. Let's discuss this choice next.

Simple or Complex?
A status bar can be either a simple status bar or a complex status bar. A simple
status bar has a single panel that occupies the entire status bar. If you want
a simple status bar, set the SimplePanel property to True. The SimplePanel property
acts as a toggle. You can switch between a simple and a complex status bar at runtime
by setting SimplePanel to True or False, accordingly.
A complex status bar is one with multiple panels. If you elect to use a
complex status bar, you can use the StatusBar Panels Editor to set up the panels
you want to see on your status bar. To invoke the StatusBar Panels Editor, double-click
on the Value column of the Panels property. To add a panel, click the Add New button
on the StatusBar Panels Editor. To delete a panel, click the Delete Selected button.
To edit a panel, select the panel and then make changes to the panel's properties
in the Object Inspector. Figure 13.5 shows the StatusBar Panels Editor and Object
Inspector when editing panels.
FIGURE 13.5. The
StatusBar Panels Editor.





NOTE: The individual panels in a complex status bar are instances of the
TStatusPanel class.





Most of the properties are self-explanatory, but a couple require further note.
The Text property contains the text that will be displayed in the panel. You can
also use the Text property at runtime to change the text in the panel. Setting the
status bar text is discussed a little later; you don't need to supply text for the
panel at design time if you are going to change the text at runtime.
You can set the Style property to either psText or psOwnerDraw. If the Style is
set to psText (the default), the panel behaves as you would expect. The text is aligned
in the panel according to the value of the Alignment property. If the Style is set
to psOwnerDraw, it is up to you to draw any text or image that is displayed in the
panel. Owner drawing of panel items is discussed later in the section & quot;Owner-Drawn
Status Bar Panels. & quot;
The Width, Bevel, and Alignment properties for the panel are straightforward.
Experiment with these properties to see how they affect your status bar's appearance.





NOTE: In the Form Designer you immediately see the results of changes made
to the status bar via the StatusBar Panels Editor. Position the StatusBar Panels
Editor so that you can view the status bar as you work with the StatusBar Panels
Editor. Each time you make a change, it will be reflected in the Form Designer.





When you are done adding panels to the status bar, close the StatusBar Panels
Editor and return to the Form Designer.





NOTE: When you modify the Panels property of a StatusBar component, the
Form Designer automatically sets the SimplePanel property to False. The assumption
is that if you are using multiple panels, you don't want to have a simple status
bar.





Changing Text in the Status Bar
There are two ways to change text in a status bar:


Manually modify the SimpleText property of the status bar (for simple status
bars) or the Text property of an individual panel (for complex status bars).

Let VCL automatically supply the status bar text by setting the AutoHint property
to True.


Manually changing the text in the status bar is simple, particularly if you have
a simple status bar. When the SimplePanel property is True, you can set the SimpleText
property to the text you want displayed in the status bar:

StatusBar.SimpleText := `This shows up in the status bar.';

In the case of complex status bars, changing the text is only slightly more complicated.
If you want to change the text for the first panel of a complex status bar, you would
use something like this:

StatusBar.Panels[0].Text := `Status Bar Text';

The Panels property of the StatusBar component has a property called Items that
is an array of panels in the status bar. Setting the Text property for an element
in the Items array changes the text for that panel (because Items is the default
array property for the Panels object, you don't have to specifically reference Items).
As you can see, the array is 0-based. The first panel in the status bar is array
element 0.
Automatic status bar hint text doesn't require much in the way of explanation.
All you have to do is set the AutoHint property to True. The rest is, as the property
name implies, automatic.





NOTE: You can still modify the status bar's text manually even when using
AutoHint. There's nothing to stop you from changing the text manually, but remember
that the text will be replaced the next time the mouse passes over a component with
hint text.





Owner-Drawn Status Bar Panels
Earlier I said that a panel's Style property can be either psText or psOwnerDraw.
When you set a panel's style to psOwnerDraw, you must take the responsibility of
drawing anything in the panel that needs to be displayed there. It is unlikely that
you are going to go to the trouble of using an owner-drawn panel just to display
text. Usually it means you are going to display some sort of icon or bitmap in the
status bar. Regardless of what is being drawn on the panel, the steps are the same:


1. Set the panel's Style property to psOwnerDraw (usually via the StatusBar
Panels Editor).

2. Respond to the OnDrawPanel event.



Obviously, the real work here is going to take place in the event handler for
the OnDrawPanel event. The declaration for the OnDrawPanel event handler looks like
this:

procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);

The StatusBar parameter is a pointer to the status bar. Usually you have a pointer
to the status bar anyway (the Name property of the StatusBar component), so this
parameter is not all that useful unless you are using multiple owner-drawn status
bars. The Panel property is a pointer to the particular panel that currently needs
drawing. You can use this parameter to determine which panel needs drawing if you
have more than one owner-drawn panel in your status bar. The Rect parameter contains
the panel's size and position. The Rect parameter is important because it tells you
the exact dimensions of the drawing area.
The OnDrawPanel event handler is called once for each panel that has its Style
property set to psOwnerDraw. If you have only one panel to draw, you don't have to
worry about much except the Rect parameter. If you have multiple panels to draw,
you must first determine which panel to draw and then do your drawing. An illustration
might help to explain this. The book's code includes a program called StatBar that
illustrates some of the things you can do with status bars. Run the program and examine
its source for tips on implementing status bars in your applications. Figure 13.6
shows the StatBar program running.
FIGURE 13.6. The
StatBar program with owner-drawn status bar panels.

As you can see, the status bar in this program has multiple panels. The middle
three panels are owner-drawn. The panels marked OVR and EXT simulate the status bar
on a word processing program or code editor. In those types of programs, the Overtype
or Extended Selection modes might be on or off. If the mode is on, the text in the
status bar panel shows in black. If the mode is off, the text has a 3D disabled-text
appearance. The third owner-drawn panel displays a stock Windows icon to illustrate
the use of a graphic on a status bar. Run the program and experiment with it to learn
how it works.
Listing 13.1 shows the OnDrawPanel event handler from the StatBar program. Examine
it and read the comments to understand what is going on in the code.

LISTING 13.1. THE StatusBarDrawPanel METHOD OF THE StatBar PROGRAM.
procedure TMainForm.StatusBarDrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
var
R : TRect;
Icon : HIcon;
begin
with StatusBar.Canvas do begin
{ Create a temporary TRect object. The Rect parameter
{ is const so we can't change it. }
R := Rect;
{ Check to see if panel 3 is the panel which needs
{ to be drawn. If so, draw an icon in the panel. }
if Panel.Index = 3 then begin
{ Load one of the stock Windows icons. This time
{ using the API is easier than using VCL. }
Icon := LoadIcon(0, IDI_HAND);
{ Draw the icon and shrink it down to 15 x 15 pixels. }
{ Center it in the panel, too. }
DrawIconEx(Handle, Rect.Left + 6, 3,
Icon, 15, 15, 0, 0, DI_NORMAL);
{ Nothing more to do. }
Exit;
end;
{ This rather lengthy if statement checks to see if
{ either the Overtype Mode or Extended Selection
{ check boxes are checked. If so, then what we need
{ to do is to draw the text twice. First, we draw it
{ in white. Then we draw it again, offset by 1 pixel,
{ in gray. The effect is a 3D disabled-text look. }
if ((Panel.Index = 1) and (OvrMode.Checked = False)) or
((Panel.Index = 2) and (ExtendedSel.Checked = False))
then begin
{ Move over and down one pixel for the offset. }
Inc(R.Left);
Inc(R.Top, 2);
{ Change the text color to white. }
Font.Color := clWhite;
{ Set the backround mode to transparent so the
{ text appears hollow and so that the white
{ text can be seen under the gray. }
Brush.Style := bsClear;
{ Draw the text using the API function DrawText. }
{ I use DrawText because it allows me to center
{ the text both horizontally and vertically within
{ the given rectangle. }
DrawText(Handle, PChar(Panel.Text), -1,
R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
{ Set the color to gray because we're going to
{ draw the text in gray in a moment. }
Font.Color := clGray;

{ Set the rect back to the original size. }

Dec(R.Left);
Dec(R.Top, 2);
end;
{ Display the text. If the item is not disabled then
{ the default color (black) is used to draw the text. }
{ If the item is disabled, then the text color has
{ been set to gray by the code above. }
DrawText(Handle, PChar(Panel.Text), -1,
R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
end;
end;

This code might seem intimidating, but most of it is comment lines. The code itself
is relatively simple. The comment lines explain what is happening at each step: The
3D appearance for the disabled text is accomplished by drawing the text once in white
and then drawing it again in gray with a slight offset. The result is that the text
looks recessed. The icon is displayed using the Windows API functions LoadIcon and
DrawIconEx.
Owner drawing of status bar panels is daunting at first, but you'll soon find
out that it's not all that bad. You might write Windows applications for a long time
and never need owner-drawn panels in your status bar. If you ever need them, however,
you'll know that's not impossible to accomplish.

Adding Functionality with Command Enabling
Command enabling is the process of enabling or disabling buttons depending
on current conditions. For example, there's not much point of having the Cut or Copy
button or menu item enabled for a text editor when no text is currently selected.
Likewise, if there is no text in the Clipboard, the Paste button should be disabled.
Command enabling isn't difficult, especially with Delphi's new TActionList component.
Still, it takes time to get right. It takes time because you have to pay attention
to detail. (Sometimes it is attention to detail that separates the great applications
from the mediocre applications.)

Command Enabling with TActionList and TAction
The TAction class provides a convenient way of performing command enabling. TActionList,
the nonvisual component that manages actions, is found on the Additional tab of the
Component palette. TActionList, as its name implies, contains a list of TAction objects.
You create an action and then assign that action to any controls that need to be
enabled or disabled based on that action. By controls I mean menu items, toolbar
buttons, context menu items, and so on.
Let's take the Edit|Cut menu item, for example. You could have at least three
objects associated with this particular task:


A main menu item

A toolbar button

A pop-up menu item


You create actions with the ActionList Editor. Given the Edit|Cut example, you
would create an action for Cut called, say, CutAction. Then, using the Object Inspector,
you assign CutAction to the Action property of each of the objects that correspond
to the cut operation (toolbar buttons and menus, for example). At runtime, when you
need to enable the Cut option, you can do so with just one line of code:

CutAction.Enabled := True;

This will enable all components with their Action properties set to CutAction.
Disabling the Cut items is as simple as assigning False to the Enabled property of
the action. The OnUpdate event of TAction and TActionList provides a convenient place
to put your command-enabling code.
Command enabling with TAction is something that you have to experience to fully
appreciate. You add command enabling to ScratchPad in the next section.

Implementing Command Enabling
In this section you implement command enabling for the ScratchPad program. First
you set up the ActionList component, and then you hook up the various components
to the action list.

Creating an ActionList
It all starts with the ActionList component, which is the heart of the command-enabling
system in VCL. First you add the actions for the Edit menu items. After that, you
add an action for the File menu's Save and Save As menu items. The following steps
will take you through the ActionList setup process.
Creating the Edit Menu Actions
The following steps show you how to create actions for the Edit menu's Cut, Copy,
and Paste items. Perform these steps:



1. Place an ActionList component on the form and change the Name property
to ActionList.


2. Double-click the ActionList icon to invoke the ActionList Editor.


3. Right-click on the ActionList Editor and choose New Standard Action
from the context menu. Choose the TEditCopy action and click OK. Notice that the
Object Inspector changes to show the properties of the TEditCopy action class.


I want to pause for a moment here and explain more about how actions work. Examine
the Object Inspector at this point. Notice that the TEditCopy action has several
familiar properties and that those properties all have values. In particular, notice
that the Caption, Hint, ImageIndex, and ShortCut properties already have values that
correspond to an Edit menu's Copy item. These properties will be transferred to any
control that you assign this action to, which means that you must set any properties
of the action to the values that you want the corresponding components to acquire.
That won't make much sense until you attach the action to components in the next
section, but it will be clear at that time. Let's continue with the action creation
process.


4. Change the new action's Name property to CopyAction, the Hint property
to Copy|Copy to Clipboard, and the ImageIndex property to 3.


5. Create another standard action, this time a TEditCut action. Change
the Name to CutAction, the Hint property to Cut|Cut to Clipboard, and the ImageIndex
property to 4.


6. Create a third action, a TEditPaste action. Change the Name property
to PasteAction, the Hint property to Paste|Paste from Clipboard, and the ImageIndex
property to 5.



You have now created the actions for the primary Edit menu items.
Creating the File Menu Actions
Next you need to create the actions for the File menu's Save and Save As menu
items. Perform these steps:



1. Right-click on the ActionList Editor and this time choose New Action.
A new TAction is created and is shown in the Object Inspector.


2. Change the Name property to SaveAction, the Caption property to & amp;Save...,
the Category property to File, the Hint property to Save|Save a File, the ImageIndex
property to 2, and the ShortCut property to Ctrl+S.


3. Create another new action. Change the Name property to SaveAsAction,
the Caption property to Save & amp;As..., and the Category property to File. You don't
need to set the Hint or ImageIndex properties because this action doesn't have an
associated button on the toolbar. Figure 13.7 shows the ActionList Editor at this
point.

FIGURE 13.7. The
ActionList Editor after creating the action items.


4. Close the ActionList Editor.








NOTE: You can create categories of actions if you have several dozen actions
to keep track of. To create an action category, simply set the Category property
of one or more actions to any text you want. For example, to create a category for
the edit actions created earlier, set each action's Category property to Edit. Action
categories are simply for organization and don't have any bearing on the way actions
operate.





Attaching Actions to Components
The next step is to attach the actions you just created to the various menu items
and toolbar buttons to which those actions correspond. Perform these steps:



1. Double-click on the MainMenu component to start the Menu Editor.


2. Select the File|Save menu item and change its Action property to SaveAction.


3. Select the File|Save As menu item and change its Action property to
SaveAsAction.


4. Move to the Edit menu and select the Cut menu item. Change the Action
property to CutAction.


5. Repeat step 4 for the Copy and Paste items on the Edit menu using CopyAction
and PasteAction for the Action property, respectively. Close the Menu Editor.


6. In the Form Designer, click on the File Save button on the toolbar.
Change the Action property of the button to FileSaveAction.


7. Repeat step 6 for the Cut, Copy, and Paste buttons on the toolbar,
setting the Action property to CutAction, CopyAction, and PasteAction, respectively.


8. Change the Action property of the MemoPopup menu items as needed.



You probably didn't notice, but when you assigned SaveAction to the Action property
of a component, that component's Caption, Checked, Enabled, HelpContext, Hint, ImageIndex,
ShortCut, and Visible properties all changed to the values of those properties in
the SaveAction object. It is important to understand that the action's properties
will overwrite the properties of any component that the action is assigned to. You
must set up the action with that in mind. That is why I had you change the Hint and
ItemIndex properties when you created the actions. Had you not done that, the hint
text and toolbar button glyphs would not be correct.
Now each of the components just listed is hooked to an action. When a particular
action changes, any components hooked to that action will change as well. Take the
following code, for example:

SaveAction.Enabled := False;

When this code executes, any components with their Action property set to SaveAction
will be disabled (the main menu Save item and the Save button on the toolbar). To
enable all items associated with this action, use this code:

SaveAction.Enabled := True;

It's as simple as that. Because the main menu and toolbar Save components have
their Action property set to SaveAction, the following two code snippets are equivalent:

{ One-shot using the Action. }
SaveAction.Enabled := False;
{ The hard way. }
FileSave.Enabled := False;
FileSaveBtn.Enabled := False;

Granted you save only one line of code in this example, but if you have several
components that need to be enabled or disabled, actions can save you a lot of time.
After you create an action and associate that action with one or more components,
command enabling is as simple as one line of code. The beauty of this system is that
it doesn't matter how many components you need to enable or disable. It's still just
one line of code.
Run the ScratchPad program. Notice that the Cut and Copy buttons are disabled.
Type some text in the memo and highlight it. The Cut and Copy buttons on the toolbar
are magically enabled. Click anywhere in the memo to deselect the selected text.
The Cut and Copy buttons are disabled again. Is the Paste button enabled? If so,
type Alt+Print Screen on the keyboard. (This copies the current window to the Clipboard
as a bitmap.) When you press Alt+Print Screen, the Paste button should become disabled
because you can't paste a bitmap into a memo. Select some text and click either the
Cut or the Copy button. The Paste button is now enabled because the Clipboard contains
text you can paste into a memo.
How does it work? The TEditCopy, TEditCut, and TEditPaste standard actions automatically
know how to enable and disable their associated components when any type of edit
control has input focus. It's not magic, but it's the next best thing to it! You
only have to create the standard actions and the rest is automatic. You didn't have
to write any code to get the Edit menu command enablers working. You can't beat that!

Command Enabling for the Save and Save As Items
The Edit menu items were easy because you didn't have to write any code. The File
menu items, Save and Save As, will take a little more work because there are no standard
actions for these menu items. Not to worry, though--adding command enabling for those
menu items doesn't take much time. To implement command enabling for these menu items,
you will make use of the OnUpdate event. But first you need a little background information
to put the OnUpdate event in perspective.
The OnUpdate event provides a convenient place to put your command-enabling code.
When your application runs out of messages to process, Windows sends it a WM_ENTERIDLE
message. Windows, in effect, tells your program, & quot;I don't have anything for
you to do right now, so relax and take it easy for a while. & quot; When a Delphi application
receives a WM_ENTERIDLE message, it triggers a TAction's OnUpdate event. All you
have to do is create an event handler for the OnUpdate event and do your command
enabling there. You can use the event handler to check the state of the memo component
and enable the Save and Save As items accordingly.
The only step remaining, then, is to create an event handler for the action's
OnUpdate event. Perform these steps:



1. Double-click the ActionList component to start the ActionList Editor.


2. Select the SaveAction action from the list of available actions. Click
on the File action category or (All Actions) if you don't see the action in the action
list.


3. In the Object Inspector, double-click the Value column next to the
OnUpdate event. The Code Editor displays the OnUpdate event handler. You add code
to the event handler in just a bit.


4. Locate the ActionList Editor (use View|Window List if you can't find
the ActionList Editor window). Select SaveAsAction from the list of actions.


5. In the Object Inspector, click the drop-down arrow next to the OnUpdate
event. Choose SaveActionUpdate from the list. This enables the Save and Save As items
to use the same OnUpdate event handler.


6. Close the ActionList Editor.



Creating the event handler is the simple part, of course. The more difficult aspect
is writing the code that goes between the begin and end statements. Listing 13.2
shows the completed OnUpdate event handler. Switch to the Code Editor and enter the
code shown in Listing 13.2 into your OnUpdate event handler.

LISTING 13.2. THE ScratchPad OnUpdate EVENT HANDLER.
procedure TMainForm.SaveActionUpdate(Sender: TObject);
begin
{ Command enabler for Save and Save As. }
SaveAction.Enabled :=
Memo.Modified and (Length(Memo.Lines.Text) & gt; 0);
SaveAsAction.Enabled := SaveAction.Enabled;

{ The following two command enablers don't use actions. }

{ Instead the Enabled property of the two menu items }
{ is accessed directly. }
{ Command enabler for Select All. }
EditSelectAll.Enabled := Memo.Lines.Count & gt; 0;
{ Command enabler for Undo. }
EditUndo.Enabled := Memo.Modified;
end;

The SaveAction's Enabled property is set based on whether or not the memo has
been modified and whether or not the memo contains text. In a nutshell, the Save
action is enabled if the memo has been modified since it was loaded and if it contains
text. The same value is assigned to the Enabled property of the SaveAsAction. This
enables both Save and Save As at the same time using the same criteria.
Notice that I slipped a couple of extra commands into the OnUpdate event handler.
The command enablers for the Select All and Undo items of the Edit menu do not use
actions. Instead, the Enabled property of the menu items is set directly. Using actions
for these two items is overkill because there is only one line of code in each case.
As long as you have an OnUpdate event handler, you can use it for any type of command
enabling you want. Put another way, this OnUpdate event handler is not exclusively
for the File menu items Save and Save As. Any command enabling can be done in the
OnUpdate event.





NOTE: The OnUpdate event handler might be called thousands of times per
second. For that reason, you must keep code in this method as short as possible.







NOTE: Debugging code in the OnUpdate event handler is tricky. The problem
is that any breakpoints in the OnUpdate event handler will be hit as soon as you
run the program. You will have to use debugging methods other than straight breakpoints
when debugging code in the OnUpdate event handler. Two such methods are conditional
breakpoints and using OutputDebugString to send messages to the Event Log.





A TCommandList Bonus
There's an added benefit to command lists that I haven't mentioned yet. To see
this hidden (up to now) benefit, perform these steps:



1. Select the MainMenu icon on ScratchPad's main form.


2. Change the Images property to ImageList.


3. Do the same for the PopupMenu.



Now run the program and look at the File and Edit menus. Wow! Instant menu bitmaps!
The menu items inherit the ImageIndex property of their associated action. All you
have to do to enable the bitmaps is assign the same image list use for the actions
to the menu's Images property. The rest is automatic.

Printing in Delphi Applications
Printing is an everyday necessity for most Windows users. Whereas plenty of programs
do not have printing capability, the majority of Windows applications have some form
of printing support. I'll cover the basics of printing in this section.
Providing printing capabilities in a DOS application used to be a real chore.
A DOS program had to provide and install printer drivers for every type of printer
that the program supported. That put a huge burden on software developers, especially
on small companies or shareware developers. Windows changed all that. For the most
part, Windows takes on the burden of dealing with different printers, printer drivers,
and so on. All you have to do is send output to the printer just as you would send
output to a window. I'll get to that soon.
Printing in Delphi applications comes in several flavors. You'll probably be relieved
to learn that, in many cases, printing is built into VCL and comes nearly automatically.
In other cases, though, you have to do some specialized printing. Before you learn
how to go about that, let's look at the common dialog boxes that pertain to printing.
After that I'll discuss the different ways you can print from a Delphi application.

The Common Printing Dialog Boxes
Windows provides the common Print and Print Setup dialog boxes for use in your
applications. You use the Print dialog box just before printing begins and the Print
Setup dialog box to configure the printer. First, though, you must add the components
to your form.

The Print Dialog Box
As I've mentioned, the Print dialog box is displayed just before printing begins,
usually when the user chooses File|Print from the main menu. If the user clicks OK,
printing begins; if the user clicks Cancel, printing is aborted. Figure 13.8 shows
the Windows Print dialog box in its most basic form.
FIGURE 13.8. The
Windows Print dialog box.

No doubt this is not the first time you have seen this particular dialog box.
The combo box at the top of the dialog box enables you to choose the particular printer
to which you want to print. The Properties button brings up a dialog box specific
to the printer currently selected that enables you to set the orientation, resolution,
and other properties specific to that printer. The Print Range section enables the
user to print all pages, a page range, or any objects or text currently selected
in the application. The Copies section enables the user to specify the number of
copies to print as well as whether to collate the copies.
The Print dialog box is encapsulated in VCL in the PrintDialog component. As with
the other common dialog boxes, you display the Print dialog box by calling its Execute
method. It shouldn't disappoint you to learn that Windows carries out much of what
the Print dialog box does. The printer selection, number of copies, and collating
options are all handled by Windows, so you don't have to worry about them. Depending
on your application, you might need to enable the user to print a specified range
of pages or to print the current selection in the application. If you are providing
that kind of support, you need to examine some of the PrintDialog properties before
printing begins.
The PrintDialog component has the Execute method only and no events. All the functionality
of the PrintDialog component takes place through its properties, as listed in Table
13.3.

TABLE 13.3. THE PrintDialog PROPERTIES.



Property
Description


Collate
Specifies collated copies. If this is set to True, Windows will print so that the
copies are collated.


Copies
Specifies the number of copies to print. You can set this property before calling
the Print dialog box if one of your application's options is the number of copies
to print. Windows takes care of printing the correct number of copies.


FromPage
Specifies the starting page when the option of printing a range of pages is enabled.
Applications that support page-range printing should read this property to determine
which pages to print.


MaxPage
Specifies the maximum page number that can be specified in the To field when printing
a range of pages. The Print dialog box takes care of validating entry in the From
and To fields.


MinPage
Specifies the minimum page number that can be specified in the From field when printing
a range of pages.


Options
Contains a set of options that control which features of the print dialog box are
enabled. You can elect to have a help button, to display the print to file option,
or to enable the page-range or print selection options.


PrintRange
Controls which of the Print Range radio buttons is selected when the Print dialog
box is initially displayed.


PrintToFile
Indicates whether the user has chosen the Print to File option. It is up to the application
to write the output to a file.


ToPage
Specifies the ending page number when printing a range of pages. Applications that
support page-range printing should read this property to determine which pages to
print.



The application doesn't have much to do in response to the Print dialog box closing
unless the Print Range and Print to File options are enabled. For example, if your
application enables printing a range of pages, you need to read the FromPage and
ToPage properties to determine which pages to print. Other than that, you begin printing
if the user clicks OK.

The Print Setup Dialog Box
The Print Setup dialog box, shown in Figure 13.9, is used when the user wants
to change printers, page size, paper source, or orientation.
FIGURE 13.9. The
Print Setup dialog box.

The Print Setup dialog box isn't necessary in most applications because the user
can always press the Properties button on the Print dialog box to change the setup
options (refer to Figure 13.8). On the other hand, implementing the Print Setup dialog
box is so easy that you might as well include it in your applications. How easy is
it? Well, the PrinterSetup component has no properties, methods, or events specific
to it. As with the PrintDialog component, the Execute method is the only method in
which you are interested. To further simplify things, Windows handles everything
that the Print Setup dialog box does. In fact, the Execute method doesn't even return
a value. This is because Windows handles everything for you. If the user clicks Cancel,
Windows does nothing. If the user clicks OK, Windows makes the appropriate changes
in preparation for printing. All you have to do is display the Print Setup dialog
box and forget about it. A typical event handler for the File|Printer Setup menu
item would look like this:

procedure TMainForm.FilePrintSetupClick(Sender: TObject);
begin
PrinterSetupDialog.Execute;
end;

That's all there is to it. As I said, implementing the Print Setup dialog box
is so simple you might as well add it to your application.

Printing the Easy Way
Printing is an application-specific task. That might not sound profound, but it's
true. Depending on what kind of application you develop, printing can be as simple
as one line or it can entail hundreds of lines of code. Let me first discuss the
easiest forms of printing, and then I'll progress to the more difficult printing
operations.

The Print Method for Forms
The TForm class has a method called Print that can be used to print the contents
of a form. Only the client area of the form is printed; the form's frame and menu
bar are not. Although this method works well for a simple screen dump, it is limited
in its implementation. You can choose from three print options, which are controlled
through the PrintScale property. Table 13.4 lists the print scaling choices and their
descriptions.

TABLE 13.4. THE PrintScale PROPERTY OPTIONS.



Option
Description


poNone
No special scaling is applied. The printed output of the form varies from printer
to printer.


PoProportional
This option attempts to print the form in roughly the same size as it appears on
the screen.


poPrintToFit
This increases or reduces the size of the image to fit the current printer settings.



You can set the PrintScaled property at runtime or at design time. The Print method's
use is limited to simple screen dumps and isn't likely to be used for any serious
printing.

The Print Method for the RichEdit Component
The RichEdit component is powerful primarily due to the amount of work done by
the underlying Windows memo control. Printing in the RichEdit component is accomplished
via a call to the Print method. This method takes a single parameter called Caption
that is used by the Print Manager when it displays the print job. Printing the contents
of a RichEdit component is as simple as this:

RichEdit.Print(`MyApp.exe - readme.txt');

Everything is taken care of for you. Word wrapping and pagination are automatically
implemented. If you are using a multiline edit control that requires printing, the
RichEdit component is the way to go.





TIP: You can use the Windows API function ShellExecute to print a text
file. ShellExecute is used, among other things, to run a program based on a filename
extension. For example, by default Windows registers the .txt extension as belonging
to Windows Notepad. If you double-click on a file with a .txt extension in Explorer,
Windows will look up the .txt extension in the Registry, see that Notepad.exe is
registered to handle .txt files, and will run Notepad. The file you double-clicked
will be loaded automatically.


You can use this behavior to your advantage. Take this line of code, for example:

ShellExecute(Handle, `print', `readme.txt', nil, nil, SW_HIDE);







This code loads Notepad, prints the file called Readme.txt, and then exits Notepad.
In fact, you never see the Notepad program's main window because the SW_HIDE style
is specified for the Show parameter. Using this technique assumes that the user has
not modified the default registration of the .txt extension and has not deleted Notepad
from his or her system. If you use ShellExecute, you have to add ShellApi to your
unit's uses list.





Printing via QuickReport
Database programs can use QuickReport to print reports. I mention QuickReport
here because it obviously pertains to printing, but because QuickReport is one of
the database components, I will postpone a detailed discussion about its actual implementation
until Day 18, & quot;Building Database Applications. & quot;

Printing the Hard Way
Don't let the title of this section put you off. Printing isn't all that difficult;
it just takes some time and organization. First, let's look at some steps you need
to know in order to implement printing in your applications. After that you delve
into the actual code.

What's a Device Context?
I talked about device contexts and TCanvas in detail yesterday, but a recap can't
hurt. A device context (DC) is like a slate that Windows programs can draw on. A
better word would be canvas . On this canvas you can draw text, lines, bitmaps,
rectangles, ellipses, and so on. The type of line used when drawing on a device context
depends on the current pen selected into the DC. The current fill color and fill
pattern are taken from the brush that is currently selected into the device context.
Device contexts must be carefully managed. There are a limited number of DCs available
to Windows, and you have to be careful to release the device context as soon as you
are finished with it. Also, if you don't properly delete the objects you select into
the device context, your program will leak memory and perhaps even leave Windows
itself in a precarious state. As you can imagine, working with DCs can be complicated.
The good news is that VCL shields you from having to know every detail of device
contexts. VCL encapsulates Windows DCs in the TCanvas class. The Canvas property
frees you from worrying about all the little details that can drive you nuts when
dealing with Windows device contexts. VCL takes care of obtaining the DC, selecting
the appropriate objects into the DC, and releasing the DC when it is no longer needed.
All you have to do is draw on the canvas and let VCL worry about the rest.
So what does this have to do with printing? (Inquiring minds want to know.) Well,
it's like this: Windows enables you to obtain a printer device context on
which you can draw text, graphics, lines, and so on. In other words, you draw on
a printer canvas just as you draw on a screen canvas. This concept represents quite
a switch from the way printing was approached back in the good old days of DOS. In
this case, it is Windows that comes to your rescue by enabling the use of a printer
DC. VCL further aids you by encapsulating device contexts in the Canvas property.
The bottom line is that printing is easier than it's ever been.

The TPrinter Class and the Printer Function
VCL aids in printing operations by providing the TPrinter class. This class encapsulates
the whole of printing in Windows. TPrinter has a Canvas property that you can use
to output lines, text, graphics, and other drawing objects to the printer. I don't
want to make it sound too easy, but all you have to do to print in your Delphi programs
is add Printers to your uses list and then do something like the following:

Printer.BeginDoc;
Printer.Canvas.TextOut(20, 20, `Hello There!');
Printer.EndDoc;

In this code, Printer is a VCL function. This function enables you access to a
TPrinter object that is set up and ready to go. All you have to do is put it to work.
Now let's take a quick look at TPrinter's properties and methods. Table 13.5 lists
the primary TPrinter properties, and Table 13.6 shows the primary TPrinter methods.

TABLE 13.5. TPrinter PROPERTIES.



Property
Description


Aborted
This property is True if printing was started and then aborted before it was finished.


Canvas
The mechanism through which you can draw on the printer (the printer device context).


Capabilities
The current settings of a printer device driver.


Copies
The number of copies printed.


Fonts
A list of fonts supported by the current printer.


Handle
The handle to the printer device context (HDC). Use this property when you have to
call a Windows API function that requires a handle to a device context.


Orientation
The printer orientation (poPortrait or poLandsacpe). This is automatically set when
the user chooses a printer or modifies the printer setup, but you can also set it
manually.


PageHeight
The height of the current printer page in pixels. This value varies from printer
to printer. In addition, this property can contain a different value based on the
orientation of the printer. Some printers can use more than one resolution, which
also causes this value to vary.


PageNumber
The page number of the page currently printing. This property is incremented each
time you call NewPage to begin printing a new page.


PageWidth
The width of the page in pixels. As with the PageHeight property, this value varies
depending on the printer resolution, paper orientation, and paper size.


PrinterIndex
The index value of the currently selected printer in the list of available printers.
Specify -1 to select the default printer.


Printers
A list of available printers on the system.


Printing
This property is True if the printer is currently printing.


Title
The text that identifies this printing job in the print manager window.



TABLE 13.6. TPrinter METHODS.



Method
Description


Abort
Used to abort the printing before normal completion.


BeginDoc
Begins the printing process. Sets up the printer with Windows in preparation for
printing.


EndDoc
Ends the printing process. Forces the current page to be printed and performs printing
cleanup with Windows.


GetPrinter
Retrieves the current printer. Use the Printers property instead of this method.
(The Printers property is the preferred method for accessing printers because you
can use it for both retrieving and setting the current printer.)


NewPage
Used to force printing of the current page and start a new page. Increments the PageNumber
property.


SetPrinter
Sets the current printer. Use the Printers property instead of this method.



The TPrinter class has no design-time interface. Everything is accomplished at
runtime.

Putting It to Work
It's time to put your newly acquired knowledge to work. Once again it's time to
dust off the ScratchPad program and spruce it up a bit. After all, what good is a
text editor that doesn't print?
First, you need to modify the main form slightly. You already have menu items
set up for the Print and Print Setup menu items, but you need to enable them and
add the Print and Printer Setup dialog boxes to the form. Here goes:



1. Double-click the MainMenu component to bring up the Menu Designer.


2. Choose File|Print from the ScratchPad menu in the Menu Designer. Change
the Enabled property to True.


3. Do the same for the File|Print Setup menu item. Close the Menu Designer.


4. Place a PrintDialog component on the form and change its Name property
to PrintDialog.


5. Place a PrinterSetupDialog on the form and change its Name property
to PrinterSetupDialog.



Okay, now that you've completed the form, it's time to go to work modifying the
code. To start, you have to add a couple items to the main form's declaration. Perform
the following steps:



1. Switch to the Code Editor and add the Printers unit to the uses list
of the main form's unit.


2. Locate the TMainForm class declaration in the interface section. Add
this line to the private section of the class declaration:






procedure PrintFooter(var R : TRect; LineHeight : Integer);







This is the declaration for a method that prints the footer at the bottom of
each page.


3. Type Ctrl+Shift+C to have Delphi's Class Completion feature create
the PrintFooter procedure in the implementation section. You fill in the code later.


4. Switch to the Form Designer and choose File|Print from the form's main
menu. The FilePrintClick method is displayed. For now, leave the method empty.


5. Choose File|Print Setup from the main menu. Enter one line at the cursor
so that the entire FilePrintSetupClick method looks like this:






procedure TMainForm.FilePrintSetupClick(Sender: TObject);
begin
PrinterSetupDialog.Execute;
end;




Okay, now you're ready to fill in the FilePrintClick and PrintFooter methods.
Listing 13.3 shows the FilePrintClick method. You can enter the code in this method
by hand, or you can load the ScratchPad project that comes with the book's code and
examine it in the Delphi IDE. Listing 13.4 shows the PrintFooter method. Enter the
code in these methods in your SPMain.pas file. You don't have to type the comment
lines, of course.

LISTING 13.3. THE FilePrintClick METHOD.
procedure TMainForm.FilePrintClick(Sender: TObject);
var
I : Integer;
LineHeight : Integer;
LinesPerPage : Integer;
LineCount : Integer;
R : TRect;
S : string;
begin
{ Display the Print dialog. }
if PrintDialog.Execute then begin
{ Set the title for the printer object. }
Printer.Title := `ScratchPad - ` + OpenDialog.FileName;
{ Set the printer font to the same font as the memo. }
Printer.Canvas.Font := Memo.Font;
{ Determine the line height. Take the Size of the
{ font and and use the MulDiv function to calculate
{ the line height taking into account the current
{ printer resolution. Use the Abs function to get
{ the absolute value because the result could be a
{ negative number. After that add 40% for leading. }
LineHeight := Abs(
MulDiv(Printer.Canvas.Font.Size,
GetDeviceCaps(Printer.Handle, LOGPIXELSY), 72));
Inc(LineHeight, (LineHeight * 4) div 10);
{ Determine how many lines will fit on a page. Trim
{ it back by three lines to leave some bottom margin. }
LinesPerPage := (Printer.PageHeight div lineHeight) - 4;
{ Start printing on line 4 rather than line 0 to leave
{ room for the header and to allow for some top margin. }
LineCount := 4;
{ Tell Windows we're starting and print the header. }
Printer.BeginDoc;
R.Top := LineHeight;
R.Left := 20;
R.Right := Printer.PageWidth;
R.Bottom := LineHeight * 2;
DrawText(Printer.Handle,
PChar(OpenDialog.FileName), -1, R, DT_CENTER);
{ Loop through all of the lines and print each one. }
for I := 0 to Pred(Memo.Lines.Count) do begin
{ When we get to the bottom of the page reset the
{ line counter, eject the page, and start a new page. }
Inc(LineCount);
if LineCount = LinesPerPage then begin
PrintFooter(R, LineHeight);
LineCount := 4;
Printer.NewPage;
end;
{ Get the next string and print it using TextOut }
S := Memo.Lines.Strings[I];
Printer.Canvas.TextOut(0, LineCount * LineHeight, S);
end;
{ All done. }
PrintFooter(R, LineHeight);
Printer.EndDoc;
end;
end;

LISTING 13.4. THE PrintFooter METHOD.
procedure TMainForm.PrintFooter(var R: TRect; LineHeight: Integer);
var
S : String;
begin
with Printer do begin
{ Build a string to display the page number. }
S := Format(`Page %d', [PageNumber]);
{ Set up the rectangle where the footer will be drawn. }
{ Find the bottom of the page and come up a couple of
{ lines. }
R.Top := PageHeight - (lineHeight * 2);
R.Bottom := R.Top + lineHeight;
{ Display the text using DrawText so we can center the
{ text with no fuss. }
DrawText(Handle, PChar(S), -1, R, DT_CENTER);
{ Draw a line across the page just above the `Page X' text. }
Canvas.MoveTo(0, R.Top - 2);
Canvas.LineTo(R.Right, R.Top - 2);
end;
end;

This code illustrates how you can print directly through Windows rather than rely
on the built-in printing that VCL provides. Although I always opt to do something
the easy way when possible, there are times when the easy way isn't flexible enough.
In those times, it's good to have the knowledge to do the job without trouble.

Printing a Bitmap
Printing a bitmap is simple. All you need to do is create an instance of the TBitmap
class, load a bitmap into the bitmap object, and send it to the printer using the
Draw method of TCanvas. Here's the entire code:

procedure TForm1.Button1Click(Sender: TObject);
var
Bitmap : TBitmap;
begin
Bitmap := TBitmap.Create;
Bitmap.LoadFromFile(`test.bmp');
Printer.BeginDoc;
Printer.Canvas.Draw(20, 20, Bitmap);
Printer.EndDoc;
Bitmap.Free;
end;

When you print a bitmap, be aware that the bitmap might turn out very small depending
on the resolution of your printer. The bitmap might have to be stretched to look
right. If you need to stretch the bitmap, use the StretchDraw method instead of Draw.

Using Cursors
The use of cursors is not difficult, but I'll describe it here to give you a basic
understanding of how it works. This section deals with cursors that you change at
runtime. (To change a cursor at design time, select a new value for the component's
Cursor property.) After a look at cursor basics, I will discuss how to load stock
cursors and custom cursors.

Cursor Basics
First, you can change the cursor for a particular component or form or for the
entire client area of your application. If you want to change the cursor for the
entire application, you need to change the Cursor property of the Screen object.
The Screen object represents your application's screen. By changing the Cursor property
of the Screen object, you ensure that the cursor is the same regardless of which
component the cursor is over. Let's say, for example, that you want to change the
cursor to the hourglass cursor. If you change the Cursor property for the form only,
the cursor will be an hourglass when it's over the form itself but will revert back
to the default cursor when it's over any other components on the form. Changing the
Cursor for the Screen object gives you the same cursor throughout.
Cursor management is the responsibility of the Screen object. All the cursors
available for your use are contained in the Cursors property of the Screen object.
Note that the name of this property is Cursors and is not the same as the Cursor
property discussed in the previous paragraph. The Cursors property is an array that
contains a list of available cursors for the application, and the Cursor property
is the property that is used to display a particular cursor. Although this is confusing
at first, you'll catch on quickly. All it takes is a little experience working with
cursors.

Windows provides several built-in cursors for use in your applications. In addition to these built-in cursors, VCL adds a few cursors of its own. Together, these cursors are called the stock cursors . Each stock cursor has a named constant
associated with it. For example, the arrow cursor is named crArrow, the hourglass cursor is named crHourGlass, and the drag cursor is named crDrag. All these cursors are held in the Cursors array and occupy positions -17 through 0 in the array; the
default cursor is at array index 0 (crDefault), no cursor is -1 (crNone), the arrow cursor is -2 (crArrow), and so on. The online help for the Cursors property lists all the cursors and their constant names, so check Delphi help for a complete list of
the available cursors.

To use one of the cursors in the Cursors array, assign the name of the cursor
you want to use to the Cursor property of the Screen object:

Screen.Cursor := crHourGlass;

At this point the VCL magic kicks in and the correct cursor is loaded and displayed.
The use of the Cursors property is transparent to you because you don't directly
access the Cursors property when you use a cursor. Instead, you make an assignment
to the Cursor property, and VCL takes care of looking up the proper cursor in the
Cursors array and displaying it. The Cursor property of a component and the Cursors
property of the Screen object work together to display different cursors in your
application.
You can change cursors in your applications for any number of reasons--to display
the wait cursor (the hourglass), if you have a drawing program that uses special
cursors, or to implement a help cursor in your application.

Loading and Using Stock Cursors
Windows provides several built-in cursors for use in your applications. In addition
to those, VCL adds a few more cursors from which you can choose. You can use these
stock cursors anytime you like.
One of the most obvious times to change the cursor is when you have a lengthy
process your application must perform. It is considered bad practice to tie up the
program without giving the user some indication that the program is busy. Windows
provides the hourglass cursor (Windows calls it the wait cursor) for exactly this
purpose. Let's say, for example, that you have a processing loop in your application
that might take a long time. You would do something like this:

procedure TMainForm.DoItClick(Sender: TObject);
var
I, Iterations : Integer;
OldCursor : TCursor;
begin
Iterations := StrToInt(Time.Text);
OldCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
for I := 0 to Iterations do begin
Application.ProcessMessages;
Time.Text := IntToStr(I);
end;
Screen.Cursor := OldCursor;
end;

Because you don't always know which cursor your application is using at any given
time, it is a good idea to save the current cursor before changing the Cursor property.
After you have finished with a particular cursor, you can restore the old cursor.





NOTE: Sometimes you will change the Cursor property before a lengthy processing
loop and nothing seems to happen. The reason is that Windows didn't get a chance
to change your cursor before your program entered the loop. When in the loop, your
application can't process any messages--including the message to change the cursor.
The fix is to pump the Windows message loop for waiting messages while you are in
your loop:
Application.ProcessMessages;







Now your application enables messages to flow and the cursor changes when your
loop starts.





You can, of course, change the cursor for an individual component. For example,
a drawing program might change the client area cursor depending on the current drawing
tool. You don't want to change the cursor for the Screen object in that case because
you want the arrow cursor to appear when the cursor is moved over the toolbar, status
bar, menu, or any other components that might be on the form. In that case, you set
the cursor only for the component that represents the client window of your application:

PaintBox.Cursor := crCross;

Now the cursor is changed for just this one component and all other components
use their own predefined cursor.

Loading and Using Custom Cursors
Loading custom cursors requires a little more work. As I mentioned earlier, the
Cursors property of the TScreen class contains the list of cursors available to your
application. To use a custom cursor requires several steps:



1. Create a cursor in the Image Editor or other resource editor and save
it as a .res file.


2. Link the resource file to your program with the $R compiler directive.


3. Load the cursor into the Cursors array using the LoadCursor function.


4. Implement the cursor by assigning the index number to the Cursor property
for the form, the Cursor property for the Screen object, or the Cursor property for
any other component.



The first two steps were covered on Day 11 when I talked about the Image Editor
and on Day 8 when I discussed resources. After you have the cursor bound to the .exe,
you can load it with the LoadCursor function. Loading a cursor into the Cursors array
is easy:

Screen.Cursors[1] := LoadCursor(HInstance, `MYCURSOR');

This code assumes that you have a cursor with a name of MYCURSOR and that you
are assigning the cursor to position 1 in the cursors list (remember, positions -17
through 0 are used for the stock cursors). Loading the cursor has to be done only
once, so you will probably do it in your main form's OnCreate event handler.





NOTE: All cursors are loaded into the Screen object's Cursors property
regardless of whether you use the cursor with the Screen object, with a form, or
with a component. There is only one Cursors array, and it belongs to the Screen object.






To use the cursor, then, just assign it to the Cursor property of the Screen object
or of any component:

PaintBox.Cursor := 1;

If you have several cursors, you might want to create constants for each cursor
so that you have meaningful names to use instead of integer values that are easy
to mix up. Using that method, the preceding code would look like this:

const
MyCursor = 1;
{ later... }

Screen.Cursors[MyCursor] := LoadCursor(HInstance, `MYCURSOR');

{ later still... }
PaintBox.Cursor := MyCursor;

As you can see, loading and implementing custom cursors isn't difficult when you
know how to do it. The book's code for today includes a sample program called CursTest
that demonstrates the theories discussed in this section.

Summary
Today you learned about some features that make up a top-quality Windows application
and how to implement them in your own programs. I should warn you that it's tempting
to overdo it with features such as control bars, status bars, and cursors. Implement
whatever decorations your application needs, but don't overuse them. You also learned
about printing today. In some cases, printing support is built into a particular
component, and in those cases printing is incredibly easy. In other cases you have
to roll up your sleeves and go to work with the TPrinter class. Even at those times,
though, printing is nothing to fear.

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, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A



Q Can I disable all the components on my toolbar at one time?


A Yes. Set the toolbar's Enabled property to False.


Q I noticed that TCoolBar and TControlBar do about the same thing. Which one
should I use?


A My advice would be to use TControlBar. There are many versions of Microsoft's
common control library, COMCTL32.DLL. TCoolBar is a wrapper around Microsoft's cool
bar common control and, as such, is affected by the version of COMCTL32.DLL the user
has installed on his or her machine. TControlBar isn't dependent on COMCTL32.DLL,
so its behavior won't change from system to system.


Q How do I put a bitmap on my status bar?


A Create a multipanel status bar. Change the style of the panel that will
contain the bitmap to psOwnerDraw. Then, in your OnDrawPanel event handler, use the
Draw method of TCanvas to draw your bitmap on the panel.


Q Why should I bother with command enabling for my menu items and toolbar
buttons?


A Because users expect a consistent interface. When certain choices are
not available (whether on menus or toolbars), they should be grayed out. This gives
the user a visual cue that these commands are sometimes available but not currently
applicable.


Q I'm a longtime Delphi user. I've got a system for command enabling in place.
Why should I switch to actions and action lists?


A Actions give you a central location from which you can do all your command
enabling. Rather than having your command enabling code spread throughout your code
base, you can now have it in one location.


Q I just want basic output of a large, multiline edit control in my application.
What's the easiest way?


A The easiest way is to use a RichEdit component and use its Print method
to print the contents of the component.


Q I see that there is a Handle property for the Printer object and also a
Handle property for the Canvas property of the Printer object. What's the difference?


A In this case there is no difference. If you are calling a Windows API
function that requires the printer device context handle, you can use either Printer.Handle
or Printer.Canvas.Handle.


Q When I change the cursor for my main form, the cursor is correct when it's
over the form but reverts back to the arrow cursor when it's over any of my buttons.
Why?


A You need to change the cursor for the Screen object, not for the form.
Changing the cursor for the Screen object ensures that the new cursor will be used
whenever the cursor is over any part of your application.



Quiz



1. How do you attach an event handler to a toolbar button's OnClick event?


2. Can you put controls other than buttons on toolbars?


3. What is the name of the TActionList event you respond to when doing
command enabling?


4. What does the SimplePanel property of the StatusBar component do?


5. How do you change the status bar text manually?


6. How do you enable and disable menu items and buttons?


7. How do you access the printer in a Delphi application?


8. What method do you call to begin printing with the TPrinter class?


9. What method of TPrinter do you call when you want to start a new page
when printing?


10. How do you change the cursor for a component at runtime?



Exercises



1. Write a program from scratch. Add a toolbar and place five buttons
on it. Now add a status bar. Enable hints so that the toolbar buttons have both tooltips
and hint text in the status bar.


2. Change the status bar for the ScratchPad program and add a second panel.
In this panel, display either Saved or Modified based on the value of Memo.Modified.


3. Change the About box of the ScratchPad program to read Version 1.05.
Also, change the Title property in the Project Options dialog box and the Caption
of the main form. After all, you added features, so you must let the users know!


4. Create a custom cursor with the Image Editor. Write a program that
displays the cursor when a button on the main form is pressed.


5. Extra Credit: Modify the ScratchPad program from exercise 3 to display
different bitmaps on the status bar depending on whether the current file is saved.
(Hint: Look at the led1on.bmp and led1off.bmp bitmaps in the Borland Shared Files\Images\Buttons
directory.)


6. Extra Credit: Modify the ScratchPad program so that the user can specify
a top margin, bottom margin, right margin, and left margin for printed output.









& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > ch07.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 7 -- VCL Components








Teach Yourself Borland Delphi 4 in 21 Days







- 7 -


VCL Components



A Review of Components

Visual Components
Nonvisual Components

The Name Property
Important Common Properties

The Align Property
The Color Property
The Cursor Property
The Enabled Property
The Font Property
The Hint Property
The ParentColor, ParentCtl3D, ParentFont, and ParentShowHint
Properties
The Tag Property
Other Common Properties

Primary Methods of Components
Common Events
Standard Windows Control Components
Edit Controls

The ListBox and ComboBox Components
VCL Button Types
The Label Component
The ScrollBar Component

The Panel Component
And That's Not All...
The Common Dialog Boxes

The Execute Method
The File Open and File Save Dialog Boxes
The File Open Picture and File Save Picture Dialog Boxes
The Color Dialog Box
The Font Dialog Box
The Find and Replace Dialog Boxes

Summary
Workshop

Q & amp;A
Quiz
Exercises

In Review








As you know by now, components are much of what gives Delphi its power. Using
the Form Designer, you can place a component on a form and modify its design-time
properties. In some cases, that's all you have to do. If needed, you can also manipulate
the component at runtime by changing its properties and calling its methods. Further,
each component is designed to respond to certain events. I discussed properties,
methods, and events on Day 5, & quot;The Visual Component Model, & quot; so I'm not
going to go over that again.
Today you will find out more about components. You will learn about often-used
components and, as a result, learn about the Visual Component Library (VCL) classes
that represent those components. As you go through this chapter, feel free to experiment.
If you read something that you want to test, by all means do so. Learning by experience
is as valuable as anything you can do, so don't be afraid to experiment.

A Review of Components
Let's review some of what you already know about components. Before doing that,
though, I want to take a moment to explain the differences between a VCL component
and a Windows control. Windows controls include components such as edit controls,
list boxes, combo boxes, static controls (labels), and buttons, not to mention all
the Win32 controls. Windows controls, by nature, don't have properties, methods,
and events. Instead, messages are used to tell the control what to do or to get information
from the control. To say that dealing with controls on this level is tedious and
cumbersome would be an understatement.
A VCL component is a class that encapsulates a Windows control (not all VCL components
encapsulate controls, though). A VCL component in effect adds properties, methods,
and events to a Windows control to make working with the control easier. You might
say that VCL takes a fresh approach to working with Windows controls. It could be
said that all VCL components are controls, but not all controls are components. A
VCL Edit component, for example, is a control, but a standard Windows edit control
is not a VCL component. VCL components work with Windows controls to raise the job
of dealing with those controls to a higher level.
Given that discussion, then, I will use the terms control and component
interchangeably when referring to VCL components. (But I will never call a Windows
control a component!)

Visual Components
Visual components include components such as edit controls, buttons, list boxes,
labels, and so on. Most components you will use in a Delphi application are visual
components. Visual components, as much as possible, show you at design time what
the component will look like when the program runs.
New Term: Some components are visual components; others are nonvisual
components. A visual component , as its name implies, is one that can be seen
by the user at design time.

Nonvisual Components
New Term: A nonvisual component is one that cannot be seen by
the user at design time.
Nonvisual components work behind the scenes to perform specific programming tasks.
Examples include system timers, database components, and image lists. Common dialog
boxes such as File Open, File Save, Font, and so on are considered nonvisual components
as well. (They are nonvisual because they don't show themselves at design time. At
runtime, they become visible when they are invoked.) The common dialog components
are discussed later in the section & quot;The Common Dialog Boxes. & quot;
When you place a nonvisual component on a form, Delphi displays an icon representing
the component on the form. This icon is used to access the component at design time
in order to change the component's properties, but the icon does not show up when
the program runs. Nonvisual components have properties, methods, and events just
like visual components do.
Now let's look at some of the common properties components share.

The Name Property
The Name property serves a vital role in components. On Day 5, & quot;The Visual
Component Model, & quot; in the section & quot;VCL Explored, & quot; I discussed some
of what happens when you place a component on a form. As soon as you place a component
on a form, Delphi goes to work in the background while you ponder your next move.
One thing Delphi does is create a pointer to the component and assign the Name property
as the variable name. For example, let's say you place an Edit component on a form
and change the Name property to MyEdit. At that point, Delphi places the following
in the class declaration for the form (in the published section):

MyEdit: TEdit;

When the application runs, Delphi creates an instance of the TEdit class and assigns
it to MyEdit. You can use this pointer to access the component at runtime. To set
the text for the edit control, you would use

MyEdit.Text := `Jenna Lynn';

Delphi also uses the Name property when creating event-handler names. Let's say
that you want to respond to the OnChange event for an Edit component. Normally, you
double-click the Value column in the Object Inspector next to the OnChange event
to have Delphi generate an event handler for the event. Delphi creates a default
function name based on the Name property of the component and the event being handled.
In this case, Delphi would generate a function called MyEditChange.
You can change the Name property at any time provided that you change it only
via the Object Inspector. When you change a component's Name property at design time,
Delphi goes through all the code that it previously generated and changes the name
of the pointer and all event-handling functions.





NOTE: Delphi will change all the code that it generated to reflect the
new value of the component's Name property, but it will not modify any code you wrote.
In other words, Delphi will take care of modifying the code it wrote, but it is up
to you to update and maintain the code you wrote. Generally speaking, you should
set the Name property when you initially place the component on the form and leave
it alone after that. There's no problem with changing the name at a later time, but
it might lead to more work.





Continuing with this example, if you change the Name property of the edit control
from MyEdit to FirstName, Delphi will change the pointer name to FirstName and the
OnChange handler name to FirstNameChange. It's all done automatically; you don't
have to do anything but change the Name property and trust that Delphi will do the
rest of the work.





CAUTION: Never change the Name property at runtime. Never manually change
a component's name (the name that Delphi assigned to the component's pointer) or
event-handler names in the Code Editor. If you perform either of these actions, Delphi
loses track of components and the results are not good, to say the least. You might
even lose the ability to load your form. The only safe way to change the Name property
of a component is through the Object Inspector.





Delphi assigns a default value to the Name property for all components placed
on a form. If you place an Edit component, for example, Delphi assigns Edit1 to the
Name property. If you place a second Edit component on the form, Delphi will assign
Edit2 to that component's Name property, and so on. You should give your components
meaningful names as soon as possible to avoid confusion and extra work later on.





NOTE: You can leave the default names for components that will never be
referenced in code. For example, if you have several label components that contain
static (unchanging) text, you can leave the default names because you won't be accessing
the components at runtime.






Important Common Properties
All components have certain properties in common. For example, all visual components
have Left and Top properties that determine where the component is placed on the
form. Properties such as Left, Top, Height, and Width are self-explanatory, so I
won't go over them here. A few of the common properties, however, warrant a closer
look.

The Align Property
On Day 6, & quot;Working with the Form Designer and the Menu Designer, & quot; I
discussed the Align and Alignment properties, so I won't go over those again in detail.
Refer to Day 6 for complete information on Align. It should be noted here, however,
that not all components expose the Align property at design time. A single-line edit
control, for example, should occupy a standard height, so the features of the Align
property do not make sense for that type of component. As you gain experience with
Delphi (and depending on the type of applications you write), you will probably rely
heavily on the Align property.

The Color Property
The Color property sets the background color for the component. (The text color
is set through the Font property.) Although the Color property is simple to use,
there are a few aspects of component colors that should be addressed.
The way the Color property is handled in the Object Inspector is somewhat unique.
If you click the Value column, you see the drop-down arrow button indicating that
you can choose from a list of color values. That is certainly the case, but there's
more to it than that. If you double-click the Value column, the Color dialog box
will be displayed. This dialog box enables you to choose a color from one of the
predefined colors or to create your own colors by clicking the Define Custom Colors
button. Figure 7.1 shows the Color dialog box after the Define Custom Colors button
has been clicked.
FIGURE 7.1. The
Color dialog box.
NOTE: This is the same Color dialog box that will be displayed if you
implement the ColorDialog component in your application.
If you choose a color from the Color dialog box, you see that the value of the
Color property changes to a hexadecimal string. This string represents the red, green,
and blue (RGB) values that make up the color. If you know the exact RGB value of
a color, you can type it in (not likely!).
Most of the time you will probably choose a color from the list of color values
provided. When you click the drop-down button to display the list of possible values,
you will see what essentially amounts to two groups of values. The first group of
colors begins with clBlack and ends with clWhite. These are the Delphi predefined
colors; this list represents the most commonly used colors. To choose one of the
listed colors, simply click the color in the list. If you can't find a color in the
list that suits your needs, you can invoke the Color dialog box as discussed.
The second group of colors in the list begins with clScrollBar. This group of
colors represents the Windows system colors. If you use colors from this list, your
application will automatically adjust its colors when the user changes color schemes
in Windows. If you want your application to follow the color scheme the user has
chosen for his or her system, you should choose colors from this list rather than
from the first list.
Use of color should be carefully considered. Proper use of color provides an aesthetically
pleasing environment for the user. Abuse of colors makes for an obnoxious application
that is annoying to use. Color is like a magnet to new programmers. It is common
to want to throw lots of colors on a form because it's fun and easy, but don't get
caught up in the fun at the expense of your users.

The Cursor Property
The Cursor property controls the cursor that is displayed when the user moves
the mouse cursor over the component. Windows automatically changes cursors for some
components. For example, Windows changes the cursor to an I-beam when the cursor
is moved over an Edit, Memo, or RichEdit component.
To let Windows manage the cursor, leave the Cursor property set to crDefault.
If you have specialized windows (components), you can specify one of the other cursors.
When the mouse is moved over that component, Windows will change the cursor to the
one you specified.
Frequently, you will need to change cursors at runtime. A long process, for example,
should be indicated to the user by displaying the hourglass cursor. When you reset
the cursor, you need to be sure to set the cursor back to whatever it was originally.
The following code snippet illustrates this concept:

var
OldCursor : TCursor;
begin
OldCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
{ do some stuff which takes a long time }
Screen.Cursor := OldCursor;
end;

This ensures that the cursor that was originally set for the application is properly
restored.
Another cursor property, DragCursor, is used to set the cursor that is used when
the mouse cursor is over a component that supports drag-and-drop. As with colors,
you should be prudent in your use of cursors. Use custom cursors when needed, but
don't overdo it.

The Enabled Property
Components can be enabled or disabled through the Enabled property. When a component
is disabled, it cannot accept focus (clicking on it has no effect), and usually it
gives some visual cue to indicate that it is disabled. In the case of buttons, for
example, the button text is grayed out as is any bitmap on the button. Enabled is
a Boolean property: Set it to True to enable the component or set it to False to
disable the component. Enabling and disabling windows (remember that windowed components
are windows, too) is a feature of Windows itself.





NOTE: Some components show their disabled state at design time, but most
don't. The BitBtn component is one that does show its disabled state at design time.






The Enabled property applies mostly to windowed components, but it can apply to
non-windowed components as well. The SpeedButton component is an example of a non-windowed
component that can be disabled.





NOTE: Modifying the Enabled property for a Panel component has additional
implications. Panels are often used as containers for other controls. Therefore,
a panel becomes the parent of the controls that are placed on the panel. If you disable
a panel, the components on the panel will not show as disabled, but they will not
function because their parent (the panel) is disabled.





Although components can be disabled at design time, enabling and disabling components
is something that is usually done at runtime. Menu items, for example, should be
enabled or disabled according to whether they apply at a given time. The same is
true of buttons. There are a variety of reasons why you might want to disable other
types of controls as well.
To disable a component at runtime, just assign False to its Enabled property,
and to enable a component assign True to Enabled. The following code snippet enables
or disables a menu item based on some condition:

if CanSave then
FileSave.Enabled := True
else
FileSave.Enabled := False;

This process is often referred to as command enabling and is an important
part of a professional-looking Windows program.





TIP: The TActionList component can be used to enable or disable a component
or groups of components. TActionList is discussed in detail on Day 13, & quot;Beyond
the Basics, & quot; in the section & quot;Command Enabling. & quot;






The Font Property
The Font property is a major property and therefore needs to be included here,
but there is not a lot that needs to be said about it. The Font property is an instance
of the TFont class and, as such, has its own properties. You can set the Font properties
by double-clicking on the font name in the Object Inspector (which will expand the
Font node and show the Font properties) or by invoking the Font dialog box. (The
Font dialog box is discussed in more detail later in this chapter in the section
& quot;The Font Dialog Box. & quot;) Figure 7.2 shows the Object Inspector with the
Font property node expanded to reveal the TFont properties.
FIGURE 7.2. The
Object Inspector showing the Font property.
The Color property sets the color of the font, and the Name property enables you
to choose the typeface for the font.
The Height and Size properties of TFont deserve special mention as well:


The Height property is used to specify the height of the font in pixels.

The Size property is used to specify the height of the font in points.


When you change one of these properties, the other will change automatically.
The Height is often specified as a negative number. Refer to the online help for
TFont for an explanation of why this is the case.
The Pitch property is not particularly useful. I'll explain it in just a moment,
but first a quick tutorial on fonts. A font can be either proportionally spaced or
fixed space:


Most fonts are proportionally spaced , which means that each letter only
takes as much space as needed. For example, an uppercase M takes up much more space
than a lowercase i. Take a look at the letters in this book and you will see what
I mean. Examples of proportional fonts include Times New Roman, Arial, and Bookman.

With a fixed space font (typically called a fixed-pitch font), on the
other hand, all characters take exactly the same amount of space. This is convenient
for windows such as code editors (the Delphi Code Editor, for example) or any other
window where a fixed-pitch font is desired. Courier New is probably the most commonly
used fixed-pitch font, although Fixedsys is the Windows fixed-pitch font of choice
in some Windows applications. Fixed space fonts are harder to read, so they
aren't normally used for long blocks of text other than code.


In theory, the Pitch property can be used to force a proportionally spaced font
to fixed space and vice versa. The problem is that Windows might perform font substitutions
to carry out the conversion. In other words, you really don't know what you might
get. It is far better to pick exactly the font you require than to rely on the Pitch
property.

Finally, the Style property of TFont can be used to toggle bold, italic, underline,
or strikethrough. These styles are not mutually exclusive, so you can mix styles
in any way you choose.





TIP: Although you can use the Object Inspector to change font properties,
the Font dialog box (invoked when you click the ellipsis button next to the Font
property) has the added benefit of showing you a sample of what the font looks like
as you choose different font options. To simply change the font's Style property
or Size property, use the Object Inspector. But if you are looking for just the right
font, the Font dialog box is a better choice.






The Hint Property
The Hint property is used to set hint text for a component. The hint text has
two parts. The first part is sometimes called the short hint . This is the
hint text that is displayed when the user places the cursor over the component and
pauses. The pop-up window that displays the hint text is called a tooltip .
The second part of the hint text is sometimes called the long hint . The
long hint is the optional hint text that shows in the status bar when the user moves
the mouse cursor over the component. The short and long hint texts are separated
by a pipe (|). For example, to specify both the short hint text and the long hint
text for a File Open speed button, you would enter the following for the Hint property:

File Open|Open a file for editing

In order for short hints to show, you must have the Application object's ShowHint
property set to True (the default) as well as the component's ShowHint property.
Displaying the long hint in the status bar requires a little more work, so I'll save
that discussion for tomorrow.





NOTE: You can specify the short hint text, the long hint text, or both.
You can use the pipe to tell Delphi which hint text you are supplying. If you don't
use the pipe, both the short hint and the long hint will use the same text.




NOTE: Although there are no limits on the length of either the long hint
or the short hint, you should keep each hint's use in mind when you create them.
Short hints should probably be limited to 30 characters or fewer. Long hints can
be more descriptive, but keep in mind that long hints that are very long will be
truncated when displayed in the status bar.






The ParentColor, ParentCtl3D, ParentFont, and ParentShowHint
Properties
The ParentColor, ParentCtl3D, ParentFont, and ParentShowHint properties work the
same way, so I'll discuss them at the same time. When these properties are set to
True, the component takes its Color, Ctl3D, Font, or ShowHint settings from its parent.
For example, for most components the ParentFont property is set to True by default.
This means the component will inherit the font that its parent is currently using.
To better understand this, do this exercise:



1. Create a blank form. Set the Font property's Size property to 16.


2. Place a Label component on the form. Notice that the label automatically
uses the 16-point font.


3. Place a Button component on the form. It also uses the 16-point font.



You can set this property to False, but by the time the component is placed it
is already too late and you will have to change the font manually to the font you
want for the component.

The Tag Property
The Tag property is nothing more than a four-byte variable set aside for your
use. You can use the Tag property to store any data that your component might need.
The data stored might be a pointer to another class, an index value, or any number
of other possibilities. Using the Tag property would probably be considered an advanced
programming technique.

Other Common Properties
Table 7.1 lists other common properties that are frequently used. These properties
don't require as much explanation, so they are listed here for your reference. Not
all components have each of the properties listed.

TABLE 7.1. ADDITIONAL COMPONENT PROPERTIES.



Property
Description


BorderStyle
Can be bsSingle or bsNone. Use bsNone when you want the component to blend in with
the background.


BoundsRect
The rectangle of the entire component (not limited to only the client area).


Caption
Sets the component's caption. Many components don't have captions, so for those components
the Caption property is not exposed.


ClientHeight
Contains the height of the client area of the component.


ClientRect
Contains the rectangle for the client area of the component.


ClientWidth
Contains the width of the client area of the component.


Constraints
Sets the size constraints for the component (maximum width and height, minimum width
and height). More important for forms than for other components.


Ctl3D
Indicates whether the control should be drawn with a 3D border. If BorderStyle is
set to bsNone, this property has no effect.


Height
Sets the component's height.


HelpContext
The HelpContext property is used to associate an index number in a help file with
a particular component.


Left
Sets the x-coordinate of the component.


Parent
A pointer to the parent of the component.


PopupMenu
Specifies the pop-up menu that will be displayed when the user clicks the secondary
mouse button.


TabOrder
For windowed components. Sets this component's position in the tab order.


TabStop
For windowed components. Indicates that this component can be tabbed into. Setting
this property to False removes the component from the tab order.


Top
Sets the y-coordinate of the component.


Visible
When read, indicates whether the component is currently visible. When written to,
Visible either hides or shows the component.


Width
Sets the width of the component.



Primary Methods of Components
There are more than 20 methods that most components have in common. Windowed components
have more than 40 common methods from which to choose. Interestingly, not many of
these are widely used. Much of the functionality of components is accomplished via
properties. For example, to hide a component, you can call the Hide method or you
can set the Visible property to False. In addition, components typically have methods
specific to their purpose, and it will likely be those methods that you use most
when dealing with a particular component.
There are a few methods worthy of note, however, so I'll list them here (see Table
7.2). Note that some of these methods are not available to all controls. These are
not the most often used methods common to every component, but rather the most commonly
used methods of components in general. Also, this list concentrates on components
representing controls (components placed on forms) rather than components as forms.
Methods particular to forms were discussed on Day 4, & quot;The Delphi IDE Explored. & quot;

TABLE 7.2. COMMON METHODS OF COMPONENTS.



Method
Description


Broadcast
Used to send a message to all windowed child components.


ClientToScreen
Converts client window coordinates into screen coordinates.


ContainsControl
Returns True if the specified component is a child of the component or form.


HandleAllocated
Returns True if the Handle property for the component has been created. Simply reading
the Handle property automatically creates a handle if it hasn't already been created,
so HandleAllocated can be used to check for the existence of the handle without creating
it.


Hide
Hides the component. The component is still available to be shown again later.


Invalidate
Requests that the component be redrawn. The component will be redrawn at Windows's
earliest convenience.


Perform
Sends a message directly to a component rather than going through the Windows messaging
system.


Refresh
Requests that a component be redrawn immediately and erases the component prior to
repainting.


Repaint
Requests that a component be redrawn immediately. The component's background is not
erased prior to repainting.


SetBounds
Enables you to set the Top, Left, Width, and Height properties all at one time. This
saves time having to set them individually.


SetFocus
Sets the focus to a component and makes it the active component. Applies only to
windowed components.


Update
Forces an immediate repaint of the control. Typically, you should use Refresh or
Repaint to repaint components.



Now let's take look at some of the events to which a component is most likely
to respond.

Common Events
As with properties and methods, there are some events that will be responded to
most often. Components cover a wide variety of possible Windows controls, so each
component will have individual needs. Events specific to forms are not covered here
because I covered that information on Day 4. The most commonly used events are listed
in Table 7.3.

TABLE 7.3. COMMONLY HANDLED COMPONENT EVENTS.



Event
Description


OnChange
This event is triggered when a control changes in one way or another. Exact implementation
depends on the component.


OnClick
Sent when the component is clicked with either mouse button.


OnDblClick
This event occurs when the user double-clicks the component.


OnEnter
This event occurs when a windowed component receives focus (is activated).


OnExit
This event occurs when a windowed component loses focus as the result of the user
switching to a different control. It does not occur, however, when the user switches
forms or switches to another application.


OnKeyDown
This event is triggered when the user presses a key while the control has focus.
Keys include all alphanumeric keys as well as keys such as the arrow keys, Home,
End, Ctrl, and so on.


OnKeyPress
This event is also triggered when the user presses a key, but only when alphanumeric
keys or the Tab, backspace, Enter, or Esc keys are pressed.


OnKeyUp
This event occurs whenever a key is released.


OnMouseDown
This event is triggered when the mouse button is pressed while it's over the component.
The parameters passed to the event handler give you information on which mouse button
was clicked, special keys that were pressed (Alt, Shift, Ctrl), and the x,y coordinate
of the mouse pointer when the event occurred.


OnMouseMove
This event occurs any time the mouse is moved over the control.


OnMouseUp
This event is triggered when the mouse button is released while over a control. The
mouse button must first have been clicked while on the control.


OnPaint
This event is sent any time a component needs repainting. You can respond to this
event to do any custom painting a component requires.








DEALING WITH MOUSE EVENTS
Mouse events have a couple of peculiarities that you should be aware of. If you
are responding just to a mouse click on a component, you will want to keep it simple
and only respond to the OnClick event. If you must use OnMouseDown and OnMouseUp,
you should be aware that the OnClick event will be sent as well as the OnMouseDown
and OnMouseUp events. For example, a single click will result in these events occurring
(and in this order):
OnMouseDown
OnClick
OnMouseUp







Similarly, when the user double-clicks with the mouse, it can result in the application
getting more events than you might think. When a component is double-clicked, the
following events occur:
OnMouseDown
OnClick
OnDblClick
OnMouseUp







The point I am trying to make is that you need to take care when responding to
both double-click and single-click events for a component. Be aware that you will
get four events for a double-click event.
Multiple events will occur when a key is pressed, too. A keypress in an edit control,
for example, will result in OnKeyDown, OnKeyPress, OnChange, and OnKeyUp events occurring.
The book's code (go to http://www.mcp.com/info
and type 0-672-31286-7) contains a program called EventTst, which illustrates the
fact that multiple events occur on mouse clicks and keypresses. Run this program
and you will see how multiple events can be triggered based on certain user actions.






In just a moment you're going to look at some of the VCL components in more detail.
First, however, I want to introduce you to a class that is used by certain VCL components--TStrings.

TStrings
The TStrings class is a VCL class that manages lists of strings. Several VCL components
use instances of TStrings to manage their data (usually text). For example, on Day
6 you used TStrings when you built the ScratchPad application. & quot;I don't recall
using a TStrings class, & quot; you say. Well, you did, but you just weren't aware
of it. Remember when you saved and loaded files? You used something like this:

Memo.Lines.SaveToFile(SaveDialog.FileName);

The Lines property of TMemo is an instance of the TStrings class. The SaveToFile
method of TStrings takes the strings and saves them to a file on disk. You can use
the same technique to load a list box from a file on disk or save the contents of
a list box to disk. In the case of the TListBox class, the property that holds the
list box items is called Items. For example, try this exercise:



1. Create a new application and place a ListBox component on the form.
Size the list box as desired.


2. Change the Name property of the list box to ListBox.


3. Double-click the background of the form (not on the list box). The
Code Editor displays the FormCreate function.


4. Modify the FormCreate function so that it looks like this:






procedure TForm1.FormCreate(Sender: TObject);
var
WinDir : array [0..255] of Char;
FileName : string;
begin
GetWindowsDirectory(WinDir, SizeOf(WinDir));
FileName := WinDir + `\win.ini';
ListBox.Items.LoadFromFile(FileName);
end;







5. Click the Run button to compile and run the program.



When the program runs, the list box will contain the contents of your WIN.INI
file. Using this method, it's easy to load a list box from any ASCII text data file.
The ComboBox component also has an Items property that works in exactly the same
way.
You can add, delete, insert, and move items in a list box, combo box, or memo
by calling the Add, Append, Delete, Insert, and Move methods of the TStrings class.





NOTE: How Add performs depends on the value of the Sorted property. If
the Sorted property is set to True, Add will insert the string where it needs to
be in the list of items. If Sorted is False, the new string will be added at the
end of the list.





A component can be cleared of its contents by calling the Clear method. An individual
string can be accessed by using the array subscript operator. For example, to retrieve
the first string in a list of strings, you would use

Edit.Text := ListBox.Items[0];






NOTE: The strings in a TStrings class are actually contained in the Strings
property. The Strings property is declared as the default array property for
the TStrings class, so you don't specifically have to reference it when retrieving
a specific string (although you can if you want to). Given that, then, the following
two lines result in the same code being generated by the compiler:
Edit.Text := ListBox.Items[0];
Edit.Text := ListBox.Items.Strings[0];







Each string in a TStrings array contains the string itself and four bytes of extra
storage. This extra storage can be accessed through the Objects property, and you
can use the extra storage any way you like. Let's say, for example, that you create
an owner-drawn list box that displays bitmaps. You can store the string in the usual
way and store a pointer to the TBitmap object in the Objects array.





TIP: There might be times when you need to manage a list of strings unrelated
to a component. The TStringList class is provided for exactly that purpose. This
class works just like TStrings but can be used outside of components. TStringList
is particularly convenient for reading, manipulating, and storing text files.




NOTE: In reality, TStrings is what is called an abstract base class .
An abstract base class is never used directly; it serves only as a base class from
which to derive other classes. The Lines property is actually an instance of the
TMemoStrings class rather than an instance of the TStrings class as I said in this
section. This can be confusing because the Lines property is declared as a
TStrings pointer but is actually an instance of TMemoStrings. The declaration and
creation of the Lines property looks like this:
var
Lines : TStrings;
{ ...later }
Lines := TMemoStrings.Create;







This is why the Lines property appears to be a TStrings but is really not. I didn't
mean to lead you astray, but I thought it was best to make this distinction after
the discussion on TStrings rather than confuse you with this information during that
discussion.





New Term: An abstract base class is a class that cannot be used
directly. A descendent class must be created using the abstract base class, and an
instance of the descendent class is used instead.

Standard Windows Control Components
Back in the Jurassic age, there was something called Windows 3.0. Windows 3.0
gave you options such as edit controls (single line and multiline), list boxes, combo
boxes, buttons, check boxes, radio buttons, and static controls. These controls must
have been fairly well designed because they are very prevalent in Windows programs
today--even considering all the new Win32 controls.
I'm not going to go over every Windows control and its corresponding VCL component.
There are a few points, though, that you should know regarding the standard components,
which are covered in the next sections.





NOTE: I will refer to components in one of two ways: by the component's
name or by the name of the VCL class that defines the component. I might say, & quot;The
Label component is used for... & quot; or I might say, & quot;TLabel is used for.... & quot;
In either case, I am talking about the same component.






Edit Controls
Delphi comes with four edit-control components. The Edit, Memo, and MaskEdit components
are based on the standard Windows edit control. The RichEdit component is based on
the Win32 rich edit control, which is not one of the standard Windows controls. Still,
I will discuss RichEdit here because it has many features in common with the other
edit controls.

The Edit Component
The Edit component encapsulates the basic single-line edit control. This component
has no Align or Alignment property. It has no Alignment property because the text
in a single-line edit control can only be left-justified. The Edit component has
no Align property because it cannot (or more accurately, should not) be expanded
to fill the client area of a window.





TIP: If you need text in an edit component to be right-justified or centered,
use a Memo component but make its height the height of a standard Edit component.
Then set the Alignment property as needed.




NOTE: Keep your forms standard whenever possible. Although you can make
an Edit component as tall as you like, it will confuse users if you make its height
greater than a standard Windows edit control (it might appear to the user to be a
multiline edit).






The MaskEdit Component
The MaskEdit component is an Edit component with an input filter, or mask, attached.
The MaskEdit does not represent a Windows control per se, but rather is just a VCL
extension of a standard edit control. A mask is used to force input to a specific
range of numbers or characters. In addition, the mask can contain special characters
that are placed in the edit control by default. For example, a date is commonly formatted
as follows:

03/21/98

An edit mask for a date can already have the slashes in place so the user only
has to enter the numbers. The edit mask would specify that only numbers can be entered
to avoid the possibility of the user entering a nonnumeric character.





NOTE: The DateTimePicker component (found on the Win32 tab) enables you
to pick a date or a time from a specialized edit component. When the Kind property
is set to dtkDate, the component displays a drop-down calendar from which the user
can choose a date. When Kind is set to dtkTime, the DateTimePicker displays a multi-field
edit control that enables the user to set the hours, minutes, seconds, and AM or
PM. The DateTimePicker is preferred over the MaskEdit for date and time entry.





The EditMask property controls the mask that is used. When you click the ellipsis
button in the Value column for the EditMask property, the Input Mask Editor is displayed.
This dialog box enables you to choose from one of the predefined masks or to create
your own. You can choose prebuilt masks from several countries. Figure 7.3 shows
the Input Mask Editor displaying the United States' set of predefined input masks.
FIGURE 7.3. The
Input Mask Editor.
For more information on building your own masks, see the Delphi online help.

The Memo Component
The Memo component encapsulates a multiline edit control. The Lines property is
the most significant property in a Memo component. As I mentioned earlier in the
discussion on TStrings, the Lines property enables you to save the contents of the
Memo component to disk, load the Memo with text from a file, or access the memo's
lines individually.
The ScrollBars property is unique to the Memo component. This property enables
you to specify whether your component has a horizontal scrollbar, a vertical scrollbar,
or both. You used the ScrollBars property on Day 6 when you wrote the ScratchPad
application. The Memo component is a very versatile component that you will probably
find yourself using frequently.

The RichEdit Component
The RichEdit component is the biggest and the best of all the edit components;
it is based on the Win32-rich edit control. The RichEdit component enables you to
change fonts, use indentation, set text to bold, italic, or underlined, and much
more. Basically, the RichEdit component is a mini word processor in one neat package.
RichEdit has surprisingly few design-time properties over what the Memo component
has.
Key runtime properties include SelAttributes and Paragraph. The RichEdit component
is complex but easy to use, considering its complexities. See the Delphi online help
for full details on the RichEdit component.

Common Edit Control Properties
Table 7.4 lists the properties specific to components based on edit controls.

TABLE 7.4. PROPERTIES FOR EDIT CONTROLS.



Item
Applies To
Description



Properties



AutoSelect
Edit, MaskEdit
When set to True, text in the edit control will automatically be selected when the
user tabs to the control. Default: True


AutoSize
Edit, MaskEdit
When set to True, the edit control will automatically resize itself when the font
of the edit control changes. Otherwise, the edit control does not change size when
the font changes. Default: True


CharCase
Edit, MaskEdit
Determines whether the edit control displays uppercase (ecUpperCase), lowercase (ecLowerCase),
or mixed text (ecNormal). Default: ecNormal.


HideScrollBars
RichEdit
When set to True, the scrollbars will be shown when needed but hidden otherwise.
When set to False, the scrollbars are shown as determined by the value of the ScrollBars
property.


HideSelection
Edit, Memo, RichEdit
When set to True, any text selected will not show as selected when the user tabs
to another control. Default: False


Lines
Memo, RichEdit
The text contained in the component. Lines is an instance of the TStrings class.


MaxLength
All
Specifies the maximum number of characters that the component will hold. When set
to 0, the amount of text that can be input is unlimited (limited only by system considerations).
When set to any non-zero value, limits the number of characters to that value. Default:
0


OEMConvert
Edit, Memo
Set this property to True when the text input will consist of filenames. Default:
False


PasswordChar
Edit, MaskEdit
When this property is set to a value other than ASCII #0, any text entered will be
echoed with the character provided. The actual text in the edit control is unaffected.
Most password edits use the asterisk (*) as the password character. Default: #0


PlainText
RichEdit
When set to True, RTF (rich text format) files will be shown as plain text without
character and paragraph formatting. When set to False, RTF files are displayed with
full formatting. Default: False


ReadOnly
All
When set to True, the component will display its text, but new text cannot be entered.
The user can, however, highlight text and copy it to the Clipboard. Default: False


ScrollBars
Memo, RichEdit
Determines which scrollbars to display. Choices are ssNone, ssBoth, ssHorizontal,
and ssVertical. Default: ssNone


Text
Edit, MaskEdit
Contains the text in the component.


WantReturns
Memo, RichEdit
When set to True, the component keeps the return character and a new line is inserted
in the edit control when the user presses Enter. When set to False, return characters
go to the form and are not placed in the edit control. If you have a form with a
default button and WantReturns set to False, pressing Enter will cause the form to
close. Default: True


WantTabs
Memo, RichEdit
When set to True, a tab character is placed in the edit control when the user presses
the Tab key. When set to False, tab characters go to the form, which would enable
tabbing out of the edit control. Default: False


WordWrap
Memo, RichEdit
When set to True, text entered will wrap to a new line when the right edge of the
edit control is reached. When set to False, the edit control automatically scrolls
as new text is entered. Default: True


Modified
All
Indicates whether the contents of the edit control have changed since the last time
the Modified property was set. After saving the contents of a Memo or RichEdit component
to a file, you should set Modified to False.


SelLength
All
Contains the length of the text currently selected in the edit control.


SelStart
All
Contains the starting point of the selected text in the edit control. The first character
in the edit control is 0.


SelText
All
Contains the currently selected text in an edit control.



Edit controls have many common methods; they are too numerous to list here. The
CutToClipboard, CopyToClipboard, PasteFromClipboard, and Clear methods deal with
Clipboard operations and text manipulation. The GetSelTextBuff and GetTextBuff methods
retrieve the selected text in the component and the entire text in the component,
respectively. See the Delphi online help topics TEdit, TMaskEdit, TMemo, and TRichEdit
for a complete list of methods associated with each edit component.
The edit component events that you are most likely to be interested in are dependent
on the type of edit control you are using. In general, though, the OnEnter, OnExit,
OnChange, OnKeyDown (or OnKeyPress), and OnKeyUp events will be the most widely used.

The ListBox and ComboBox Components
The ListBox and ComboBox components are also widely used. The ListBox component
represents a standard Windows list box, which simply presents a list of choices that
the user can choose from. If the list box contains more items than can be shown at
one time, scrollbars provide access to the rest of the items in the list box.
New Term: Some list boxes are owner-drawn list boxes. In an owner-drawn
list box, the programmer takes the responsibility for drawing the items in the list
box.
You can do owner-drawn list boxes if needed. Owner-drawn list boxes are fairly
common, although you might not realize it. On Day 4 I talked about customizing the
Delphi toolbar. As part of that discussion, you looked at the Delphi Toolbar Editor
dialog box. The Toolbar Editor dialog box contains two list boxes (see Figure 7.4).
FIGURE 7.4. The
Toolbar Editor's Commands list box is owner-drawn.
The list box on the left is a regular list box; it lists the possible button groups
you can choose from. The list box on the right is an owner-drawn list box. It shows
the actual button as it will appear on the toolbar, as well as a textual description
of what function the button performs.
Combo boxes are specialized list boxes. Actually, a combo box is a combination
of a list box and an edit control. The user can choose from the list or type in a
value in the edit portion. When the user chooses an item from the list, that item
is placed in the edit control. There are three different types of combo box. The
combo box type is determined by the Style property. Table 7.5 lists the types of
combo boxes and a description of each.

TABLE 7.5. TYPES OF COMBO BOXES.



Item
Description


Simple
The simple style of the combo box is nothing more than an edit control placed on
top of a list box. The user can choose from the list or type text in the edit portion.


Drop-down
Similar to the simple style, except the list box portion is not initially displayed.
A drop-down button is provided so that the user can view the list and choose an item.
The user can also type text in the edit portion.


Drop-down list
This is the most restrictive type of combo box. As with the drop-down style, the
list is not initially exposed. The user can click the drop-down button to expose
the list and choose an item from the list, but cannot enter text in the edit portion.
Use this style when you want the user to select only from a predetermined set of
choices.



The book's code contains a program called ComboTst that illustrates the different
types of combo boxes. Figure 7.5 shows the test program running. Run the program
and try out the combo boxes to get a feel for how each works.
FIGURE 7.5. The
ComboTst program.
Table 7.6 lists the properties common to list boxes and combo boxes.

TABLE 7.6. PROPERTIES FOR EDIT CONTROLS.



Property
Applies To
Description


Properties


Columns
ListBox
Contains the number of columns in the list box. You can create multiple columns by
making this property greater than 1. Default: 0


ExtendedSelection
ListBox
Determines whether extended selection is allowed. Extended selection enables
the user to select items using Shift+click and Ctrl+click. Has no effect if MultiSelect
is set to False. Default: True


IntegralHeight
ListBox
When True, the list box height will be resized to be sure that no partial lines are
displayed. When False, the list box might show partial lines. Default: False


ItemHeight
Both
For use with owner-drawn list boxes and combo boxes. Sets the height of the items
in the control. Default: 13


Items
Both
A TStrings instance that contains the list of items in the list box. (See the section
on TStrings earlier in this chapter for a description of available properties and
methods.)


MaxLength
ComboBox
The maximum number of characters the user can type in the edit portion of the combo
box. Same as MaxLength in edit controls. Default: 0 (no limit)


MultiSelect
ListBox
When True, the list box enables multiple items to be selected. Default: False


Sorted
Both
When set to True, the list box items are sorted in ascending order. When set to False,
the items are not sorted. Default: False


Style
ComboBox
The style of the combo box. Choices are csSimple, csDropDown, csDropDownList, lbOwnderDrawFixed,
and csOwnerDrawVariable. (See Table 7.5 for a description of the three basic styles.)
Default: csDropDown



ListBox
Style choices for list boxes are lbStandard, lbOwnderDrawFixed, and csOwnerDrawVariable.
Default: lbStandard


TabWidth
ListBox
List boxes can use tabs. This property sets the tab width in pixels. Default: 0


Text
ComboBox
Contains the text in the edit portion of the combo box.



Runtime Properties




ItemIndex


ListBox


Contains the index of the currently selected item, with 0 being
the first item in the list. Returns -1 if no item is selected. When written to, selects
the specified index.




SelCount


ListBox


Contains the number of items selected in a multiple-selection list
box.




Selected


ListBox


Returns True if the specified item is selected or False if it is
not.




SelLength


ComboBox


Contains the length of the text currently selected in the edit
control part of the combo box.




SelStart


ComboBox


Contains the starting point of the selected text in the edit control.
The first character in the edit control is 0.




SelText


ComboBox


Contains the currently selected text in the edit control.




TopIndex


ListBox


Returns the list box item that is at the top of the list box. Can
be used to set the top item to a certain list box item.




As with the edit components you looked at earlier, there are very few ListBox
and ComboBox methods. The Clear method clears the control of all data. The ItemAtPos
methods return the list box item at the specified x and y coordinates. The SelectAll
method selects the text in the edit control portion of a combo box.
Easily the most-used events when dealing with combo boxes and list boxes are the
OnChange and OnClick events. Use these events to determine when a selection has been
made in the list box.





NOTE: Clicking the edit portion of a combo box or the drop-down button
does not result in an OnClick event being sent. Only when the list box portion of
a combo box is clicked will the OnClick event occur.





The OnChange event can be used to detect changes to the edit portion of a combo
box just as it is used with edit controls. The OnDropDown event is used to detect
when the drop-down button on a combo box has been clicked. The OnMeasureItem and
OnDrawItem events are used with owner-drawn list boxes and owner-drawn combo boxes.

VCL Button Types
VCL contains several types of buttons that you can use in your applications. Although
not all of them are based on the standard Windows button control, I will still address
all the button types here. Before you look at the specific button components, though,
let's cover some of the basics.





NOTE: When setting a button's Caption property, use the ampersand ( & amp;)
just as you would when setting the Caption property of menu items. The character
after the ampersand will be underlined and will be the accelerator for the button.







Button Properties
The button components have only four properties of note, as follows:


ModalResult

Default

Cancel

Enabled


The ModalResult Property & #160; & #160; The ModalResult property is used to
provide built-in form closing for forms displayed with ShowModal. By default, ModalResult
is set to mrNone (which is 0). Use this value for buttons that are used as regular
buttons on the form and that don't close the form. If you use any non-zero value
for ModalResult, pressing the button will close the form and return the ModalResult
value. For example, if you place a button on a form and set the ModalResult property
to mrOk, pressing the button will close the form, and the return value from ShowModal
will be mrOk (1). Given that, then, you can do something like the following:

var
Res : Integer;
begin
Res := MyForm.ShowModal;
if Res = mrOK then
DoSomething;
if Res = mrCancel then

Exit;

end;

Table 7.7 lists the ModalResult constants that VCL defines.

TABLE 7.7. VCL ModalResult CONSTANTS.



Constant
Value


mrNone
0


mrOk
1


mrCancel
2


mrAbort
3


mrRetry
4


mrIgnore
5


mrYes
6


mrNo
7


mrAll
8


mrNoToAll
9


mrYesToAll
10








NOTE: You don't have to use one of the predefined ModalResult constants
for your buttons; you can use any value you like. Let's say, for example, you have
a custom dialog box that could be closed by using a variety of buttons. You could
assign a different ModalResult value to each button (100, 150, and 200, for example),
and you would then know which button closed the dialog box. Any nonzero number is
valid, up to the maximum value of an Integer.





The book's code contains a program called ButtnTst that demonstrates the use of
ModalResult. The program enables you to execute a form containing several buttons.
When you click a button, the ModalResult will be reported on the main form.
The Default Property & #160; & #160; The Default property is another key property
of buttons. Windows has a standard mechanism for dealing with dialog boxes. One of
the features of this mechanism is as follows: If a control other than a button has
keyboard focus and the user presses the Enter key on the keyboard, the dialog box
will behave as if the user had clicked the default button .
The default button is the button that has the BS_DEFPUSHBUTTON style set (usually
the OK button). This feature has been the bane of programmers and the curse of data-entry
personnel for years. The Default property is used to set a button as the default
button for a form. The default value for this property is False. To make a button
the default button, set its Default property to True. If you don't specifically set
any button's Default property to True, the form will not close when the user presses
the Enter key.





NOTE: When the user closes the form by pressing the Enter key, the OnClick
handler of the default button (if one exists) will be called before the form closes.






The Cancel Property
The Cancel property works with the Esc key in much the same way as the Default
property works with the Enter key. When the user presses the Esc key to close a form,
the return value from ShowModal will be the ModalResult value of the button whose
Cancel property is set to True. If no button has its Cancel property set to True,
mrCancel will be returned if the user uses the Esc key to close the form (mrCancel
is equal to 2; see Table 7.7).





NOTE: Closing a form by clicking the system close box or by pressing Alt+F4
will result in mrCancel being returned from ShowModal, as you would expect. Pressing
the Esc key, however, will result in a return value of the ModalResult property being
set to whatever button has the Cancel property set to True. The OnClick handler for
the Cancel button will be called before the form closes. No OnClick handler is called
if the user uses the system close box or Alt+F4 to close the form. Be sure to anticipate
the different ways users might use (or abuse) your forms.




NOTE: You can have more than one button with a Default property set to
True. Likewise, you can have more than one button with the Cancel property set to
True. However, when the user presses Enter on the keyboard, the first button in the
tab order that has its Default property set to True will be invoked. Similarly, when
the user presses the Esc key to close the form, the return value from ShowModal will
be the ModalResult value of the first button in the tab order that has its Cancel
property set to True.





The Enabled Property & #160; & #160; Earlier I discussed the Enabled property
when I discussed components in general. This property is used often with buttons
to enable or disable the button depending on the current state of the program or
of a particular form. When a button is disabled (its Enabled property is set to False),
its text is grayed out and the button does not function. In the case of buttons with
bitmaps on them (BitBtn and SpeedButton), the bitmap will also be grayed out automatically.
Button components have only one method of interest: the Click method, which simulates
a mouse click. When you call Click for a button, the OnClick event of the button
is executed just as if the user had clicked the button. As for events, typically
only the OnClick event is used.
Now let's take a look at the different button components Delphi provides.

The Button Component
The standard Button component is sort of like actor Danny DeVito: He ain't pretty,
but he sure gets a lot of work. There really isn't anything to add concerning the
standard Button component. It has a default Height property value of 25 pixels and
a default Width property value of 75. Typically you will place a button on a form
and respond to its OnClick event, and that's about it.

The BitBtn Component
The BitBtn component is a perfect example of how a component can be extended to
provide additional functionality. In this case, the standard Button component is
extended to enable a bitmap to be displayed on the face of the button.
The BitBtn component has several properties in addition to what the Button component
provides. All these properties work together to manage the bitmap on the button and
the layout between the bitmap and the button's text. They are explained in the following
sections.
The Glyph Property & #160; & #160; The Glyph property represents the bitmap
on the button. The value of the Glyph property is a picture, or glyph.
New Term: A glyph is a picture that is usually in the form of
a Windows bitmap file (BMP).
The glyph itself consists of one or more bitmaps that represent the four possible
states a button can be in: up, down, disabled, and stay down. If you are creating
your own buttons, you can probably get by with supplying just one glyph, which the
BitBtn component will then modify to represent the other three possible states. The
bitmap will move down and to the right when the button is clicked and will be grayed
out when disabled. The glyph in the stay-down state will be the same as in the up
state, although the button face will change to give a pressed look.
If you provide more than one glyph, the glyphs must all be the same height and
width and must be contained in a bitmap strip. The bitmaps that ship with Delphi
provide two glyphs. Figure 7.6 shows the bitmap for the print button that comes with
Delphi (print.bmp) in both its actual size and zoomed in to show detail. Note that
the two glyphs each occupy the same width in the bitmap.
FIGURE 7.6. The
PRINT.BMP bitmap.







NOTE: The pixel in the lower-left corner of the bitmap is the color that
will be used for the transparent color. Any pixels in the bitmap having that color
will be transparent when the glyph is displayed on the button. You must keep this
in mind when designing your bitmaps. If you are not using transparency, you will
need the pixel in the lower-left corner to be a color not present anywhere else on
the bitmap. If you don't want to use the lower-left pixel as the transparent color,
you can set the TransparentMode property to tmFixed and then set the TransparentColor
property to the transparent color of your choice.





To set the glyph for a BitBtn, double-click the Value column in the Object Inspector
next to the Glyph property. The Picture Editor will be displayed, and you can choose
the bitmap that will be used for the glyph.





NOTE: The standard button glyphs that come with Delphi are 15 & #165;15 pixels
in size. This size fits well with the standard button height of 25 pixels. Your glyphs
can be any size you like, but the BitBtn component makes no effort to size the button
according to the size of the bitmap. If you use larger glyphs, you will have to size
the button accordingly.





The Kind Property & #160; & #160; The Kind property is a nice feature of the
BitBtn component that enables you to choose from several predefined kinds of buttons.
The default value for the Kind property is bkCustom, which means that you will supply
the glyph and set any other properties for the button. Choosing any of the other
predefined kinds will result in these five events happening:


The Glyph property is automatically set for the kind of button chosen.

The Cancel or Default properties are modified according to the kind of button
chosen.

The Caption property is modified for the type of button chosen.

The ModalResult property is set according to the kind of button chosen.

The button on the form is updated to reflect all these settings.


For example, if you set the value of Kind to bkOK, the button will become an OK
button. The glyph is set to a green check mark, the Cancel property is set to False,
the Default property is set to True, the ModalResult property is set to mrOk, the
Caption property is set to OK, and the results show up on the form. You can always
override any of the properties modified by changing the Kind property, but it is
not usually necessary to do so. Figure 7.7 shows the Button Test program from the
code that comes with this book, with the BitBtn Test form displayed. The form contains
each of the predefined button types available plus one custom button.
The Layout Property & #160; & #160; The Layout property determines where the
button is placed relative to the text. The default is blGlyphLeft. You can also choose
to place the glyph on the face of the button to the right of the text, above the
text, or below the text.
FIGURE 7.7. The
predefined BitBtn types.
The Margin Property & #160; & #160; The Margin property specifies the margin
between the glyph and the edge of the button (which edge this property affects is
determined by the value of the Layout property). The default is -1, which centers
the glyph and the text in the button. Enter any positive value to set an absolute
margin (in pixels).
The NumGlyphs Property & #160; & #160; The NumGlyphs property specifies the
number of glyphs you have in your bitmap strip for a particular button. You can supply
from one to four glyphs, as I've mentioned. The glyphs must appear in the bitmap
strip in this order: up, disabled, down, stay down.
The Spacing Property & #160; & #160; The Spacing property controls the distance
in pixels between the glyph and the button's text. The default value is four pixels.

The SpeedButton Component
The SpeedButton component was designed to be used with the Panel component to
build toolbars. It is different from the Button and BitBtn components in that it
is not a windowed component. This means that a speed button cannot receive input
focus and cannot be tabbed to.
On the other hand, the SpeedButton component has several features in common with
the BitBtn component. The way in which the Glyph property is handled by the SpeedButton
component is exactly the same as with the BitBtn component, so I'm not going to go
over that ground again. There are a couple of major differences, though, so let's
look at those.
By default, speed buttons are square and are 25 & #165;25 pixels. Your speed buttons
can be any size you like and can contain text, although speed buttons don't usually
contain text. There are some properties specific to speed buttons that you should
be aware of, which I've broken down in the following sections.





NOTE: The Delphi 1.0 method of creating toolbars involved using a Panel
component on which various components (SpeedButtons, primarily) were placed. Delphi
4 has the Toolbar component, which is the preferred method of creating a toolbar.
The Toolbar component has some added benefits but is slightly more complicated to
use.





GroupIndex & #160; & #160; Speed buttons can be grouped to make them behave
like radio buttons (radio buttons will be discussed later in this chapter in the
section & quot;Radio Buttons and Check Boxes & quot;). When one button in the group
is pressed, it stays down, and the button that was previously pressed pops up again.
To group speed buttons, simply assign the same value to the GroupIndex property for
all buttons in a group. (The default value of 0 indicates that the button is not
part of any group.) To better understand this, try the following exercise:



1. Create a blank form and place five speed buttons on the form. (I won't
bother adding glyphs to the buttons in this simple exercise, but you certainly can
if you want.)


2. Select all the buttons and change the value of the GroupIndex property
to 1. The GroupIndex for all buttons will be changed to 1.


3. Optional: Change the Down property of one of the buttons to True.


4. Click the Run button to compile and run the program.



When you run the program, click several of the buttons. You will notice that only
one button can be in the down state at one time. As you can see when you assign a
nonzero value to GroupIndex, the speed buttons change their behavior. A speed button
with a GroupIndex of 0 pops back up when you click it, whereas a speed button that
is part of a group stays down when clicked.
AllowAllUp & #160; & #160; By default, one button in the group must be down
at all times. You can change that behavior by setting the AllowAllUp property to
True. Doing this for one button automatically changes the AllowAllUp property for
all other buttons in the group to True as well. Now you can have any one button in
the group selected or no buttons.





TIP: Sometimes you want a speed button to act as a toggle button. A toggle
button is used to turn an option on or off and is not part of a button group. To
make an individual speed button a toggle button, assign a nonzero value to its GroupIndex
property and set its AllowAllUp property to True. Be sure to set the GroupIndex property
to a value not used by any other components on the form. When the user clicks the
button, it stays down. When the button is clicked again, it pops back up.





Down & #160; & #160; The Down property, when read, returns True if the button
is currently down and False if it is not. When written to, the Down property can
be used to toggle a button as pressed or not pressed. Writing to the Down property
has no effect unless the speed button is part of a group.

Radio Buttons and Check Boxes
Although radio buttons and check boxes are specialized buttons, they are, in the
end, still buttons. I'm not going to spend a lot of time discussing these two components
because implementing them is straightforward. Both the RadioButton and CheckBox components
have a property called Checked that can be used to set the check state and can be
read to retrieve the current check state.
The radio button is usually used in a group of buttons. A radio button typically
signifies a group of options, only one of which can be selected at one time (like
a group of speed buttons, which you just learned about). Although you can use a radio
button by itself, it is not recommended because it is confusing to your users. When
tempted to use a radio button by itself, use a check box instead--that's what a check
box is for, after all.
Any radio buttons placed on a form will automatically be considered part of the
same group. If you have more than one group of radio buttons, and those groups need
to operate independent of one another, you need to use a RadioGroup component. This
component enables you to quickly set up a group of radio buttons with a 3D frame
around the buttons and a caption as well. To better understand this concept, try
the following exercise:



1. Create a blank form or use the form you created in the previous exercise.
Place a RadioGroup component on the form (you will find it on the Standard tab).


2. Locate the Items property and double-click the Value column.


3. The String list editor is displayed. Type the following lines in the
String list editor:






Redtailed Hawk
Peregrine Falcon
Gyrfalcon
Northern Goshawk






4. Click OK to close the String list editor. The group box is populated
with radio buttons containing the text you typed.

5. Change the Caption property of the radio group box to Apprentice Falconers
Can Legally Possess:.


6. Click Run to compile and run the program.



When you click one of the radio buttons, the previously selected button pops up
as expected. Using the RadioGroup component, you can put more than one group of radio
buttons on a form. Like the list box and combo box components discussed earlier,
the RadioGroup component has an ItemIndex property that you can read at runtime to
determine which item in the group is selected. You can also set the ItemIndex to
force a particular radio button to be selected. You might have noticed that none
of the radio buttons were selected when the application ran. Change the ItemIndex
to 0 in the Object Inspector and run the program again. This time the first radio
button is selected.
Oh, by the way--if you live in the U.S., the answer to the quiz is Redtailed Hawk
(American Kestrel would also have been an acceptable answer, but it was not presented
in the list).





NOTE: You can also use a GroupBox component to hold radio buttons. The
GroupBox component is less convenient to use than the RadioGroup component, but it
has more flexibility. You can place any type of control in a group box. After they're
placed in the group box, the controls and the group box itself can be moved as a
unit at design time.





The CheckBox component is used to enable users to turn an option on or off or
to indicate to a user that an option is currently on or off. A check box can have
up to three states, depending on its style: on, off, or grayed. If the check box's
AllowGrayed property is False, it can only be checked or unchecked. When the AllowGrayed
property is True, the check box can be any one of the three states. The grayed, or
indeterminate, state is handled programmatically.
In other words, it's up to you to decide what the grayed state means for your
application. If the AllowGrayed property is False (the default), you can use the
Checked property to determine whether the check box is checked or unchecked. If the
AllowGrayed property is True, you must use the State property to determine (or set)
the check box state. State will return cbChecked, cbUnchecked, or cbGrayed.





NOTE: Sometimes you might want to use a check box to indicate that
some feature is on or off but not enable the user to change the state by clicking
on the check box. In that case, you want the check box to be disabled but to appear
normal. To make a check box read-only but not grayed out, place it on a panel and
change the panel's Enabled property to False.






The Label Component
The Label component is used to display text on a form. Sometimes the label text
is determined at design time and never changed. In other cases, the label is dynamic
and is changed at runtime as the program dictates. Use the label's Caption property
to set the label text at runtime. The Label component has no specialized methods
or events beyond what is available with other components. Table 7.8 lists the properties
specific to the Label component.

TABLE 7.8. PROPERTIES FOR THE Label COMPONENT.



Property
Description


AutoSize
When set to True, the label sizes itself according to the text contained in the Caption
property. When set to False, text is clipped at the right edge of the label. Default:
True


FocusControl
A label is a non-windowed component, so it cannot receive input focus and it cannot
be tabbed to. Sometimes, however, a label serves as the text for a control such as
an edit control. In those cases you could assign an accelerator key to the label
(using the ampersand) and then change the FocusControl property to the name of the
control you want to receive focus when the label's accelerator key is pressed.


ShowAccelChar
Set this property to True if you want an actual ampersand to show up in the label
rather than the ampersand serving as the accelerator key. Default: True


Transparent
When this property is set to True, the Color property is ignored and anything beneath
the label shows through. This is useful for placing labels on bitmap backgrounds,
for example. Default: False


WordWrap
When set to True, text in the label will wrap around to a new line when it reaches
the right edge of the label. Default: False








NOTE: The StaticText component (found on the Additional tab) is another
type of label component. This component is different from the regular Label component
in that it is a windowed control (it has a window handle). The StaticText component
is handy when you use a label with an Edit component and you want an accelerator
key to be associated with the component.






The ScrollBar Component
The ScrollBar component represents a stand-alone scrollbar. It's standalone in
the sense that it is not connected to an edit control, list box, form, or anything
else. I have not found that the scrollbar is a control I use very frequently. Certain
types of applications use scrollbars heavily, of course, but for day-in, day-out
applications its use is fairly uncommon.
The scrollbar's performance is set by setting the Min, Max, LargeChange, and SmallChange
properties. The scrollbar's position can be set or obtained via the Position property.
The Kind property enables you to specify a horizontal or vertical scrollbar.

The Panel Component
The Panel component is sort of a workhorse in Delphi. There is almost no limit
to what you can use panels for. Panels can be used to hold toolbar buttons, to display
text labels such as a title for a form, to display graphics, and to hold regular
buttons as well. One of the advantages of a panel is that components placed on the
panel become children of the panel. As such, they go with the panel wherever the
panel goes. This can be a great aid at runtime and at design time.
Much of the power of the Panel component lies in its Align property. For example,
let's say you want a title to be displayed on the top of a form. Let's further assume
that you want it centered no matter how the user sizes the window. By setting the
Align property to alTop and the Alignment property to taCenter, your title will always
be centered. It's as simple as that.
A panel can have many appearances. The panel's appearance can be altered by changing
the BevelInner, BevelOuter, BorderStyle, and BorderWidth properties, as displayed
in Figure 7.8.
FIGURE 7.8. The
Panel Styles Example showing different styles.
The Panel component is so versatile that it will take you a while to discover
all its possible uses.

And That's Not All...
Unfortunately, there isn't sufficient space here to go over all the components
Delphi provides. You saw the Image component on Day 4 when you created the Picture
Viewer program. You also got a brief glimpse at the Bevel component on Day 4 when
you built an About dialog box, and the Shape component on Day 6 as part of an exercise
in aligning components. These represent just a sampling of the components that are
waiting for you. You need to test drive each one of them to determine their usefulness
for you.
There is one other group of components that I need to discuss before you move
on: the Dialog group.

The Common Dialog Boxes
Windows provides a set of common dialog boxes that any Windows program can use,
including the following:


File Open

File Save

File Open Picture

File Save Picture

Font

Color

Print


Printer Setup
Find

Replace


The common dialog boxes are found on the Dialogs tab of the Component palette.
These components are considered nonvisual because they don't have a visual design-time
interface. The following sections discuss each of these dialog boxes with one exception--I'll
leave the discussion of the Print and Printer Setup dialog boxes for Day 13 when
I discuss printing.

The Execute Method
One feature that all the common dialog boxes have in common is the Execute method,
which is used to create and display the dialog box. The dialog box is displayed modally
except for the Find and Replace dialog boxes, which are displayed modelessly. Execute
returns True if the user clicked the OK button, double-clicked a file name (in the
case of the file dialogs), or pressed Enter on the keyboard. Execute returns False
if the user clicked the Cancel button, pressed the Esc key, or closed the dialog
box with the system close box. A common dialog box is often implemented like this:

if OpenDialog.Execute then begin
{ user pressed OK so use the filename }
Memo.Lines.LoadFromFile(OpenDialog.FileName);
{ do some other stuff }
end;

This code displays the File Open dialog box and gets a filename from the user.
If the user clicked the OK button, the code inside the if block is executed and the
file is loaded in to a Memo component. If OK was not pressed, the code inside the
if block is ignored and no action takes place.





NOTE: The code used in the previous snippet is another example of Object
Pascal shortcut syntax. The first line
if OpenDialog.Execute then begin







is equivalent to
if OpenDialog.Execute = True then begin







Use either method, but the first is preferred.






The File Open and File Save Dialog Boxes
The File Open and File Save dialog boxes have several properties in common. File
Open is used when you want to allow the user to open a file in your application (see
Figure 7.9). It is encapsulated in the OpenDialog component. The File Save dialog
box is used when getting a filename from the user in order to save a file. It is
also used as the Save As dialog box. The File Save dialog box is encapsulated by
the SaveDialog component.
FIGURE 7.9. A
typical File Open dialog box.
The file dialog boxes are fairly easy to use in their most basic form. They do
have a few features, however, that need to be explained in order for you to get the
full benefit of using them. The following sections examine the properties that are
specific to the file dialog boxes.





NOTE: The OpenDialog and SaveDialog components merely retrieve a filename
from the user. It is up to the programmer to write code that actually does something
with the filename.






The DefaultExt Property
Use the DefaultExt property to set the default extension that the dialog box will
use. The default extension is the extension that will automatically be appended
to the filename if the user does not supply an extension.

The FileName Property
The FileName property is the most obvious of the file dialog box properties: It
holds the text of the file that the user chose. Set this property prior to calling
the dialog box if you want a filename to show in the edit portion of the file dialog
box when it is initially displayed. After the user clicks OK to close the dialog
box, this property will contain the full path and filename of the file chosen.

The Files Property
Files, a read-only property, is a TStrings instance that contains the list of
files selected when multiple file selection is enabled.

The Filter Property
The Filter property contains a list of the file types from which the user can
choose. The file types are displayed in the File of type: combo box in the file dialog
box. You can set Filter to reflect types of files specific to your application. For
example, a simple text-editing program could have the filter set to show files of
type .TXT, .INI, and .LOG, to name just a few.
The filter can easily be set at design time through the Filter Editor dialog box.
To invoke the Filter Editor, double-click the Value column next to the Filter property
in the Object Inspector. Figure 7.10 shows the Filter Editor for a File Open dialog
box, as described previously.
FIGURE 7.10. The
Filter Editor dialog box.


The Filter Name column contains a textual description of the file type. The Filter
column is the actual file mask that will be used to display files of that type.
Although you can enter the filter string directly in the Value column of the Object
Inspector, it is easiest to use the Filter Editor. If you are using only a single
filter, you can type it directly into the Value column for the Filter property. Separate
the description and filter with a pipe. For example, to have a single filter for
all file types, you would enter the following:

All Files (*.*)|*.*

The FilterIndex Property
The FilterIndex property is used to set the filter that will be used when the
dialog box is initially displayed. The index is not 0-based as you might expect,
however. The first filter in the list is 1, the second is 2, and so on. For example,
refer to Figure 7.10. If you want the All Files filter to be the one initially displayed,
you would set the FilterIndex property to 4.

The InitialDir Property
The InitialDir property is used to specify the directory that will be used as
the initial directory when the file dialog box is displayed. If no value is supplied
for the InitialDir property, the current directory will be used (as determined by
Windows).





TIP: A top-notch Windows program keeps track of the last directory the
user used both when opening files and when saving them. Usually this information
is stored in the Registry. Before displaying a File Open or File Save dialog box,
set the InitialDir to the previous directory the user used. After the user selects
a file, update the Registry to reflect the new directory if necessary.






The Options Property
The Options property controls the way the file dialog box is used. The list of
options is too long to list here, but common items include whether you enable new
files or directories to be created, whether the Help button is shown on the dialog
box, whether long filenames are allowed, whether multiple file selection is allowed,
and others. See the Delphi online help regarding the OpenDialog and SaveDialog components
for complete information.

The Title Property
The Title property is used to set or read the title of the file dialog box. If
no title is specified, the common dialog box defaults of Open for the OpenDialog
component and Save for the SaveDialog component are used.





TIP: A Save As dialog box is nothing more than a SaveDialog component with
the Title property set to Save As.





The file dialog boxes have no events associated with them.





TIP: You can implement a File Open dialog box (or any of the common dialog
boxes) at runtime without ever placing an OpenDialog component on your form. To accomplish
this, create an instance of the TOpenDialog class and then call its Execute method,
as follows:
procedure TForm1.Button1Click(Sender: TObject);
var
OpenDlg : TOpenDialog;
begin
OpenDlg := TOpenDialog.Create(Self);
if OpenDlg.Execute then begin
{ do something here }
end;
OpenDlg.Free;
end;







If necessary, you can set any of the OpenDialog component's properties prior to
calling Execute.






The File Open Picture and File Save Picture Dialog Boxes
These two dialog boxes are nothing more than the regular File Open and File Save
dialog boxes with an extra feature: They display a preview window that enables you
to see the image that is currently selected. These dialog boxes also have the Filter
property pre-set to the common Windows image formats. Otherwise, they behave just
like the File Open and File Save dialog boxes. Figure 7.11 shows a File Open Picture
dialog box in action.

The Color Dialog Box
The Color dialog box enables the user to choose a color. When the OK button is
clicked, the Color property will contain the color information (refer to Figure 7.1
to see the Color dialog box). The Color dialog box, like the file dialog boxes, has
no events to respond to.
FIGURE 7.11. The
File Open Picture dialog box.


The Font Dialog Box
The Font dialog box enables the user to choose a font from the list of fonts available
on his or her system. Through the Device property, you can choose whether you want
screen fonts, printer fonts, or both types of fonts to be displayed. You can limit
the maximum and minimum font sizes that the user can select by modifying the MaxFontSize
and MinFontSize properties. As with the file dialog boxes, the Options property contains
a wide variety of options you can use to control how the Font dialog box functions.
If the user clicks OK, the Font property will contain all the information you
need to implement the new font. Figure 7.12 shows the Font dialog box in the default
configuration.
FIGURE 7.12. The
Font dialog box.





The Font dialog box has one event in addition to the usual events for dialogs.
OnApply will occur when the user clicks the Apply button on the Find dialog box.
The Apply button will not be present on the Font dialog box unless you first create
a valid (not empty) event handler for the OnApply event.

The Find and Replace Dialog Boxes
The Find and Replace dialog boxes provide users the capability to enter text to
search for and text to replace the found text with, and a variety of search and replace
options. The Find dialog box is encapsulated in the VCL component FindDialog, and
the Replace dialog box is represented by the ReplaceDialog component. The Replace
dialog box, which contains everything found on the Find dialog box plus the extra
replace features, is shown in Figure 7.13.
FIGURE 7.13. The
Replace dialog box.
Major properties of the FindDialog and ReplaceDialog components include FindText
(the text to find), ReplaceText (the text with which to replace the found text),
and Options. Obviously, the FindDialog does not have a ReplaceText property. The
Options property contains a wide variety of information about the various options
that the user set at the time the Find Next, Replace, or Replace All button was clicked.
The Execute method for the FindDialog and ReplaceDialog components is a little
different than it is with the other common Dialog components. First of all, the Find
and Replace dialog boxes are modeless dialog boxes. As soon as the dialog box is
displayed, the Execute method returns.
Because the dialog box is modeless, the return value from Execute is meaningless
(it's always True). Instead, the Find and Replace dialog boxes use the OnFind and
OnReplace events along with the Options property to determine what is happening with
the dialog box. The OnFind event occurs when the Find Next button is clicked. The
ReplaceDialog has an OnFind event, but it also has an OnReplace event that is fired
when the Replace or Replace All button is clicked. Use these events to determine
when the user has requested a find or replace action. Your programs should read the
Options property to determine how the user intended the find or replace operation
to be carried out.

Summary
Today you have had a look at some of the basic components that Delphi provides.
You have learned about components in general, and then you learned about some of
the specifics of the components that are based on Windows controls. It is important
to understand the basic controls available in Windows and the Delphi components that
represent those controls. Finally, you examined some of the Windows common dialog
boxes.

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 answers to the quiz questions in Appendix A, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A



Q If I change the Name property of a component using the Object Inspector,
Delphi will automatically change all references to that component in my code, right?


A Yes and no. Delphi will change all references to that component name
in Delphi-generated code, but it will not change any user-written code.


Q The OpenDialog component is obviously a visible component. Why is it called
a nonvisual component?


A Because it is not visible at design time. It is visible only at runtime
when you invoke it with the Execute method.


Q Why is it important to change the Name property only with the Object Inspector?


A As you work with the Form Designer, Delphi writes code based on the
Name property. If you later change the Name property either by directly editing the
source files or at runtime, all references to that form or component will be incorrect
and will likely lead to your program refusing to compile or crashing at runtime.


Q I seem to be using properties more than methods when dealing with my components
in code. Is that wrong?


A Not at all. In fact, that's the way VCL components are designed. A well-written
component makes maximum use of properties. For this reason you cannot use a component's
methods very often. Use methods when necessary, but otherwise use properties to manipulate
your components at runtime.


Q I'm responding to both the OnDblClick and the OnClick events for a component.
Every time I double-click a component, both the OnClick and the OnDblClick event
handlers get called. Why?


A Because when you double-click a component, Windows will generate both
single- and double-click messages. You can't prevent it, so you have to write code
to account for that fact.


Q I want to use the features of the TStrings class to keep a list of strings
my program needs in order to operate. The compiler won't let me use a TStrings object.
What do I do?


A Use a TStringList object instead. The TStringList class is provided
for this purpose.


Q I need a single-line edit control to be right-justified, but there is no
Alignment property for the Edit component. Can I right-align text in a single-line
edit?


A No. What you can do, though, is use a Memo component and make it appear
to be a regular Edit component. Be sure to set the Memo component's WantReturn property
to False, its Height to the height of a standard edit component (21 pixels), and
its Alignment property to taRightJustify. The component will give all appearances
of being a single-line edit control that is right-justified.


Q I have a form that has several buttons on it. When the user closes the form
using the Esc key I get one return value from ShowModal, and when the user closes
the form with the system close box I get a different return value from ShowModal.
Why?


A You have a button on the form that has a Cancel property set to True.
When the user presses the Esc key, the ModalResult value of that button is used as
the return value from ShowModal. When the user closes the form with the system close
box, you will always get a return value of mrCancel. You need to be prepared to take
into account both ways that a form can be closed.



Quiz



1. Can you change the Name property of a component at runtime?


2. What property is used to enable and disable controls?


3. How can you tell at runtime that a button is disabled?


4. What is the difference between the long hint and the short hint?


5. Name three of the four methods that can be used to tell a control to
repaint itself.


6. How many types of combo boxes are there?


7. How is the ModalResult property used for button components?


8. What component is often used as a container for other components?


9. What is the return value from the Execute method for an OpenDialog
component if the user clicks OK to close the dialog box?


10. How do you make the SaveDialog component into a Save As dialog box?



Exercises



1. Create a program that contains two edit components. When the user types
information in the first control, make it appear in the second edit control as it
is entered.


2. Create a program with a list box. Write code to load the list box from
a text file prior to the application being visible.


3. Add an edit component to the program in Exercise 2. When the user selects
an item in the list box, have the item's text appear in the edit control.


4. Add a button to the program in Exercises 2 and 3. Write code so that
when the button is clicked, any text in the edit control is added as a new item in
the list box.


5. Create a program that has a RadioGroup with four items in the group.
Add a label component whose text changes depend on which radio button is clicked.


6. Create a program that has a title on the form that is centered at the
top of the form regardless of how the program's window is sized.


7. Modify the program in Exercise 6 so that the font of the title can
be changed to any font available on the system by clicking a button.


8. Reopen the Picture Viewer program created on Day 4. Modify the program
so that it uses File Open Picture and File Save Picture dialog boxes rather than
the regular dialog boxes.



In Review
You covered a lot of ground this week. In some ways this was the toughest week.
Object Pascal, while not as daunting as some other programming languages, still takes
some time to learn. But there is no doubt that you can learn to program in Delphi
if you stay with it. Don't forget to take a break now and then. This book is titled
Sams Teach Yourself Delphi 4 in 21 Days , but that doesn't mean that they have
to be consecutive days! Sometimes it's good to take a few days off to let it all
soak in.
If you are confused by some of the Object Pascal syntax, don't feel you are alone.
It all can be confusing at first. Don't worry, though, because you will start to
get the hang of it before long. As you work with Delphi, little by little it begins
to make sense. What you probably lack at this point is real-world experience. That
is where you really learn. Knowledge gained by experience is the kind that sticks.
My advice is to take an idea and turn it into a working program. You can't necessarily
do that at this point, but you can get a good start. The program doesn't have to
be as advanced as Microsoft Word, Netscape Navigator, or a game like DOOM, mind you.
It just needs to be a little something to help you tie your education in with some
experience.
The first part of this week, you worked on Object Pascal language keywords and
syntax. Items such as loops and if statements are fairly easy to comprehend. Don't
be concerned, though, if you have to go back and look up the syntax once in a while.
There is a lot to learn, and you aren't expected to memorize every keyword and its
syntax. Later on you will be, but at this stage of the game, it isn't expected.
On Day 3, you were introduced to Object Pascal classes. Classes are a big part
of Object Pascal and programming in Delphi. Sometimes it takes a while to perceive
where classes can be used in your programs. For a long time, you might deal only
with the classes that the Visual Component Library (VCL) provides and not write any
classes of your own. Later on you will probably find situations where a class will
fit perfectly with a particular task you have to accomplish. When that time comes,
you will be ready to tackle writing your own class. After you've written one or two,
you will be off and running.
On Day 4, you learned about the IDE: how to customize it to your liking, how the
Component Palette works, what the Object Inspector is for, and so on. This part of
the week you experienced the fun stuff. It's okay to use the word fun . I find
all kinds of programming a great deal of fun. That's why I do it. I hope that you
find it fun, too.
On Day 5, I gave you an introduction to class libraries, also known as frameworks .
VCL is a framework. A framework makes your life easier by encapsulating difficult
Windows programming tasks into classes that you can deal with on a more rational
level. Believe me, sometimes the raw Windows API seems to be anything but rational.
VCL takes care of dealing with those issues for you and provides you with a higher
level of programming objects that you can easily incorporate in your applications.
No, VCL is not easy, but it is much easier than dealing with the API. As part of
the discussion on frameworks, you were introduced to the component model. You learned
about properties, methods, and events and how you use them to build Windows programs
in Delphi.
On Day 6, you learned about the Form Designer. The Form Designer is where the
bulk of your Delphi applications will be designed--the graphical part of the application,
anyway. Working with the Form Designer can be fun, too. Using the Form Designer,
you can create great-looking forms. Remember, a form represents a window in your
applications. Most applications have a main window and several dialog boxes that
are displayed, based on user interaction with the program. On Day 6, you also created
a simple but useful program. This program, ScratchPad, gave you a start in building
an application with Delphi. ScratchPad will be used throughout the book. As you build
your programming knowledge, you will add new features to ScratchPad to give you practice
with the techniques presented. If you are developing an application of your own,
I encourage you to add new features to your program as you learn about them.
On Day 7, you learned about some of the VCL components available to you. I didn't
cover all the VCL components, but I covered the components that are most commonly
used in Windows programming with Delphi. You will be introduced to other VCL components
as you work through the rest of the book.
I hope this week hasn't left you too worn out. If it has, take a short break and
then jump right back into the game. If you found this week exhilarating and energizing,
just keep on turnin' those pages. I'm ready if you are.











& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > ch08.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 8 -- Creating Applications in Delphi








Teach Yourself Borland Delphi 4 in 21 Days







- 8 -


Creating Applications in Delphi



Working with the Object Repository

Object Repository Pages and Options
The Copy Button
Creating New Objects from the Object Repository
Adding Objects to the Object Repository
Adding Projects to the Object Repository
Object Repository Housekeeping

Building Forms and Applications with the Wizards

Using the Dialog Wizard
Creating Applications with the Application Wizard

Adding Methods and Data Fields to Code
How Delphi Manages Class Declarations
Adding a Method to Your Code

Adding a Class Data Field
Deleting Delphi-Generated Code

Creating Component Templates
Using Resource Files

Resources in Delphi
Compiling Resource Files
Linking Resource Files to Your Executable
A Sample Program Using Resources

Using Packages

What's a Package?
Static Linking Versus Dynamic Linking
Using Runtime Packages in Your Applications
Deploying Applications Using Packages

Summary
Workshop

Q & amp;A
Quiz
Exercises









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


The Object Repository

The Dialog Wizard

The Application Wizard

Adding methods and data fields to your code

Component templates

Using resources in your Delphi applications

Packages


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:


Choose a predefined application, form, or dialog box to implement in your application.

Add your own forms, dialog boxes, and applications to the Object Repository.

Add other objects to your application such as ASCII text files and additional
source code units.


Manage data modules.

Create new components.

Create new packages.

Create new ActiveX controls or ActiveForms.

Invoke wizards to help you build a dialog box or an application.


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 & quot;Borland & quot; 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:


The default form that is used when you choose File | New Form from the main menu.

The default form that is used as the main form when you choose File | New Application
from the main menu.


The default project that is used when you choose File | New Application from
the main menu.


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, & quot;The Delphi IDE Explored. & quot;) 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, & quot;Building
Database Forms & quot;) is used to create database forms, and the ActiveX Control Wizard
(discussed on Day 15, & quot;COM and ActiveX & quot;) 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, & quot;Working with the Form Designer and the Menu Designer, & quot; 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,
& quot;Don't go there. & quot; 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


Accelerators

Bitmaps

Cursors

Dialog boxes

Icons

Menus

Data tables

String tables

Version information

User-defined specialty resources (sound and video files, for example)







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 resources can be accessed more quickly because it takes less time to locate
a resource in the executable file than it does to load it from a disk file.
The program file and resources can be contained in a single unit (the .exe file)
without the need for a lot of supporting files.


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,
& quot;Delphi Tools and Options. & quot;)
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:


Compile the resource file manually from the command line.

Add a batch file target to your project group.


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, & quot;Up & quot;
102, & quot;Down & quot;
END
ID_WAVEUP WAVE & quot;up.wav & quot;
ID_WAVEDOWN WAVE & quot;down.wav & quot;
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, & quot;Graphics and Multimedia Programming & quot;). 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:


The first advantage is that you don't have to worry about shipping any additional
files with your application. Your application contains all the code it needs to run
and no runtime libraries are required.

The second advantage is that an application that is statically linked is generally
smaller in total size than an application that requires packages. I'll talk about
this more in just a bit when I talk about the advantages and disadvantages of 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 & #165; 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, & quot;So should I use static linking or dynamic linking? & quot;
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 & gt; 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, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;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 & quot;borrow & quot; bitmaps and other resources from
other programs. Is this okay?


A The short answer is, & quot;No. & quot; 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 & quot;I will not borrow bitmaps from other programs. & quot; 100
times on the blackboard.








& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > ch06.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 6 -- Working with the Form Designer and the Menu Designer








Teach Yourself Borland Delphi 4 in 21 Days







- 6 -


Working with the Form Designer and the Menu Designer



Working with the Form Designer

The Form Designer's Context Menu
Placing Components
The Form Designer Grid
Selecting Components
Moving Components
Preventing Components from Being Moved or Sized
Ordering, Cutting, Copying, and Pasting Components
Sizing Components
Aligning Components
Setting the Tab Order

Building an Example Application

Step 1: Starting a New Application
Step 2: Adding a Toolbar
Step 3: Adding a Status Bar
Step 4: Adding the Memo Component
Running the Program

May I See a Menu, Please?

Creating a Main Menu
Writing the Code
And Now, the Moment You've All Been Waiting For...
Pop-Up Menus (Context Menus)
Creating and Saving Menu Templates

Summary
Workshop

Q & amp;A
Quiz

Exercises








As you know by now, Delphi is heavily form-based, a model that takes maximum advantage
of the visual programming environment. In this chapter you will explore


The Form Designer

The Menu Designer


To illustrate the use of the Form Designer, you will build an application that
approximates the Windows Notepad program. Along the way you will gain valuable experience
working with the Form Designer. Later in the chapter, you'll explore the Menu Designer
in detail.
This chapter might seem elementary if you have used Delphi extensively. Even so,
be sure to take a quick look to discover things previously unknown or to rediscover
things you've forgotten. I'm willing to bet there is at least one thing in this chapter
that will be new to you.

Working with the Form Designer
The Delphi Form Designer is a powerful visual programming tool. It enables you
to place, select, move, resize, and align components and much more. The Form Designer
also enables you to size and position the form itself, add menus, and create specialized
dialog boxes--everything you need to create the user interface to a typical Windows
program.
I'll examine each Form Designer feature in the following sections. As you read,
I encourage you to stop and experiment any time you are curious about how something
works. Sometimes a few minutes playing around can teach you a technique that you
will use for a long time to come.

The Form Designer's Context Menu
When you first start Delphi or when you create a new project, you are presented
with a blank form in the Form Designer. The Form Designer, like most Delphi windows,
has a context menu associated with it. Table 6.1 lists and describes each item on
the Form Designer context menu.

TABLE 6.1. THE FORM DESIGNER'S CONTEXT MENU ITEMS.



Item
Description


Align to Grid
Aligns selected components to the Form Designer grid.


Bring to Front
Brings selected components to the front of all other components.


Send to Back
Sends selected components behind all other components.


Revert to Inherited
Causes the selected control to revert to its original state when you are working
with a form that you have inherited from the Object Repository. (Inheriting forms
from the Object Repository is covered on Day 8, & quot;Creating Applications in Delphi. & quot;)


Align
Displays the Alignment dialog box.


Size
Displays the Size dialog box.


Scale
Displays the Scale dialog box.


Tab Order
Displays the Edit Tab Order dialog box.


Creation Order
Displays the Creation Order dialog box.


Flip Children
For non-English versions of Windows, this command changes the reading order of a
component. Disabled for English versions of Windows.


Add to Repository
Adds this form to the Object Repository. Custom forms can be saved to be used later.
(The Object Repository is discussed on Day 8.)


View as Text
Shows the form description as text in the Code Editor. You can edit the form's text
version if you like. Choose View as Form from the Code Editor context menu to go
back to the form. You can also use Alt+F12 to switch from the View as Text and View
as Form options.








NOTE: Delphi creates a form file (DFM) for every form you create and places
it in your project's directory. The form file is a binary resource file that can't
be read by mere humans. When you choose the View as Text context menu item, Delphi
converts the binary resource to readable form. When you switch back to the View as
Form option, Delphi recompiles the form file to implement any changes you have made.






Most of the context menu options are discussed in the following sections. Others
are discussed in later chapters when you examine the particular aspect of Delphi
to which they pertain.

Placing Components
Placing a component on a form is simple. All you have to do is select the component
you want from the Component palette and click on the form to place the component.
When you click on the form, the component's upper-left corner is placed at the location
you clicked. Notice that when you click a button on the Component palette, the button
appears as pressed. When you click on the form to place the component, the button
on the Component palette pops up again to indicate that the action is complete.





TIP: As you learned on Day 4, & quot;The Delphi IDE Explored, & quot; to place
a component on a form multiple times, press and hold Shift when you first select
the component's button on the Component palette. Each time you click on the form,
a new component will be added. Click the Arrow button on the Component palette to
stop placing components.





Most components can be sized. You can place a component on a form and then size
it, or you can size the component at the same time you place it on the form. To size
while placing the component, click on the form where you want the top-left corner
to be placed and then drag with the mouse until the component is the desired size.
When you release the mouse, the component will be placed at the size you specified.





NOTE: Not all components can be sized in this manner. Nonvisual components,
for example, are represented on the form by an icon. Although you can click and drag
to place a nonvisual component, the drag size will be ignored. Another example is
a single-line edit component. The edit component can be placed by dragging, but only
the drag width will be used. The drag height will be ignored because the height of
a single-line edit component defaults to the height of a single-line edit control.





CAUTION: If you change your mind while placing the control via the dragging
method, you can press the Esc key on the keyboard before you release the mouse button
to cancel the operation. The component's button will still be pressed on the Component
palette, however, so you might need to click the Arrow button to return to component-selection
mode.





Placing components is simple enough that you don't need to spend much time on
the subject. You had some experience with placing components yesterday, so let's
move on to other things.

The Form Designer Grid
The Form Designer has a built-in grid that aids in designing forms. By default,
Delphi shows the grid. The grid size is initially set to eight pixels horizontally
and eight pixels vertically. When the Form Designer is set to display the grid, a
dot is placed at the intersection of each grid point. Components placed on a form
will snap to the nearest grid point. By snap to I mean that the component's
top-left corner will automatically jump to the nearest grid point. This is an advantage
because you frequently want a group of controls to be aligned either on their left,
right, top, or bottom edge. When the Snap to Grid option is on, you merely get close
enough to the correct location and the Form Designer automatically places your component
at the nearest grid point. This saves you time by sparing you from tweaking the individual
component's size or position on the form.
The grid settings can be modified via the Preferences page of the Environment
Options dialog box. (I'll discuss the Environment Options in detail on Day 9, & quot;Projects,
the Code Editor, and the Code Explorer. & quot;) From here you can change the grid
size or turn off the Snap to Grid feature. You can also turn the grid display on
or off. When the grid display is off, the grid is still active (assuming Snap to
Grid is on), but the dots marking grid points are not drawn on the form.

Selecting Components
After you place a component on a form, you often have to select the component
in order to modify it in some way. You might have to select a component to perform
one of the following actions:


Move the component.


Change the component's properties.

Align the component.

Size the component.

Cut or copy the component to the Clipboard.

Order the component (bring to front or send to back).

Delete the component.


Selecting Individual Components
To select a single component, just click on it. When you select the component,
eight black sizing handles appear around the component to indicate that it is selected.
(I'll discuss the sizing handles in a moment.) Figure 6.1 shows a form with a button
component selected.
As soon as you select a component, the Object Inspector changes to show the properties
and events for the control you selected. To deselect a control, click on the form's
background or Shift+click on the control. (Shift+click is described in the next section.)





NOTE: Each component has a default event handler associated with it. When
you double-click a component on a form, the Code Editor displays the default event
handler for that component, ready for you to type code. In most cases, the default
event handler is the OnClick handler. Exactly what happens when the component is
double-clicked depends on how the component is designed. For example, in the case
of the Image component, double-clicking will display the Picture Editor dialog box.






FIGURE 6.1. A
form with a button component selected.


Selecting a Group of Components
You can also select multiple components so that you can act on them as a group.
This is accomplished in one of three ways:


Shift+click with the keyboard and mouse.

Drag with the mouse.

Choose Edit | Select All from the main menu.


To select all components on the form, choose Edit | Select All from the main menu.

Selecting Components with Shift+Click
To use the Shift+click sequence, first select one control. Then press and hold
the Shift key on the keyboard and click on any other controls you want to include
in the selection. Each control you click is bounded by four gray boxes to indicate
that it is part of the selection.
You can remove a control from the selection by continuing to hold the Shift key
and again clicking on the component. In other words, the Shift+click sequence toggles
a component's inclusion in the selection. To illustrate, first start with a blank
form and then perform the following steps:



1. Place three button components anywhere on the form. They will automatically
be labeled Button1, Button2, and Button3.


2. Click Button1. The black sizing rectangles appear around the component.


3. Press and hold the Shift key on the keyboard. Click Button2. It is
added to the selection. Gray boxes now appear at the corners of both Button1 and
Button2.


4. Shift+click on Button3. Now all three buttons are part of the selection.


5. Shift+click again on Button2. Button2 is removed from the selection
(the gray boxes disappear), but Button1 and Button3 are still included in the selection.


6. Shift+click on Button1. Now Button3 is the only component in the selection.
The gray boxes are replaced with the black sizing rectangles.


7. Shift+click on Button1 and Button2. All three buttons are now part
of the selection again.



Figure 6.2 shows the form as it will look at the end of this sequence. Keep in
mind that your buttons could have been placed anywhere on the form. Keep the form
handy because you'll use it again in the next exercise.
FIGURE 6.2. A
form with three buttons selected.





NOTE: If you click on a component that is part of a selection, nothing
will happen. To select a single control that is currently part of a group selection,
you need to first click on the form's background or press the Esc key to remove the
group selection. Then you can click on the individual control you want to select.







Selecting Multiple Components by Dragging
You can select multiple controls by dragging a bounding rectangle around the controls
to be selected. The bounding rectangle is a broken-line rectangle that changes
size as you drag. In fact, you don't have to drag the bounding rectangle completely
around the components. You have only to touch a component with the bounding rectangle
in order for it to be included in the selection.
Be sure that you start by placing the mouse cursor over the form's background
and not on a component. Hold the left mouse button down and begin dragging. You will
see the bounding rectangle as you drag. Surround or touch the components you want
selected and release the mouse button. Any components that were inside the bounding
rectangle (or touching it) are included in the selection.
When you have selected a group of controls, you can use the Shift+click technique
explained in the previous section to add other controls to the selection or to remove
controls from the selection. For example, you might want to select all controls in
one area of your form except for one. Surround the controls and then deselect the
control you want to exclude from the selection.
Go back to the form with the three buttons you created earlier (if you've already
discarded that form, create a new one and place three buttons on it). Start at the
top-left corner and drag down and to the right to surround the buttons. Let go of
the mouse button and the controls will be selected. Figure 6.3 shows the form and
the bounding rectangle being dragged.
FIGURE 6.3. Controls
being selected by dragging.
CAUTION: You can use Shift+drag to select non-adjacent groups of controls.
If, for instance, you have two separate groups of controls in different areas on
your form, you can drag around the first set and then hold the Shift key down and
drag around the second set. Both groups will be selected.






NOTE: You don't have to drag down and to the right. You can drag in any
direction to select components.






Selecting Multiple Items: Components within Components
Frequently you will have components placed within other components. The Panel
component is often used as a container for other components. To select a group of
components on a panel, you have to hold down the Ctrl key on the keyboard while you
drag to select the components. (Try it without holding down the Ctrl key and see
what happens!) In case you're wondering, yes, you can use a combination of Ctrl+Shift+drag.
(I suppose the Borland designers could have figured out some way of working the Alt
key in there, too.)
To illustrate, first start with a blank form. Then do the following:



1. Select a Panel component from the Component palette and place it on
the form using the drag method. Drag it so that it occupies most of the form.


2. Now select a Button component and place six buttons on the form. Your
form will look something like Figure 6.4.



FIGURE 6.4. The
form with a panel and six buttons.





3. Drag a bounding rectangle around Button1, Button2, and Button3. Notice
that you moved the panel, which is not what you expected (and not what you wanted).
Move the panel back to where it was.


4. Hold down the Ctrl key and drag a rectangle around Button1, Button2,
and Button3. The buttons are selected.


5. Now hold down both the Ctrl and Shift keys and drag the bounding rectangle
around Button5 and Button6. Now all buttons are selected except Button4.



Using the Ctrl+drag sequence is the only way to select a group of components contained
within another component if you are using the drag method. You can use the Shift+click
method to select components contained within another component just as you do when
selecting components on a form.

Moving Components
Moving components is a common and simple task. To move an individual component,
place the mouse cursor over the component and drag. As you drag, a rectangle that
represents the component moves with the mouse cursor. When you have the rectangle
where you want it, let go of the mouse button and the component will be moved to
that location.





NOTE: When you move a control via drag and drop, the control's Left and
Top properties are automatically updated. If you pause for a moment while moving
a component, you will notice a tooltip appear next to the mouse cursor. The tooltip
shows what the new top and left position of the component will be when you stop dragging.





A similar tooltip is displayed when sizing a component by dragging. In the case of
sizing a component, the tooltip shows what the new width and height of the component
will be when you stop dragging. Figure 6.6 shows the tooltip you see when sizing
a component (in the lower-right corner).





If you have the Snap to Grid option on, the dragging rectangle will snap to the
nearest grid point as you drag.





TIP: If you change your mind while dragging, you can press the Esc key
on the keyboard before you release the mouse button to cancel the drag operation.
The component will return to its original position.





Dragging a group of controls works the same way. After you have a group of components
selected, place the mouse cursor over any one of the controls and begin dragging.
The dragging rectangle will be displayed for each control in the group. This enables
you to visualize where the group will be placed when you release the mouse button.





NOTE: You cannot move a group of components if components in the group
have different parent controls. For instance, let's say you select both a Button
component on the main form and a SpeedButton on a panel. Because these two components
have different parent controls, you cannot move them as a group.





TIP: When you have selected a control, you can nudge it a pixel at a time
by holding down the Ctrl key while using the arrow keys on the keyboard. This technique
works for both groups of controls and individual controls. The Snap to Grid feature
is overridden when you use this technique.
After you have moved a component using this method, the component is no longer on
a grid point--it is offset by some amount. If you now drag the component, it will
maintain its offset from the grid point as you drag.
TIP: If you move a control using the Ctrl+arrow method and want to align
it again to the grid, choose Edit | Align to Grid from the main menu. The control's
top-left corner will snap to the nearest grid point.





A control cannot be dragged outside its parent form. If you drag a component off
the form's left or top edge, you will see that the component is clipped at the form's
edge. If, however, you drag the component off the right or bottom of the form and
drop it, scrollbars will appear on the form so that you can scroll to see the rest
of the form.
The form's Width and Height properties are not altered. If you drag the component
back onto the visible part of the form, the scrollbars disappear again. This is the
default behavior and will occur unless you change the form's AutoScroll property
to False. Figure 6.5 shows a Memo component that has been dragged partially off the
form's right edge. Notice the scrollbar that appears at the bottom of the form.

Preventing Components from Being Moved or Sized
Components can be locked into place so that they cannot be moved. Locking components
is useful if you know that a form's design is final and you don't want to worry about
accidentally moving controls. To lock a form's controls, choose Edit | Lock Controls
from the main menu. Locked controls cannot be moved or sized. When controls are locked,
their sizing handles are gray with a black border. To unlock the controls, choose
Edit | Lock Controls again. The controls can now be moved as before. You can lock
all components on a form with this technique or none of the components. You cannot,
however, lock just selected components.
FIGURE 6.5. A
form with AutoScroll in action.

Ordering, Cutting, Copying, and Pasting Components
Sometimes you will place components on top of one another to achieve a visual
effect. For example, a shadowed box can be created by placing a white box over a
black box (both would be Shape components). Obviously you can't have the shadow on
top of the box, so you have to order the controls to tell Delphi which controls go
on top and which go on the bottom. Let's do a simple exercise that illustrates this.
Along the way, you will also see how you can use Copy and Paste with components.
First, start with a blank form (you know the drill by now). Now follow these steps:



1. Click on the Additional tab on the Component palette and choose the
Shape component. Click on the form to place the shape. A white square appears on
the form.


2. Size the shape as desired (mine ended up being 209 pixels by 129 pixels).


3. Be sure the Shape component is selected. Choose Edit | Copy from the
main menu.


4. Choose Edit | Paste from the main menu. A copy of the shape is placed
below and to the right of the original shape. Conveniently, this is exactly where
you want it.








NOTE: After a paste operation, the component just pasted will be selected.









5. Double-click the Brush property in the Object Inspector and change
its Color property to clBlack. The new shape is now black, but it is on top of the
original shape. Can't have that!


6. Click the secondary mouse button and choose Send to Back from the context
menu (you can also choose Edit | Send to Back from the main menu). The black shape
is moved behind the white shape. You now have a box with a shadow. (As an alternative,
you could have clicked on the white shape and used Bring to Front to move it on top
of the black shape.)



This exercise illustrates two features of the Form Designer. It shows how you
can change the stacking order of controls and how you can use Copy and Paste to copy
components. The original component's properties are copied exactly and pasted in
as part of the pasting process. Each time you paste a component, it is placed below
and to the right of the previous component you pasted.





NOTE: If a component that can serve as a container is selected when you
perform Paste, the component in the Clipboard will be pasted as the container component's
child. For example, you might want to move a button from the main form to a panel.
You can select the button and choose Edit | Cut from the main menu to remove the
button from the form and place it in the Clipboard. Then you can select the panel
and choose Edit | Paste from the main menu to paste the button onto the panel.





I don't need to go into much detail on the cut operation. When you cut a component,
the component disappears from the form and is placed in the Clipboard. Later, you
can paste the component onto the form or onto another component such as a Panel component.





TIP: You can also copy a component and paste it into your source code.
The results will be something like this:
object Edit1: TEdit
Left = 24
Top = 16
Width = 457
Height = 21
TabOrder = 0
Text = `Edit1'

end







This is not code that will compile, but this technique will give you a component's
size and position as it appears on the form. This information comes in handy when
you create components on-the-fly at runtime rather than at design time. You can place
a dummy component visually on the form, get its size and position using Copy and
Paste, and then delete the component. Then you can write code to create the component
at runtime and know that it will be properly sized and positioned.






Sizing Components
With some components, you drop them on a form and accept the default size. Buttons
are a good example. A standard button has a height of 25 pixels and a width of 75
pixels. For many situations, the default button size is exactly what you want. With
some components, however, the default size is rarely exactly what you need. A Memo
component, for example, nearly always has to be sized to fit the specific form on
which you are working.

Sizing by Dragging
When you select a control, eight black sizing handles appear around the control.
When you place the mouse cursor over one of the sizing handles, the cursor changes
to a double-headed arrow known as the sizing cursor . When you see the sizing
cursor, you can begin dragging to size the control. How the component is sized depends
on which sizing handle you grab.
The sizing handles centered at the top and bottom of the component size it vertically
(taller or shorter). Likewise, the right and left sizing handles size the component
horizontally (wider or narrower). If you grab one of the sizing handles in the component's
corners, you can size both horizontally and vertically at the same time. As with
moving a component, a sizing rectangle appears as you drag. When the sizing rectangle
is the desired size, let go of the mouse button and the component will be resized.
Figure 6.6 illustrates a memo component being sized by dragging; Figure 6.7 shows
the form after the drag operation.





NOTE: Sizing applies to visual components only. Nonvisual components appear
on the form as an icon that cannot be sized. The sizing handles appear on nonvisual
components and the handles can be dragged, but the result of the dragging operation
will be ignored.





FIGURE 6.6. A
memo component being sized.
FIGURE 6.7. The
form after sizing the memo component.
Groups of controls cannot be sized by dragging. The sizing handles (black squares)
are replaced by selection indicators (gray squares) when you select more than one
component.





TIP: To size all the components in a group at one time, modify the Width
or Height property in the Object Inspector or use the Size dialog box (the Size dialog
box is discussed in the next section). All components in the selection will take
on the new values.




TIP: To size a control or group of controls by one pixel at a time, hold
down the Shift key and press any arrow key on the keyboard. The up and down arrows
size the control vertically and the right and left arrows size it horizontally. Only
the component's Width and Height properties are affected. The Top and Left properties
are not modified.






Sizing with the Size Dialog Box
Another sizing option is the Size dialog box. You can bring up the Size dialog
box by choosing Edit | Size from the main menu. Figure 6.8 shows the Size dialog
box.
FIGURE 6.8. The
Size dialog box.


The Size dialog box is used when you want to force a group of controls to the
same width or height. For instance, let's say you have six edit components on a form,
all with different widths. To make the form appear more balanced, you might want
to make all the edit components the same width. First, select the components and
then invoke the Size dialog box. From there you can choose Shrink to smallest (in
the Width column) to make all the components the width of the shortest edit component,
or Grow to largest to make all the components the width of the longest component
in the group. You can also enter an exact width in the Width box, in which case you
would leave the Height set on No change. When you click OK, the components will all
be the same width.





TIP: The Size dialog box can also be invoked from the Form Designer context
menu.






Sizing with the Scale Dialog Box
Another sizing tool is the Scale dialog box, shown in Figure 6.9. This dialog
box enables you to specify a scaling percentage. To make the components twice as
large, enter 200 in the Scaling factor box. To reduce the components' size by half,
enter 50 in the Scaling factor box. The Scale dialog box is convenient for quickly
changing the size of all the form's components. You can bring up the Scale dialog
box by choosing Edit | Scale from the main menu or Scale from the Form Designer context
menu.
A control can also be sized and moved by using the various Alignment options.
Let's take a look at those in the next section.
FIGURE 6.9. The
Scale dialog box.






NOTE: Remember, components can always be moved by modifying their Left
and Top properties and sized by modifying their Width and Height properties in the
Object Inspector.






Aligning Components
Regardless of whether you have the Snap to Grid option turned on, you sometimes
need to align components after placing them. Aligning components could mean aligning
several components along a common edge, centering components on the form, or spacing
components. There are two ways to align components:


Use the Alignment palette and Alignment dialog box.

Modify a component's Align property.


The following sections explain these two methods.





NOTE: You might have noticed the Alignment property for some components.
This property pertains only to the way the component's text is aligned (centered,
right-justified, or left-justified) and has nothing to do with aligning components
on a form.






The Alignment Palette and the Alignment Dialog Box
It is often necessary to move or size components relative to the form or relative
to one another. The Alignment palette contains several buttons that aid in that task.
The Alignment dialog box performs the same operations as the Alignment palette, but
in a different format. To display the Alignment palette, choose View | Alignment
Palette from the main menu. Figure 6.10 shows the Alignment palette and describes
each button. If you pause the mouse cursor over a button on the Alignment palette,
a tooltip describing the button will appear.
FIGURE 6.10. The
Alignment palette.






TIP: The Alignment palette can save you a lot of work. Don't spend too
much time trying to get controls to line up exactly. Place the components on the
form and then use the Alignment palette to position them.





The Align Left Edges button is used to line up components on their left edges.
Start with a blank form and then do the following:



1. Place five button components vertically on the form without regard
to their left edges.


2. Select the buttons by dragging a bounding rectangle around them. The
selection indicators show that all the buttons are selected. The form will look something
like the one in Figure 6.11.



FIGURE 6.11. The
form with the buttons randomly placed.





3. Choose View | Alignment Palette from the main menu. The Alignment palette
is displayed. Move the Alignment palette, if necessary, so that it doesn't obscure
the form.


4. Click the Align Left Edges button on the Alignment palette. The buttons
are all lined up.



See how easy that is? As long as you have the buttons selected, let's look at
another alignment option. The Space Equally Vertically alignment option can now be
used to space the buttons evenly. The buttons should still be selected, so all you
have to do is click the Space Equally Vertically button on the Alignment palette,
and voil & #224;! The buttons are perfectly spaced. The form will now look like Figure
6.12.
FIGURE 6.12. The
form with the buttons aligned and equally spaced.





NOTE: The Space Equally Vertically alignment option spaces the components
equally between the first component in the column (the top component) and the last
component in the column (the bottom component). Be sure to set the first and last
components where you want them before choosing the Space Equally Vertically alignment
option. This is true of the Space Equally Horizontally alignment option as well.







The Center Horizontally in Window and Center Vertically in Window alignment options
do exactly as their names indicate. These options are convenient for centering a
single control on the form or for centering a group of controls. As long as you still
have the group of buttons selected, click both the Center Horizontally in Window
and Center Vertically in Window buttons on the Alignment palette. The buttons will
be centered on the form both horizontally and vertically.





NOTE: When you select a group of controls and click one of the centering
buttons, the controls will be treated as a group . If you choose each control
individually and center it both horizontally and vertically on the form, all the
controls will be stacked on top of one another in the middle of the form. By selecting
the group and then centering, you get the entire group centered as you intended.






The form will now look like the one shown in Figure 6.13.
FIGURE 6.13. The
form with the buttons centered.






NOTE: The Center Horizontally in Window and Center Vertically in Window
alignment options can be used to align components contained within other components,
such as buttons on a panel. The components will be centered horizontally or vertically
on their parent component regardless of whether the parent is a panel, a form, or
some other container component.





The Align Tops, Align Bottoms, and Align Right Edges options work just like the
Align Left Edges option you used earlier. There's not much point in going over all
the possibilities that exist for their use.





TIP: The first component selected will be the anchor point when using any
edge-alignment option. Refer to Figure 6.4. Let's say you selected Button3 first
and then used Shift+click to select the remaining buttons. When you choose Align
Left Edges, Button3 will remain where it is and all the other buttons will line up
with Button3's left edge because Button3 is the anchor component.





The Align Horizontal Centers and Align Vertical Centers options can be used to
center components relative to one another. This is best illustrated with shapes.
Start with a new form (or delete the buttons from the form you have been working
on). Now do the following:



1. Click on the Additional tab on the Component palette and choose the
Shape component. Click somewhere on the upper left of the form to add the shape.


2. Change the Shape property to stCircle.


3. Change the Width and Height properties to 150.


4. Double-click the Brush property and change its Color property to clBlack.


5. Place another Shape component on the form.


6. Change the second shape's Shape property to stCircle as well. Now you
have two circles of different sizes on the screen--a white circle and a black circle.


7. Click on the black circle. Hold the Shift key and click on the white
circle. Both shapes are selected.


8. Choose View | Alignment Palette from the main menu, if necessary (it
might already be displayed). Arrange the Alignment palette so you can see the two
shapes on the form. Observe the shapes as you perform the last two steps.


9. Click the Align Vertical Centers button on the Alignment palette. The
vertical centers are aligned.


10. Click the Align Horizontal Centers button on the Alignment palette.
The horizontal centers are aligned. Congratulations--you made a tire!



Did you see the effect as you performed the last two steps? Notice that because
you selected the black circle first, it did not move (it is the anchor component),
but the white circle moved as you clicked the alignment buttons. You can use these
alignment options to center any number of controls on one another. These two alignment
options have no effect when used on a single control.
Like the Component palette, the Alignment palette has a context menu associated
with it. Place the mouse cursor over the Alignment palette and click the secondary
mouse button. The context menu is displayed. Table 6.2 lists the items on the Alignment
palette's context menu and explains their uses.

TABLE 6.2. THE ALIGNMENT PALETTE'S CONTEXT MENU ITEMS.



Menu Item
Description


Stay on Top
Forces the Alignment palette to always be on top. This is useful if you are frequently
switching back and forth between the Form Designer and the Code Editor. Because the
Alignment palette is a small window, it's easy to lose it.


Show Hints
Turns the hints (tooltips) for the Alignment palette buttons on and off.


Hide
Hides the Alignment palette. (You can also use the close box on the



Alignment palette to hide it.) To show the Alignment palette again, you



have to choose View | Alignment Palette from the main menu.


Help
Brings up Delphi Help with the Alignment palette page displayed.



The Alignment dialog box performs the same actions as the Alignment palette. To
bring up the Alignment dialog box, choose Edit | Align from the main menu or Align
from the Form Designer's context menu. Figure 6.14 shows the Alignment dialog box.
FIGURE 6.14. The
Alignment dialog box.
In most cases, the Alignment palette is easier to use, but you can certainly use
the Alignment dialog box if you prefer.

Using the Align Property
Another type of alignment can be set using the Align property. This property controls
how a component is aligned with its parent. The possible values for the Align property
and a description of each are listed in Table 6.3.

TABLE 6.3. POSSIBLE VALUES FOR THE Align PROPERTY.



Value
Description


alBottom
The component is aligned at the bottom of the parent window. A status bar is an example
of a component aligned along the bottom of a main form.


alClient
The component expands to fill the parent window's client area. If other components
occupy part of the client area, the component fills what client area remains. Examples
include Memo components, Image components, and RichEdit components.


alLeft
The component is aligned along the parent window's left edge. A vertical toolbar
is an example of a left-aligned component.


alNone
The component is placed as designed with no special relationship to the parent. This
is the default for most components.


alRight
The component is aligned along the parent's right edge.


alTop
The component is aligned along the top of the parent's window. A toolbar is an example
of this type of alignment.



An example helps explain the Align property. Start with a blank form and then
perform these steps:



1. Click on the Standard tab on the Component palette and choose a Panel
component. Place the panel anywhere on the form.


2. Locate the Align property in the Object Inspector (it's at the top
of the list). Notice that it is set on alNone. Change the Align property to alTop.
The panel is aligned at the top of the form and it expands to fill the form's width.


3. Try to move the panel back to the middle of the form. The panel will
snap back to the top.


4. Try to make the panel narrower. Notice that the panel retains its width.


5. Change the panel's height. Note that the panel's height can be changed
(the width cannot).


6. Change the Align to alBottom. Now the panel is glued to the bottom
of the form.


7. Change the Align to alRight and then alLeft. The width is now the same
as the height was before. In effect, the panel is rotated. Again, attempts to move
the panel or size it vertically fail.


8. Change the Align property to alClient. The panel expands to fill the
form's entire client area. The panel cannot be resized in any dimension.


9. Change the Align property to alNone. The panel can again be sized and
moved.



As you see, changing Align to anything other than alNone effectively glues the
panel to one edge of the form. In the case of alClient, the panel is glued to all
four edges.

Setting the Tab Order
New Term: The tab order refers to the order in which components
receive input focus when the user presses the Tab key on the keyboard.
Delphi forms automatically support component navigation using the Tab key. This
means that you can move forward from component to component using Tab and backward
using Shift+Tab.





NOTE: There are two types of visual components. Windowed components
are components that accept keyboard focus, which means that the component can be
clicked with the mouse or tabbed to with the Tab key. When a component has keyboard
focus, it either displays a specialized cursor (such as the I-beam cursor in an edit
control) or has a focus rectangle drawn somewhere on the component. Windowed components
include the Edit, Memo, ListBox, ComboBox, and Button components, as well as many
more.
Non-windowed components are components that don't accept keyboard focus.
Components such as Image, SpeedButton, Label, Shape, and many others are non-windowed
components.
The tab order applies only to windowed components. Non-windowed components are
excluded from the tab order.





The tab order is initially set based on the order the components were placed on
the form when the form was designed. You can modify the tab order by changing the
TabOrder property for each control in the Object Inspector, but that method is tedious
because you have to go to each control individually. The Edit Tab Order dialog box
provides an easier way (see Figure 6.15).
FIGURE 6.15. The
Edit Tab Order dialog box.
The Edit Tab Order dialog box is invoked by choosing Edit | Tab Order from the
main menu. This dialog box displays all windowed components currently on the form;
non-windowed components are not displayed. To change the tab order, click on the
name of the component you want to move in the tab order and then click the up or
down buttons as needed. You can also drag the component to its new position in the
tab order. After you get the tab order the way you want it, click OK to set it that
way. You can confirm the new settings by viewing each control's TabOrder property.





NOTE: The tab order starts with 0. The first component in the tab order
is 0, the second is 1, and so on.





Building an Example Application
To illustrate how different components work together, let's build a prototype
of an application that resembles Windows Notepad, Windows standard text editor.





NOTE: Building a text editor probably doesn't sound too glamorous. To be
honest, it's not. What building a text editor will do for you, however, is teach
you how to conquer many of the real-world problems you will encounter when programming
in Delphi. It might not be glamorous, but it will almost certainly teach you more
than building Snazzy Gadgets 1.0.





New Term: A prototype is an application that has the appearance
of a working application but lacks full functionality, usually because it's in the
early stages of design.





NOTE: Delphi is perfect for quick prototyping of an application. You can
have the main screens and dialog boxes designed and displayed in much less time than
it would take with traditional Windows programming tools. That is not, however, to
say that Delphi is just for prototyping. Delphi is fully capable of handling all
your 32-bit Windows programming needs.





Step 1: Starting a New Application



1. Choose File | New Application from the main menu. If prompted to save
the current project, click No.


2. The form is selected, so change the Name property to MainForm.


3. Change the Caption to ScratchPad 1.0.


4. Choose Project | Options from the main menu. Click on the Application
tab and enter ScratchPad 1.0 for the application's title. Click OK to close the Project
Options dialog box.



Step 2: Adding a Toolbar
Most Windows applications these days have a toolbar. Building a toolbar requires
several steps in itself. I'm not quite ready to explain everything there is to know
about toolbars in Delphi, so I'll save that for Day 13, & quot;Beyond the Basics. & quot;
You'll add a toolbar to the ScratchPad program at that time. What you can do for
now, though, is add a toolbar that can be used as a placeholder for the real toolbar
that you add later. Follow these steps:



1. Click on the Win32 tab on the Component palette and choose the ToolBar
component (it's the third from the right).


2. Click anywhere on the form to add the toolbar. Notice that the toolbar
automatically aligns itself to the top of the form.

3. Right-click on the toolbar and choose New Button. A button appears
on the toolbar.

4. Repeat step 3 to add a second button.



That's all you are going to do with the toolbar for now. As I said, you'll make
a real toolbar for this program on Day 13.

Step 3: Adding a Status Bar
Okay, so far, so good. Windows Notepad doesn't have a status bar (or a toolbar,
for that matter), but you can put one in your application by following these steps:



1. Click on the Win32 tab on the Component palette and choose the StatusBar
component.


2. Click anywhere on the form. The status bar is automatically placed
at the bottom of the form. The status bar has a default Align value of alBottom.


3. Change the Name property to StatusBar.



The form will now look like Figure 6.16.
FIGURE 6.16. The
ScratchPad form up to this point.

Step 4: Adding the Memo Component
You need some component in which to type text, so you can use a memo component
to add this feature (believe it or not, you're almost done with your prototype):



1. Click on the Standard tab on the Component palette and choose a Memo
component. Place the memo anywhere on the form's client area.


2. Change the Name property to Memo.


3. Double-click the Value column next to the Lines property. The String
List Editor is displayed. Delete the word Memo and click OK.


4. Change the Scrollbar property to ssVertical. (Initially, you want only
a vertical scrollbar on the memo.)


5. Change the Name property of the Font property to Fixedsys. (Because
this is a Notepad copycat, you'll use the system font.)


6. Change the Align property to alClient. The memo expands to fill the
client area between the toolbar and the status bar.



Stand back and admire your work. This is starting to look like a real application!
If the form looks too large or too small, resize it by dragging the lower-right corner.
It's your program, so make it look the way you want it to look.





TIP: Pressing the Esc key selects the parent of the control that currently
has the selection. For example, our form's client area is covered by components,
which makes it impossible to select the form itself. To make the form the active
component in the Object Inspector, select the memo component and then press the Esc
key on the keyboard. You can also choose the form from the Component Selector combo
box on the Object Inspector.





Notice that all the controls automatically resize themselves to retain their relationship
with the parent window--the form, in this case. That is one of the main advantages
to the Align property. The form now looks like the one in Figure 6.17.

Running the Program
You can now click the Run button to run the program. You can type text in the
window's client area and you can press the toolbar buttons (although they don't do
anything at this point). Keep in mind that this is a prototype and is mostly for
show right now. You add more to the program by the end of the day.
FIGURE 6.17. The
completed prototype.
You'd better save the project because you're going to use it later in the chapter.
Choose File | Save All from the main menu. Save the main form's source unit as SPMain
and the project as Scratch.

May I See a Menu, Please?
Menus are a big part of most Windows applications. Some Windows programs don't
have menus, but the vast majority do. Delphi makes creating menus easy with the Menu
Designer. The Menu Designer has the following features:


It can create both main menus and pop-up menus (context menus).

It provides immediate access to the Code Editor to handle the OnClick events
for menu items.

It can insert menus from templates or from resource files.

It can save custom menus as templates.


All the Menu Designer's commands are accessed via the Menu Designer context menu
or by interacting with the Object Inspector. Figure 6.18 shows the Menu Designer's
context menu.
For the most part, these menu items are self-explanatory, so I'm not going to
go over each one. Rather, you can learn about them by working with them. To begin,
let's add a main menu to the ScratchPad application you created earlier. After that
you'll add a context menu.
FIGURE 6.18. The
Menu Designer's context menu.


Creating a Main Menu
The Menu Designer enables you to quickly build any menu. The menu structure for
a main menu consists of a MainMenu component, which is represented by the VCL class
TMainMenu. Each item on the menu is a MenuItem component that is encapsulated in
the TMenuItem class. You don't need to be too concerned about the intricacies of
how these classes work together because the Menu Designer makes creating menus easy.
With that brief overview, let's add a main menu to the ScratchPad application.

Adding a Main Menu to the Form
The first thing you must do is add a MainMenu component to your form.





NOTE: By now you have had some experience with Delphi. From this point
on I will abbreviate some steps that you need to take to perform certain actions.
For example, from here on I'll say, & quot;Place a MainMenu component on the form & quot;
rather than & quot;Click on the Standard tab on the Component palette. Click the MainMenu
button and click on the form to place the component. & quot; Don't worry, I'll still
give plenty of details when new operations are introduced.









1. Open the ScratchPad project you created earlier in the chapter.


2. Place a MainMenu component on the form and change its Name property
to MainMenu. Notice that a MainMenu component has very few properties and no events.
All the menu's work is done by the individual menu items.


3. Double-click on the MainMenu icon. The Menu Designer is displayed.



The Menu Designer looks like a blank form without grid points. The Menu Designer
can be sized in any way you want. The size is just for your convenience and has no
bearing on how the menu operates at runtime. At this point, the Menu Designer is
waiting for you to begin building the menu. After you have created your first menu,
you will find that menu creation is easy and intuitive.

Creating a Menu by Hand
Although there are easier ways to create a File menu, you will create your first
menu by hand. The Menu Designer always has a blank menu item that acts as a placeholder
for any new menu items you will create. When you first start the Menu Designer, the
blank item is selected.



1. Change the Name property to FileMenu.


2. Click on the Caption property in the Object Inspector, type & amp;File,
and press Enter.








NOTE: The ampersand ( & amp;) is used to create the underlined character
for a menu item. The underlined character is the accelerator the user can
type in combination with the Alt key to navigate a menu using the keyboard. You can
put ampersands anywhere in the menu item's text. For instance, the customary text
string for the Exit menu item would be E & amp;xit so that the x is the accelerator.
All you have to do is provide the ampersands before the appropriate letter, Windows
will take it from there.





At this point, several things happen. First, the File menu shows up in the Menu
Designer. It also appears on the main form behind the Menu Designer. The other thing
that happens is that a new, blank placeholder is added below the File menu you just
created (you'll have to click on the File menu in the Menu Designer to see the placeholder).
In addition, a new pop-up placeholder is created to the right of the File menu. The
Object Inspector is displaying a blank MenuItem component, waiting for you to enter
the Caption and Name property values. Figure 6.19 shows the Menu Designer as it appears
at this point.
FIGURE 6.19. The
Menu Designer and Object Inspector after creating the File menu.
Let's continue with the creation of the menu:



1. Change the Name property for the new item to FileNew.


2. Change the Caption property to & amp;New and press Enter. Again, a blank
item is created in the Menu Designer.


3. Repeat steps 1 and 2 and create menu items for Open, Save, and Save
As. If you need help on where to place the ampersand, refer to Figure 6.20. Don't
worry that you might not get it exactly right. You can always go back later and fix
any errors.








TIP: Make your menus as standard as possible. Be sure that your accelerators
(the underlined characters) are the same as in other Windows programs. Also, remember
that an ellipsis (...) following a menu item's text is a visual cue to the user that
choosing the menu item will invoke a dialog box.





At this point, you need a menu separator.
New Term: A separator is the horizontal line on a menu that separates
groups of menu items.
Adding a separator is easy with the Delphi Menu Designer. All you have to do is
put in a hyphen for the Caption property. Select the blank menu item under Save As,
type a hyphen for the Caption, and press Enter. A separator is placed in the menu.
Continue adding menu items until your menu looks like the one in Figure 6.20. If
you need to modify a menu item, just click on it and change properties in the Object
Inspector as needed.
FIGURE 6.20. The
Menu Designer with the finished File menu.





NOTE: The Menu Designer always provides a blank menu item at the bottom
of each pop-up menu and on the menu bar's right side. You cannot delete these blank
items, but there's no need to--they are used only in the Menu Designer and won't
show on the menu when your program runs.





Now that the File menu is done, you need to create an Edit menu and a Help menu.

Inserting a Menu from a Template
This time you'll take the easy approach. First, click on the blank pop-up menu
placeholder to the right of the File menu. Now click your secondary mouse button
and choose Insert From Template from the context menu. The Insert Template dialog
box is displayed, as shown in Figure 6.21.
FIGURE 6.21. The
Insert Template dialog box.
This dialog box shows a list of templates from which you can choose. You can use
the predefined templates or create your own. In this case, you are only interested
in adding an Edit menu, so choose Edit Menu and click OK. A full Edit menu is immediately
inserted into the Menu Designer. In fact, it's a little too full. I'll deal with
that in a moment.
As long as you're here, you can add the Help menu, too. Click on the placeholder
to the right of the Edit menu. Choose Insert From Template again, and this time insert
a Help menu. (Don't choose the Expanded Help menu, though.) You'll tidy up both the
Edit and Help menus in the next section. Notice that the main form has been updating
to show the new menu items as they are placed.





NOTE: You can insert templates to create pop-up menus as easily as creating
main menu items.





Yes, inserting from a template is really that easy. After using Delphi for a while,
you will no doubt have your own custom templates to choose from for building menus
quickly and easily. You still have to update the Name properties to meaningful names,
but it's much easier than creating the whole menu from scratch.





NOTE: The Insert From Resource choice works the same as Insert From Template
except that it expects a resource script file (a resource script file has the extension
.RC) containing a valid menu definition. The menu resource must use the begin/end
menu resource syntax and cannot use braces. For example, the following is an invalid
menu resource:
MENU_1 MENU
{
POPUP & quot;File & quot;
{
MENUITEM & quot;Open & quot;, 100
MENUITEM & quot;About & quot;, 101
}

}







The following menu resource, however, is legal:
MENU_1 MENU
BEGIN
POPUP & quot;File & quot;
BEGIN
MENUITEM & quot;Open & quot;, 100
MENUITEM & quot;About & quot;, 101
END

END







This applies only to menus inserted with Insert From Resource and not to menu
resources in general.






Deleting Menu Items
The process of creating a Windows application is a living, breathing thing. Rarely
will you get everything exactly right the first time. Users will request new features,
the boss will come up with a few changes, and some features will even be dropped.
You will often need to update your application's menus as these changes occur. For
example, the Edit menu inserted earlier is a little verbose for your needs; there
are several items that you just don't need. No problem--you can just delete them:



1. Click on the Edit menu.


2. Click on the item called Repeat & lt;command & gt;.


3. Press Delete on the keyboard or choose Delete from the Menu Designer
context menu to delete the item. The item disappears and the remaining items move
up.


4. Delete the Paste Special menu item as well.



There, that was easy! You're not quite done with the Edit menu, but before going
on, I want to mention a very useful feature of the Menu Designer. You are probably
familiar with using Shift+click and Ctrl+click when selecting items in other Windows
programs. These techniques can be used in Windows Explorer to select files, for example.
The Menu Designer supports Shift+click and Ctrl+click with one qualification--you
can use these to select multiple menu items but not to deselect an item. As always,
an exercise can illustrate better than I can explain:



1. The Edit menu should still be displayed. If not, click on Edit to reveal
the Edit menu.


2. Click on the menu item called Goto.


3. Hold down the Shift key and click on the menu item called Object. All
items between those two points are selected.


4. Press Delete on the keyboard to delete all the items at one time.


5. Move to the Help menu and delete the middle two items. Only the Contents
and About items remain.



As you can see, the Shift+click technique can be used to quickly delete unwanted
menu items. Now you have the menus trimmed back to the way you want them to appear
in the ScratchPad application.

Inserting Menu Items
Inserting menu items is pretty straightforward. Just click on the menu item above
which you want to insert a new item and press the Insert key on the keyboard (or
choose Insert from the Menu Designer's context menu). A blank menu item is inserted,
and you can now modify the Name and Caption properties just as you did earlier. Let's
insert an item into the Edit menu:



1. Click on Edit to display the Edit menu.


2. Click on the Find menu item.


3. Press the Insert key on the keyboard. A new menu item is provided,
and all other menu items below the new item move down.


4. Change the Name property to EditSelectAll and change the Caption property
to Select & amp;All.


5. Click on the empty placeholder at the bottom of the Edit menu. Add
a menu separator (remember, just enter a hyphen for the Caption property).


6. Click on the placeholder again and add a new item. Make the Name property
EditWordWrap and the Caption property & amp;Word Wrap.



Moving Menu Items
You can easily move menu items as needed. You can move them up or down within the pop-up menu they are already in, or you can move them across pop-ups. There are two ways to move a menu item. The first is by using Cut and Paste. Cut and Paste
work as you would expect, so there's no need to go over that.

The other way to move a menu item is by dragging it to a new location and dropping
it. Let's try it. You really want the Select All menu item just below the Undo item.
That's easy enough to fix, you can just move it:



1. Click on Edit to display the Edit menu.


2. Click on the Select All item and drag it up until the separator under
the Undo item is highlighted.


3. Let go of the mouse and the menu item is moved.



Too easy, right? Yes, but that's what Delphi is all about.

Batch Modifying Properties
Sometimes you want to modify several menu items' properties at once--that is called
batch modifying. For example, you have a few menu items in the ScratchPad application
that you are not ready to implement at this time. You aren't ready for printing support,
for instance, nor are you ready to implement the help system. Therefore, you need
to gray out (disable) those menu items:



1. Choose Help | Contents in the Menu Designer.


2. Change the Enabled property to False. The menu item is grayed out.


3. Click on the File menu.


4. Click on the Print menu item, hold the Shift key down, and click on
the Print Setup menu item. Both items are selected.


5. In the Object Inspector, change the Enabled property to False. Both
menu items are disabled.


6. Repeat steps 4 and 5 to disable the Find and Replace items on the Edit
menu.



You can modify a group of menu items at one time with this method. Simply select
the items you want to modify and then change the property you want to modify. All
menu items currently selected will have the new property value.

Adding Bitmaps to Menu Items
You can easily add bitmaps to your menus. First click on a menu item for which
you want to add a bitmap. Then, double-click in the Value column of the Bitmap property
in the Object Inspector. When the Picture Editor comes up, you can select a bitmap
for the menu item. The bitmap can be a single image or a bitmap strip (an image list).
If you are using an image list, you should set the ImageIndex property to the index
number of the image in the image list you want to display for this menu item.





NOTE: The menu item bitmaps don't display in the Menu Designer or on the
form at design time. You will have to run the program to see the bitmaps.






Creating Submenus
There's nothing special or tricky about creating submenus. A submenu is
a menu item that, when clicked, expands to show more menu choices. A submenu is denoted
by a right-pointing arrow next to the menu item text. You can create a submenu by
choosing Create Submenu from the Menu Designer context menu or by holding down the
Ctrl key and pressing the right-arrow key. When you create a submenu, a blank menu
item is placed to the right of the menu item. You can add menu items to the submenu
just as you did when you created the main menu. You can create a submenu by inserting
a menu template as well.

Adding Shortcuts
You can easily add a keyboard shortcut to a menu item by changing its ShortCut
property in the Object Inspector. The Edit menu that you inserted earlier already
had keyboard shortcuts built in. For example, the customary shortcut for Cut is Ctrl+X.
If you look at the Edit menu, you will see Ctrl+X listed next to the Cut item (the
shortcut was already assigned when you loaded the menu from a template).
Click on the Cut menu item and you will see that the ShortCut property says Ctrl+X.
Click on the Value column next to the ShortCut property. On the Value column's right
side you will see a drop-down button. Click on the button to display the list of
available shortcuts. The list you see there contains just about any keyboard shortcut
you need. To set the keyboard shortcut for a menu item, simply pick a shortcut from
the list.
The standard shortcut for Select All is Ctrl+A, so let's add that as a shortcut
for our Select All menu item:



1. Choose Edit | Select All from your menu in the Menu Designer.


2. Click on the ShortCut property in the Object Inspector.


3. Choose Ctrl+A from the list of available shortcuts. Now the Select
All menu item shows Ctrl+A next to it.



That's all you have to do; Delphi takes care of it from there. The shortcuts function
without you having to write any code.

Final Touches
Let's finish off your menu. First, you'll make the Word Wrap menu item on by default.
This menu item is going to be used to turn word wrapping on or off. When word wrapping
is on, the Word Wrap menu item will have a check mark next to it. When word wrapping
is off, it will not have a check mark next to it. Click on the Word Wrap menu item
and change the Checked property to True. A check mark shows up to indicate that the
word wrap feature is on.
Another thing you need to do is to change the Name property on all of the menu
items that you inserted from a template. They were given default names, and you want
to change them to more meaningful names. Follow these steps:



1. Click on the Edit | Undo menu item. Change the Name property from Undo1
to EditUndo. Notice that you prepend the pop-up menu name, Edit, to the front of
the menu item name and remove the 1 at the end.


2. You can use any naming convention you like, but be consistent. Repeat
the process for the Cut, Copy, Paste, Find, and Replace menu items.


3. Now move to the Help menu and modify the Name property of the Contents
item to HelpContents, and that of the About menu item to HelpAbout.



That about finishes your menu. Run through the menu to check it once more. If
you find any errors, make the necessary changes. When you are satisfied that the
menu is correct, click the close box to close the Menu Designer.





NOTE: You can access the Code Editor directly from the Menu Designer by
double-clicking any menu item. When you double-click a menu item, the Code Editor
displays the OnClick event for that item and you can start typing code. In this case,
you are going to go back to the main form and do your code editing there.






Writing the Code
Okay, so you have all these menu items but no code to make them work. It's going
to be a lot of work implementing all these, right? Actually, it's easy. Most of the
required code is already part of the TMemo class. All you have to do is call the
appropriate TMemo methods in the menu handlers. You'll have to do a few other things,
but most of what you add is code you have seen before.

Adding Components to the Form
Before writing the code, you need to add the usual OpenDialog and SaveDialog components to the form:




1. Place an OpenDialog component on the form.


2. Change the Name property to OpenDialog.


3. Place a SaveDialog component on the form.


4. Change the Name property to SaveDialog.


5. Line up the MainMenu, OpenDialog, and SaveDialog icons on the form.



Writing the Menu Items' Code
Okay, that was easy enough. Now let's get on with writing the code for the menu
items. You'll start with the File Exit menu item (hey, it's the easiest!). Be sure
that the Menu Designer is closed so you don't confuse the Menu Designer with the
Form Designer.



1. Choose File | Exit from the main menu. The Code Editor comes to the
top and the FileExitClick event handler is displayed.


2. The cursor is positioned and ready to go. Type the following at the
cursor (I always indent two spaces, by the way):






Close;









NOTE: In step 2 I had you use the Close function to close the form. That
works fine here because this is the application's main form. But if you want to terminate
the application from anywhere in the program, you should use this:
Application.Terminate;







This code ensures that the application is terminated regardless of which form
is currently open.






That's it. I told you it was the easiest. Let's do one more; then I'm going to
turn you loose to finish the rest on your own.



1. Choose Edit | Cut from the main menu. The Code Editor comes to the
top and the EditCutClick event handler is displayed.


2. Type the following at the cursor:






Memo.CutToClipboard;




And that's all there is to that particular menu item. You might not fully realize
it, but VCL does a lot for you behind the scenes. The whole idea of a framework is
to take the burden of the low-level details off the programmer's back. Life is good.

Adding the Finishing Touches
One of the interesting aspects of a program like Delphi is that you rarely view
your program as a whole. Delphi conveniently takes you to the section of code you
need to work on to deal with a particular event, so you usually only see your program
in small chunks. Listing 6.1 contains the main form unit for the ScratchPad program
up to this point. The class declaration is entirely Delphi generated. Follow the
examples you've just worked through to write code for each of the remaining menu
items. Copy the code for each of the menu OnClick handlers from Listing 6.1. (The
comment lines are there to explain to you what the code is doing. You don't have
to include them when you type the code.)





NOTE: The event handlers appear in the source file in the order in which
they were created. Don't be concerned if the order of the event handlers in your
source file doesn't exactly match Listing 6.1. The order in which the functions appear
makes no difference to the compiler.






LISTING 6.1. SPMAIN.PAS.
unit SPMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, & #194;Dialogs,
Menus, StdCtrls, ComCtrls, ToolWin;
type
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;
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);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FileExitClick(Sender: TObject);
begin
{ All done. Close the form. }
Close;
end;
procedure TMainForm.EditCutClick(Sender: TObject);
begin
{ Call TMemo.CutToClipboard. }
Memo.CutToClipboard;
end;
procedure TMainForm.EditCopyClick(Sender: TObject);
begin
{ Call TMemo.CopyToClipboard. }
Memo.CopyToClipboard;
end;
procedure TMainForm.EditPasteClick(Sender: TObject);
begin
{ Call TMemo.PasteFromClipboard. }
Memo.PasteFromClipboard;
end;
procedure TMainForm.FileNewClick(Sender: TObject);
var
Res : Integer;
begin
{ Open a file. First check to see if the }
{ current file needs to be saved. }
if Memo.Modified then begin
{ Display a message box. }
Res := Application.MessageBox(
`The current file has changed. Save changes?',
`ScratchPad Message', MB_YESNOCANCEL);
{ If Yes was clicked then save the current file. }
if Res = IDYES then
FileSaveClick(Sender);
{ If No was clicked then do nothing. }
if Res = IDCANCEL then
Exit;
end;
{ Delete the strings in the memo, if any. }
if Memo.Lines.Count & gt; 0 then
Memo.Clear;
{ Set the FileName property of the Save Dialog to a }
{ blank string. This lets us know that the file has }
{ not yet been saved. }
SaveDialog.FileName := `';
end;
procedure TMainForm.FileOpenClick(Sender: TObject);
var
Res : Integer;
begin
{ Open a file. First check to see if the current file needs }
{ to be saved. Same logic as in FileNewClick above. }
if Memo.Modified then begin
Res := Application.MessageBox(

`THE CURRENT FILE HAS CHANGED. SAVE CHANGES?',
`ScratchPad Message', MB_YESNOCANCEL);
if Res = IDYES then
FileSaveClick(Sender);
if Res = IDCANCEL then
Exit;
end;
{ Execute the File Open dialog. If OK was pressed then }
{ open the file using the LoadFromFile method. First }
{ clear the FileName property. }
OpenDialog.FileName := `';
if OpenDialog.Execute then begin
if Memo.Lines.Count & gt; 0 then
Memo.Clear;
Memo.Lines.LoadFromFile(OpenDialog.FileName);
SaveDialog.FileName := OpenDialog.FileName;
end;
end;
procedure TMainForm.FileSaveClick(Sender: TObject);
begin
{ If a filename has already been provided then there is }
{ no need to bring up the File Save dialog. Just save the }
{ file using SaveToFile. }
if SaveDialog.FileName & lt; & gt; `' then begin
Memo.Lines.SaveToFile(SaveDialog.FileName);
{ Set Modified to False since we've just saved. }
Memo.Modified := False;
{ If no filename was set then do a SaveAs. }
end else FileSaveAsClick(Sender);
end;
procedure TMainForm.FileSaveAsClick(Sender: TObject);
begin
{ Display the File Save dialog to save the file. }
{ Set Modified to False since we just saved. }
SaveDialog.Title := `Save As';
if SaveDialog.Execute then begin
Memo.Lines.SaveToFile(SaveDialog.FileName);
Memo.Modified := False;
end;
end;
procedure TMainForm.EditUndoClick(Sender: TObject);
begin
{ TMemo doesn't have an Undo method so we have to send }
{ a Windows WM_UNDO message to the memo component. }
SendMessage(Memo.Handle, WM_UNDO, 0, 0);
end;
procedure TMainForm.EditSelectAllClick(Sender: TObject);
begin
{ Just call TMemo.SelectAll. }
Memo.SelectAll;
end;
procedure TMainForm.EditWordWrapClick(Sender: TObject);
begin
{ Toggle the TMemo.WordWrap property. Set the Checked }
{ property of the menu item to the same value as WordWrap. }
Memo.WordWrap := not Memo.WordWrap;
EditWordWrap.Checked := Memo.WordWrap;
{ If WordWrap is on then we only need the vertical scroll }
{ bar. If it's off, then we need both scroll bars. }
if Memo.WordWrap then
Memo.ScrollBars := ssVertical
else
Memo.ScrollBars := ssBoth;
end;

end.

And Now, the Moment You've All Been Waiting For...
After you create the event handlers for the menu items, you are ready to run the
program. Click the Run button and the program should compile and run. If you get
compiler errors, carefully compare your source code with the code in Listing 6.1.
Make any changes and click the Run button again. You might have to go through this
process a few times before the program will compile and run. Eventually, though,
it will run (I promise!).
When the program runs, you will find a program that, although not yet 100 percent
feature-complete, acts a lot like Windows Notepad. Even though you have a few things
to add before you're finished, you have a fairly good start--especially when you
consider the actual time involved up to this point. Figure 6.22 shows the ScratchPad
program running.
FIGURE 6.22. The
ScratchPad program in action.

Pop-Up Menus (Context Menus)
I am not quite done with the discussion of menus. In Delphi, you can create pop-up
menus as easily as you can a main menu. A nice feature of Delphi is that you can
assign a particular pop-up menu to a component via the component's PopupMenu property.
When the cursor is placed over the component and the secondary mouse button is clicked,
that pop-up will automatically be displayed. Writing event handlers for pop-up menus
is exactly the same as writing event handlers for main menus.
A common feature of text-editing programs is to place the Cut, Copy, and Paste
operations on a context menu. You'll add that capability to ScratchPad. To create
the pop-up, you'll cheat and copy part of the main menu. Follow these steps:


1. Choose a PopupMenu component from the Component palette and place it
on the form.

2. Change the Name property to MemoPopup.


3. Double-click the PopupMenu icon to run the Menu Designer.


4. Click the secondary mouse button to bring up the Menu Designer context
menu. Choose Select Menu from the context menu. A dialog box is displayed that shows
the menus available for your application. Choose MainMenu and click OK.


5. Click on the Edit menu. Click on the Cut menu item, hold down the Shift
key, and click on the Paste menu item. Cut, Copy, and Paste are all now highlighted.


6. To copy the selected items to the Clipboard, choose Edit | Copy from
the Delphi main menu (don't choose Edit | Copy from the menu you are creating in
the Menu Designer) or press Ctrl+C.


7. Again, choose Select Menu from the Menu Designer context menu. This
time, choose MemoPopup and click OK. The Menu Designer shows a blank pop-up menu.


8. Choose Edit | Paste from the main menu or type Ctrl+V on the keyboard.
The Cut, Copy, and Paste menu items are inserted into the pop-up.



Okay, just a few more things and you'll be done. You need to change the Name property
for the new menu items:



1. For the Cut menu item, change the Name property to PopupCut.


2. For the Copy menu item, change the Name property to PopupCopy.


3. For the Paste menu item, change the Name property to PopupPaste.



The final step is to write event handlers for the pop-up menu items. Hmmm...you
have already written code for the main menu's Cut, Copy, and Paste items. It would
be a shame to duplicate that effort (even if it is just a single line in each case).
Could you just use the same event handlers that you created earlier? Sure you can.
Just follow these steps:



1. Click on the Cut popup menu item.


2. Click on the Events tab in the Object Inspector.


3. Click the drop-down arrow button in the Value column next to the OnClick
event (the only event in the list). A list of event handlers created so far is displayed.


4. Choose the EditCutClick event handler from the list. Now, when the
Cut pop-up menu item is clicked, the Edit | Cut handler will be called. No code duplication
is required.


5. Repeat steps 1 through 4 for the Copy and Paste items on the pop-up
menu. When you are done, close the Menu Designer.


6. On the main form, click on the Memo component. Change the PopupMenu
property to MemoPopup (by choosing it from the list).



You can attach just about any event to any event handler by using this method.
Now run the program again to test the new context menu. Of course it works!

Creating and Saving Menu Templates
Delphi provides you with several menu templates that you can insert into your
main menus and pop-ups. You can also create and save your own templates for future
use in your programs. First, start the Menu Designer and create the menu.





NOTE: When creating menus to use as templates, you first must have a main
menu or a pop-up menu on a form in order to start the Menu Designer. You can use
a temporary, blank form if you want. Start with a blank form, place a MainMenu component
on it, and double-click the menu component's icon to start the Menu Designer. After
you are done creating menu templates, discard the blank form without saving.





When you have created the menu, choose Save As Template from the Menu Designer's
context menu. The Save Template dialog box is displayed. Give the menu a meaningful
name and click the OK button, and the menu is saved as a template. To insert the
menu, choose Insert From Template from the Menu Designer's context menu just as you
did earlier. Any menus you have created will show up along with Delphi's prebuilt
templates.
To remove a template that you previously added, choose Delete Templates from the
Menu Designer's context menu. The Delete Templates dialog box is displayed, and you
can choose the templates you want to delete. When you click the OK button, the selected
menu templates will be deleted. Press Cancel to close the dialog box without deleting
any templates.

Summary
Congratulations! You have just covered the bulk of the Delphi visual programming
features. I hope it was enjoyable as well as educational. The Form Designer is a
powerful tool that enables you to do as much programming as possible in a visual
manner. If you haven't had to create windows and dialogs using traditional Windows
programming tools, you might not fully appreciate that advantage. Trust me, it's
significant. The Menu Designer is also a powerful tool, particularly because of the
capability to import menus, which makes menu creation easy and actually fun with
Delphi. The Menu Designer also makes updating existing menus a snap.

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, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A



Q I'm using the Alignment palette a lot, and every time I switch from the
Code Editor back to the Form Designer, the Alignment palette gets lost somewhere.
Is there anything I can do about that?


A Locate the Alignment palette (it's there somewhere!) and click your
secondary mouse button to bring up the Alignment palette's context menu. Choose the
Stay on Top item from the context menu. Now the Alignment palette will always be
on top where you can find it.


Q I am trying to select a group of components on a panel by dragging the selection
rectangle around them, but I keep moving the panel. What's wrong?


A You need to hold down the Ctrl key while dragging when you are selecting
components contained on a panel.


Q I've moved my components around my form several times and now the tab order
is erratic. What can I do to fix that?


A Choose Tab Order from the Form Designer's context menu. Arrange the
tab order the way you want it. When you click OK, the new tab order will be implemented.


Q The menu templates provided are nice, but they have so much stuff on them
that I don't need. What can I do about that?


A You can do two things. First, you can import a menu and then simply
delete the items you don't want. Using the click, Shift+click method you can get
rid of unwanted menu items in just a few seconds. Deleting items from a menu inserted
from a template has no adverse effects. The second thing you can do is to follow
the click, Shift+click method and then, when you have the menu just the way you want
it, you can save it as a new template. That way you can keep the original Delphi-supplied
template and have your customized template as well.


Q Can I save my own menus as templates?


A Yes. First create the menu and then choose Save As Template from the
Menu Designer context menu. Give the template a name, click OK, and the template
is saved. Now all you have to do to reuse the menu later is insert the menu using
the Insert From Template feature.



Quiz



1. When do you use Ctrl+drag in selecting components?


2. What significance does the first component selected have when aligning
a group of components?


3. What is the quickest method to select a group of components?


4. How can you make a group of components all have the width of the group's
widest component?


5. What happens when you double-click a component on a form?


6. What does the Align property's alClient option do?


7. What does the ellipsis following a menu item mean?


8. What two ways can you move a menu item?


9. How do you add menu accelerators to menu items?


10. How do you initially disable a menu item?



Exercises



1. Place five edit components on a form and arrange them so that they
are stacked vertically with their left edges aligned.


2. Turn the Snap to Grid option off (choose Tools | Environment Options
from the main menu). Place five controls of your choice on a form and align their
right edges.


3. Place a ListBox component on a blank form and modify it so that it
always occupies the form's entire client area.


4. Add an About box to the ScratchPad program. Use the Alignment palette
to quickly align the text labels.


5. Add an Undo item and a menu separator to the context menu for the ScratchPad
program.


6. Start a new application. Place six edit components on a form in random
fashion. Now arrange the tab order so that tabbing proceeds from top to bottom. Run
the program to test the tabbing order.


7. Add the Ctrl+S keyboard shortcut to the File | Save menu item in the
ScratchPad program.


8. Open the Picture Viewer project you created on Day 4. Remove all unused
menu items.








& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > ch03.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 3 -- Classes and Object-Oriented Programming








Teach Yourself Borland Delphi 4 in 21 Days







- 3 -


Classes and Object-Oriented Programming



Sets

Styles := Styles - [fsItalic];
Styles := [fsBold, fsItalic];

Casting
Pointers

Local Versus Dynamic Memory Usage
Dynamic Allocation and Pointers
Dereferencing a Pointer

What's a Class?
Anatomy of a Class
Class Access Levels

Constructors
Destructors
Data Fields
Methods
About Self
A Class Example

Inheritance
Overriding Methods

Class Keywords: is and as

Summary
Workshop

Q & amp;A
Quiz
Exercises









Today you get to the good stuff. In this chapter you will learn about classes.
Classes are the heart of Object Pascal and a major part of object-oriented programming.
Classes are also the heart of the Visual Component Library (VCL), which you will
use when you start writing real Windows applications. (The VCL is discussed in detail
on Day 5, & quot;The Visual Component Model. & quot;) Today you will find out what a
class is and how it's expected to be used. Along the way you will learn the meaning
of Object Pascal buzzwords like inheritance , object , and data
abstraction . Before you get to that, however, I want to cover a few more aspects
of Object Pascal that I haven't yet covered.

Sets
Sets are used frequently throughout Delphi, so you need to know what sets are
and how they work.
A set is a collection of values of one type.
That description doesn't say too much, does it? An example that comes to mind
is the Style property of a VCL font object. This property can include one or more
of the following values:


fsBold

fsItalic

fsUnderline


fsStrikeout


A font can have any combination of these styles or none of them at all. A set
of font styles, then, might have none of these values, it could have all of them,
or it could have any combination.
So how do you use a set? Let me use the Style property to illustrate. Typically,
you turn the individual Style values for the font on or off at design time. Sometimes,
however, you need to set the font's Style property at runtime. For example, let's
say that you want to add the bold and italic attributes to the font style. One way
is to declare a variable of type TFontStyles and then add the fsBold and fsItalic
styles to the set. Here's how it looks:

var
Styles : TFontStyles;
begin
Styles := Styles + [fsBold, fsItalic];
end;

This code adds the elements fsBold and fsItalic to the Styles set. The elements
are enclosed in brackets to indicate that you are adding elements to the set. The
brackets, when used in this way, are called a set constructor . Notice that
this code doesn't actually change a font's style; it just creates a set and adds
two elements to it. To change a font's style, you have to assign this newly created
set to the Font.Style property of some component:

Memo.Font.Style = Styles;

Now, let's say that you want the font to be bold but not italic. In that case,
you have to remove the italic style from the set:

Styles := Styles - [fsItalic];
The style now contains only the fsBold value because the fsItalic value has been
removed.
Often you want to know whether a particular item is in a set. Let's say you want
to know whether the font is currently set to bold. You can find out whether the fsBold
element is in the set by using the in keyword:

if fsBold in Styles then
DoSomething;

Sometimes you need to make sure you are starting with an empty set. You can clear
a set of its contents by assigning an empty set to a set variable. This is done with
an empty set constructor--for example,

{ start with an empty set }
Styles := [];
{ now add the bold and italic styles }
Styles := Styles + [fsBold, fsItalic];

In this example the font style is cleared of all contents, and then the bold and
italic styles are added. This same thing can be accomplished in a slightly different
way by just assigning directly to a set:

Styles := [fsBold, fsItalic];
You don't specifically have to create a TFontStyles variable to change a font's
style. You can just work with the property directly--for example,

Memo.Font.Style := [];
Memo.Font.Style := Memo.Font.Style + [fsBold, fsItalic];

A set is declared using the set keyword. The TFontStyles property is declared
in the VCL source file GRAPHICS.PAS like this:

TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);

TFontStyles = set of TFontStyle;

The first line here declares an enumeration type called TFontStyle. (An enumeration
is a list of possible values.) The second line creates the TFontStyles set as a set
of TFontStyle values.
Sets are used often in VCL and in Delphi programming. Many component properties
are defined as sets. You'll get the hang of sets quickly as you work with Delphi.

Casting
New Term: Cast means to tell the compiler to treat one data type as if it were a different type. Another term for cast is typecast .

Here's an example of a Char data type typecast to an Integer:

procedure TForm1.Button1Click(Sender: TObject);
var
AChar : Char;
AnInteger : Integer;
begin
AChar := `A';
AnInteger := Integer(AChar);
Label1.Caption := IntToStr(AnInteger);
end;

In this example, the cast Integer(AChar) tells the compiler to convert the value
of AChar to an Integer data type. The cast is necessary because you can't assign
the value of a Char data type to an Integer type. If you attempt to make the assignment
without the cast, the compiler will issue an error that reads Incompatible types:
`Integer' and `Char'.
By the way, when the preceding code executes, the label will display the text
65 (65 is the integer value of the character A).
It is not always possible to cast one data type to another. Take this code, for
example:

procedure TForm1.Button1Click(Sender: TObject);
var
Pi : Double;
AnInteger : Integer;
begin
Pi := 3.14;
AnInteger := Integer(Pi);
Label1.Caption := IntToStr(AnInteger);
end;

In this case, I am trying to cast a Double to an Integer. This is not a valid cast, so the compiler will issue an error that reads Invalid typecast. To convert a floating-point value to an integer value, use the Trunc, Floor, or Ceil functions.
These functions do just as their names indicate, so I don't need to explain further. See the Delphi help for more information on these functions.

Pointers can be cast from one type to another using the as operator. (Pointers
are discussed in the next section.) I'll discuss the as operator later in the section
& quot;Class Keywords: is and as. & quot;

Pointers
Pointers are one of the most confusing aspects of the Object Pascal language.
So what is a pointer? It's a variable that holds the address of another variable.
There, that wasn't so bad, was it? I wish it were that simple! Because a pointer
holds the address of another variable, it is said to & quot;point to & quot; the second
variable. This is called indirection because the pointer does not have a direct
association with the actual data, but rather an indirect association.
New Term: A pointer is a variable that holds the address of another
variable.
Let's look at an example. Let's say you have a record, and you need to pass the
address of that record to a procedure requiring a pointer. You take the address of
a record instance using the @ operator. Here's how it looks:

var
MLRecord : TMailingListRecord;
APtr : Pointer;
begin
{ Fill MLRecord with data. }
APtr := @MLRecord;
SomeFunction(APtr);
end;

The APtr variable (which is of type Pointer) is used to hold the memory address of the MLRecord record. This type of pointer is called an untyped pointer because the Pointer data type simply holds a memory address. Another type of pointer
is a pointer that is declared as a pointer to a specific type of object. For example, let's say that you create a new type, a pointer to TMailingListRecord record. The declaration would look like this:

type
PMailingListRecord = ^TMailingListRecord;
TMailingListRecord = record
FirstName : string;
LastName : string;
Address : string;
City : string;
State : string;
Zip : Integer;
end;

The type PMailingListRecord is declared as a pointer to a TMailingListRecord.
You will often see records and their corresponding pointers declared in this way.
You might be wondering what the point is (no pun intended). Let's go on to the next
section and I'll show you one way pointers are used.





NOTE: I almost never use long strings in records as I have done here with
the TMailingListRecord. I usually use an array of Char rather than a long string.
The reason for this is that long strings are dynamically allocated and are not a
fixed size. Fixed-size fields are important if you are writing records to disk. I
used long strings in the case of TMailingListRecord because I didn't want to muddy
the waters with a discussion on fixed-length records at this point in the book.





Local Versus Dynamic Memory Usage
Yesterday when you read about records, I showed you some examples. All of those
examples used local allocation of objects. That is, the memory required for the record
variable was obtained from the program's stack.
New Term: Local allocation means that the memory required for
a variable or object is obtained from the program's stack.
New Term: The stack is an area of working memory set aside by
the program when the program starts.
Any memory the program needs for things such as local variables, function calls,
and so on is taken from the program's stack. This memory is allocated as needed and
then freed when it is no longer needed; usually this happens when the program enters
a function or other local code block. Memory for any local variables the function
uses is allocated when the function is entered. When the function returns, all the
memory allocated for the function's use is freed. It all happens for you automatically;
you don't have to give any thought to how the memory is freed or whether the memory
is freed at all.
Local allocation has its good points and its bad points. On the plus side, memory
can be allocated from the stack very quickly. The negative side is that the stack
is a fixed size and cannot be changed as the program runs. If your program runs out
of stack space, weird things start to happen. Your program might crash, it might
start behaving oddly, or it might seem to perform normally but crash when the program
terminates. This is less of a problem in the 32-bit world than in 16-bit programming,
but it's still a consideration.
For things like variables of the built-in data types and small arrays, there is
no point in doing anything other than local allocation. But if you are going to be
using large records, you will probably want to use dynamic allocation from the heap.
The heap amounts to your computer's free physical RAM plus all your free hard disk
space. In other words, you can easily have 100MB of heap memory available on a typical
Windows system. The good news here is that you have virtually unlimited memory available
for your programs. The bad news is that memory allocated dynamically requires some
additional overhead and, as such, is just a smidgen slower than memory allocated
from the stack. In most programs the extra overhead is not noticed in the least.
An additional drawback of dynamic allocation is that it requires more from the programmer--not
a lot more, mind you, but a little.
New Term: Dynamic allocation means that memory required for an
object is allocated from the heap.

New Term: The heap in a Windows program refers to all of your computer's virtual memory.

Dynamic Allocation and Pointers
In an Object Pascal program, memory can be allocated dynamically in several different
ways. Perhaps the best way is to use the AllocMem function. AllocMem allocates memory
and fills the allocated memory with zeros. (Other ways to dynamically allocate memory
include the GetMem procedure and the New function.) All things considered, AllocMem
probably provides the best way of allocating memory dynamically. Let's go back to
the TMailingListRecord record. In previous examples, I allocated memory for one of
these records from the stack like this:

var
MLRecord : TMailingListRecord;
begin
{ Fill MLRecord with data. }
MLRecord.FirstName := `Per';
MLRecord.LastName := `Larsen';
{ etc. }
end;

Now I'll create the record dynamically rather than locally:

var
APtr : PMailingListRecord;
begin
APtr := AllocMem(SizeOf(TMailingListRecord));
APtr.FirstName := `Per';
APtr.LastName := `Larsen';
{ Do some other things. }
FreeMem(APtr);

end;
Notice that this time I declare a PMailingListRecord (a pointer to a TMailingListRecord)
rather than a TMailingListRecord itself. Also notice that I allocate memory for the
structure by calling the AllocMem function. The parameter passed to AllocMem is the
amount of memory to allocate. The SizeOf function returns the size of the record,
so I use that function to determine how much memory to allocate. The call to AllocMem
allocates memory and initializes the pointer by creating a new instance of a TMailingListRecord
dynamically. After the memory has been allocated, you can use the pointer variable
just as you do a regular variable. Finally, notice that after I am done with the
object, I free the memory associated with the object by using the FreeMem procedure.
Failure to call FreeMem to free dynamically allocated objects will result in a program
that leaks memory (uses up memory that it never releases).
This is the process by which you dynamically create and access records in Object
Pascal. You probably won't use dynamic allocation very much, but sometimes it's necessary,
so you should know how it's done.





NOTE: Dynamic allocation of memory for records and arrays is optional.
It is mandatory for classes. I'll discuss that in just a bit when I talk about classes.





NOTE: The nil keyword is used to specify a pointer that has no value. If
you want to clear a pointer of its value, you use the nil keyword like this:
SomePointer := nil;







You can also use nil to test a pointer to see whether it has been allocated:





if SomePointer = nil then
SomePointer := AllocMem(Size);

This code checks a pointer to see whether it has been assigned a value. If it hasn't been assigned a value, then memory is allocated for the pointer.

Dereferencing a Pointer
Sometimes you need to dereference a pointer.
New Term: Dereferencing a pointer means retrieving the object
that the pointer points to.
Let's say that you dynamically created a mailing list record as described earlier.
Now you want to assign the contents of that mailing list record to another mailing
list record variable that was allocated from the stack. Here's what you have so far:

var
APtr : PMailingListRecord;
Rec : TMailingListRecord;
begin
APtr := AllocMem(SizeOf(TMailingListRecord));

Now let's say you want to copy the contents of APtr to the Rec variable. The APtr
variable is a pointer to a TMailingListRecord and the Rec variable is a TMailingListRecord.
You might try this:

Rec := APtr;

That won't work, however, because APtr contains a memory address, not a TMailingListRecord. In order to make this assignment, you have to dereference the pointer by using the pointer operator (^). It looks like this:

Rec := APtr^;

When you dereference a pointer, you are telling the compiler, & quot;Give me the
object pointed to by the pointer and not the value of the pointer itself. & quot;

What's a Class?
A class is a collection of fields and methods (functions and procedures)
that work together to accomplish a specific programming task. In this way a class
is said to encapsulate the task. Classes have the following features:


The capability to control access

Constructors

Destructors

Fields

Methods (procedures and functions)


A hidden, special pointer called Self


Before diving into an explanation of these features, let me give you a quick example of how a class might be used. Let's use a typical Windows control as an example--a check box, for instance. A class that represents a check box would have fields
for the caption of the check box and for the state (checked or unchecked). This class would also have methods that enable you to set and query both the check box caption and the check state. These methods might be named GetCheck, SetCheck, GetCaption,
and SetCaption. After the class has been written, you can create an instance of the class to control a check box in Windows. (It's not quite that simple, but this is just an example after all.) If you have three check boxes, you would have three
instances of the CheckBox class that could then be used to control each check box individually.

var
Check1 : TMyCheckBox;
Check2 : TMyCheckBox;
Check3 : TMyCheckBox;
begin
Check1 := TMyCheckBox.Create(ID_CHECK1);
Check2 := TMyCheckBox.Create(ID_CHECK2);
Check3 := TMyCheckBox.Create(ID_CHECK3);
Check1.SetCaption(`Thingamabob Option');
Check1.SetCheck(True);
Check2.SetCaption(`Doohickey Options');
Check2.SetCheck(False);
Check3.SetCaption(`Whodyacallum Options');
Check3.SetCheck(True);
if Check1.GetCheck then DoThingamabobTask;
if Check2.GetCheck then DoDoohickeyTask;
{ etc. }
end;

In this example, each instance of the class is a separate object. Each instance
has its own fields, and the objects operate independently of one another. They are
all objects of the same type but are separate instances in memory. With that brief
introduction, you can roll up your sleeves once more and go to work on understanding
classes.





NOTE: The previous example might have been more clear if I had used properties
rather than methods called SetCheck, GetCheck, and SetCaption. I didn't because I'm
not ready to talk about properties in detail at this time. In fact, most of this
chapter will talk about classes without much emphasis on properties. I'll talk about
properties more on Day 5.





Anatomy of a Class
A class, like a record, has a declaration. The class declaration is always in
a type section.

Class Access Levels
Classes can have four levels of access:


Private

Public

Protected

Published


Each of these access levels is defined in this section.
Class access levels control how a class is used. As a single programmer, you might
be not only the class's creator but also a user of the class. In team programming
environments, one programmer might be the creator of the class and other programmers
the users of the class. To understand the role that levels of access play in class
operation, you first need to understand how classes are used.
In any class there is the public part of the class, which the outside world
has access to, and there is the private part of a class. The private part
of a class is the internal implementation of the class--the inner workings, so to
speak.
Part of a well-designed class includes hiding anything from public view that the
user of the class doesn't need to know.
New Term: Data abstraction is the hiding of internal implementations
within the class from outside views.
Data abstraction prevents the user from knowing more than he or she needs to know
about the class and also prevents the user from messing with things that shouldn't
be messed with. For example, when you get in your car and turn the key to start it,
do you want to know every detail about how the car operates? Of course not. You only
want to know as much as you need to know to operate the car safely. In this analogy,
the steering wheel, pedals, gear shift lever, speedometer, and so on represent the
public interface between the car and the driver. The driver knows which of those
components to manipulate to make the car perform the way he or she wants.
Conversely, the engine, drive train, and electrical system of the car are hidden
from public view. The engine is tucked neatly away where you never have to look at
it if you don't want to. It's a detail that you don't need to know about, so it is
hidden from you--kept private, if you prefer. Imagine how much trouble driving would
be if you had to know everything the car was doing at all times: Is the carburetor
getting enough gas? Does the differential have enough grease? Is the alternator producing
adequate voltage for both the ignition and the radio to operate? Are the intake valves
opening properly? Who needs it! In the same way, a class keeps its internal implementation
private so the user of the class doesn't have to worry about what's going on under
the hood. The internal workings of the class are kept private and the user interface
is public.
The protected access level is a little harder to explain. Protected class
members, like private class members, cannot be accessed by users of the class. They
can, however, be accessed by classes that are derived from this class. Continuing
with the car analogy, let's say you want to extend the car (literally) by making
it a stretch limousine. To do this, you need to know something about the underlying
structure of the car. You need to know how to modify the drive shaft and frame of
the car at the very minimum. In this case you would need to get your hands dirty
and, as a limousine designer, get at the parts of the car that were previously unimportant
to you (the protected parts).
The internal workings of the engine are still kept private because you don't need
to know how the engine works to extend the frame of the car. Similarly, most of the
public parts of the car remain the same, but you might add some new public elements
such as the controls for the intercom system. I've strayed a little here and given
you a peek in to what is called inheritance , but I won't go in to further
details right now. I will talk more about protected access a little later in the
section & quot;Methods, & quot; and about inheritance in the section & quot;Inheritance. & quot;
The point here is that the protected section of a class contains the parts of a class
that someone extending the class will need to know about.
The published access level is used when writing components. Any components
declared in the published section will appear in the Object Inspector at design time.
I'll talk more about the published section on Day 20, & quot;Creating Components. & quot;
The Object Pascal language has four keywords that pertain to class access. The
keywords are (not surprisingly) public, private, protected, and published. You specify
a class member's access level when you declare the class. A class is declared with
the class keyword. Here's an example:

TVehicle = class
private
CurrentGear : Integer;
Started : Boolean;

Speed : Integer;

procedure StartElectricalSystem;
procedure StartEngine;
protected
procedure StartupProcedure;
public
HaveKey : Boolean;
Start : Boolean;
procedure SetGear(Gear : Integer);
procedure Accelerate(Acceleration : Integer);
procedure Brake(Factor : Integer);
procedure Turn(Direction : Integer);
procedure ShutDown;
end;

Notice how you break the class organization down into the three access levels.
You might not use all of the access levels in a given class. For example, I am not
using the published access level in this example. You are not required to use any
of the access levels if you don't want, but typically you will have a public and
a private section at the least.

Constructors
Classes in Object Pascal have a special method called the constructor.
New Term: The constructor is a method that is used to create
an instance of a class.
The constructor is used to initialize any class member variables, allocate memory
the class will need, or do any other startup tasks. The TVehicle example you just
saw does not have a constructor. If you don't provide a constructor, you can use
the base class's constructor when you create the class. (If not otherwise specified,
all Object Pascal classes are derived from TObject. The TObject class has a constructor
called Create, so it is this constructor that will be called if you don't provide
a constructor. I'll discuss base classes and inheritance later in the section & quot;Inheritance. & quot;)
Although using the base class's constructor is fine for simple classes, you will
almost always provide a constructor for classes of any significance. The constructor
can be named anything, but it must be declared using the constructor keyword. This
is what distinguishes it as a constructor. Given that, let's add a constructor declaration
to the TVehicle class:
TVehicle = class

private
CurrentGear : Integer;
Started : Boolean;
Speed : Integer;
procedure StartElectricalSystem;
procedure StartEngine;
protected
procedure StartupProcedure;
public
HaveKey : Boolean;
Start : Boolean;
procedure SetGear(Gear : Integer);
procedure Accelerate(Acceleration : Integer);
procedure Break(Factor : Integer);
procedure Turn(Direction : Integer);
procedure ShutDown;
constructor Create; { the constructor }
end;

Notice that the constructor is a special type of method. It does not have a return
type because a constructor cannot return a value. If you try to add a return type
to the constructor declaration, you will get a compiler error.
A class can have more than one constructor. This can be accomplished in two different
ways. The first way is to simply give the constructor a different name--for example,

TVehicle = class
{ rest of class deleted }
constructor Create;
constructor CreateModel(Model : string);
end;

This example shows two constructors, one called Create and the other called CreateModel.
Another way to declare multiple constructors is through method overloading, which
I discussed yesterday. Here is an example that uses constructors with the same name,
but with different parameters:
TVehicle = class

{ rest of class deleted }
constructor Create; overload;
constructor Create(AOwner : TObject); overload;
end;

Because method overloading is new in Delphi 4, I don't expect to see this way
of declaring multiple constructors used very much in Delphi programs. The traditional
method is to declare constructors with different names, and I suspect that trend
will continue for quite some time. Still, both methods are legal and either one can
be used.





NOTE: If you create components for the retail market, you should be sure
that your components' constructors have different parameter lists. This will ensure
that your components will work with C++Builder as well as with Delphi (C++Builder
does not have named constructors, so method overloading is used to differentiate
constructors). Even if you don't plan on selling your components to the C++Builder
market, you'd be wise to plan ahead for the possibility.





What's the point of multiple constructors? Multiple constructors provide different
ways of creating a class. For instance, a class can have a constructor that takes
no parameters and a constructor that takes one or more parameters to initialize fields
to certain values. For example, let's say you have a class called TMyRect that encapsulates
a rectangle (rectangles are frequently used in Windows programming). This class could
have several constructors. It could have a default constructor that sets all the
fields to 0, and another constructor that enables you to set the class's fields through
the constructor. First, let's take a look at how the class declaration might look:

TMyRect = class
private
Left : Integer;
Top : Integer;
Right : Integer;
Bottom : Integer;
public
function GetWidth : Integer;
function GetHeight : Integer;
procedure SetRect(ALeft, ATop, ARight, ABottom : Integer);
constructor Create;
constructor CreateVal(ALeft, ATop, ARight, ABottom : Integer);

end;
The definitions for the constructors would look something like this:

constructor TMyRect.Create;
begin
inherited Create;
Left := 0;
Top := 0;
Right := 0;
Bottom := 0;
end;
constructor TMyRect.CreateVal(ALeft, ATop, ARight, ABottom : Integer);
begin
inherited Create;
Left := ALeft;
Top := ATop;
Right := ARight;
Bottom := ABottom;
end;

The first constructor simply initializes each field to 0. The second constructor
takes the parameters passed and assigns them to the corresponding class fields. The
variable names in the parameter list are local to the constructor, so each of the
variable names begins with an A to differentiate between the local variables
and the class fields (the use of the leading A is customary for Delphi programs).
Notice the use of the inherited keyword in the constructors. I'll talk about the
inherited keyword later in the section & quot;Inheritance. & quot; I wanted to point
it out here just so you would know I'm not leaving you in the dark.





NOTE: It's not strictly necessary to initialize the fields to 0 as the
Create constructor does here. All fields are automatically initialized to 0 when
an object of the class is created.





New Term: Instantiation is the creation of an object, called
an instance , of a class.

So how do you use one of these constructors instead of the other? You do that when you instantiate an instance of a class. The following code snippet creates two instances of the TMyRect class. The first uses the Create constructor and the second
uses the CreateVal constructor:

var
Rect1 : TMyRect;
Rect2 : TMyRect;
begin
Rect1 := TMyRect.Create;
Rect2 := TMyRect.CreateVal(0, 0, 100, 100);
end;

You can have as many constructors as you like as long as they all have different
names or, if overloaded, as long as they follow the rules of method overloading.
There is one thing that I need to point out about the previous example: Both instances
of the TMyRect class are allocated dynamically. Earlier I said that you allocate
memory for an object dynamically by calling the GetMem procedure. Now I seem to be
contradicting myself, but in truth I am not. The reason is that memory for Object
Pascal classes is always allocated dynamically. Although that is not true
of records, it is true of classes. That also means that the previous code snippet
leaks memory because I didn't free the memory associated with the two classes. I'll
talk about that next. Because all Object Pascal classes are created on the heap,
all class variables are, therefore, pointers. The Rect1 and Rect2 variables in the
preceding example are both pointers to the TMyRect class.

Destructors
New Term: The destructor is a special method that is automatically
called just before the object is destroyed.
The destructor can be considered the opposite of the constructor. It is usually
used to free any memory allocated by the class or do any other cleanup chores. A
class is not required to have a destructor because the base class's destructor can
be used instead. Like a constructor, a destructor has no return value.
Although a class can have multiple destructors, it is not something that is typically
done. If you have just one destructor, you should name it Destroy. This is more than
just tradition. When you free an instance of a class (remove it from memory), you
call the Free method. Free is a method of the TObject class that calls the class's
Destroy method just before the class is removed from memory. This is the typical
way to free the memory associated with a class. Here's an example:

Rect1 := TMyRect.Create;
{ Do some things with Rect1. }
{ ... }
{ Now delete Rect1. }
Rect1.Free;

The example in the & quot;Constructors & quot; section would actually leak memory because the two TMyRect objects were never freed.

The following shows the updated code for the TMyRect class, complete with destructor
(some code removed for brevity):

TMyRect = class
private
Left : Integer;
Top : Integer;
Right : Integer;
Bottom : Integer;
Text : PChar; { new field }
public
function GetWidth : Integer;
function GetHeight : Integer;
procedure SetRect(ALeft, ATop, ARight, ABottom : Integer);
constructor Create;
constructor CreateVal(ALeft, ATop, ARight, ABottom : Integer);
destructor Destroy; override;
end;
constructor TMyRect.Create;
begin
inherited Create;
{ Allocate memory for a null-terminated string. }
Text := AllocMem(1024);
end;
destructor TMyRect.Destroy;
begin
{ Free the allocated memory. }
FreeMem(Text);
inherited Destroy;
end;

The modified version of the TMyRect class allocates storage for a null-terminated
string (a PChar) named Text in its constructor and frees that storage in the destructor.
(I can't think of a good reason for a class that handles rectangles to have a text
field, but you never know! It's just an example, after all.)
Take a closer look at the declaration of the destructor in the TMyRect class declaration.
It looks like this:

destructor Destroy; override;

Notice the override keyword at the end of the declaration. This keyword tells the compiler that you are overriding a method that is also found in the base class. I'm getting ahead of myself again, so I'll continue this discussion later in the
section entitled & quot;Inheritance. & quot; (I keep saying that, so I'll bet you expect that section to be really good!)






NOTE: Typically, you will call inherited as the first statement in your
constructor and the last statement in your destructor.





Data Fields
Data fields of a class are simply variables that are declared in the class
declaration; they can be considered as variables that have class scope. Fields in
classes are essentially the same as fields in records except that their access can
be controlled by declaring them as private, public, or protected. Regardless of a
field's access, it is available for use in all methods of the class. Depending on
the field's access level, it can be visible outside the class as well. Private and
protected fields, for example, are private to the class and cannot be seen outside
the class. Public fields, however, can be accessed from outside the class but only
through an object. Take the TMyRect class declared previously, for example. It has
no public fields. You could try the following, but you'll get a compiler error:

Rect := TMyRect.CreateVal(0, 0, 100, 100);
Rect.Left := 20; { compiler error! }

The compiler error will say Undeclared identifier: `Left'. The compiler is telling
you that Left is a private field, and you can't get to it. If Left were in the public
section of the class declaration, this code would compile.





NOTE: The preceding discussion of private data fields holds true if the
TMyRect class were declared in a separate unit, but not true if the TMyRect class
is declared in the unit where it is used. Classes contained in the same unit have
what are sometimes called friend privileges , which means that the classes
can access each other's private data fields. This applies only to classes declared
in the same unit.





Object Pascal uses properties to control the access to private fields. A property can be read/write, read-only, or write-only (although write-only properties are rare). A property can have a read method that is called when the property is read,
and a write method when a property is written to. Neither is required, however, because a property can have direct access to the private field. These read and write methods are called any time the property is accessed. The write method is particularly
important, as it can be used to validate input or to carry out other tasks when the property is assigned a value. In this way the private field is never accessed directly, but always through a property. I'm getting ahead of myself again so I'll leave
it at that for now. Properties are discussed in detail on Day 5.






NOTE: Some OOP extremists say that fields should never be public. They
would advise you to use properties to access all fields. On the other end of the
spectrum is the group that recommends making all your fields public. The truth lies
somewhere in between. Some fields are noncritical and can be left public if doing
so is more convenient. Other fields are critical to the way the class operates and
should not be made public. If you are going to err, it is better to err on the side
of making fields private.





When you create an instance of a class, each class has its own data. You can assign
a value to the variable Left in one instance of a class and assign a different value
to the Left variable in a different instance of the class--for example,

Rect1 := TMyRect.CreateVal(100, 100, 500, 500);
Rect2 := TMyRect.CreateVal(0, 0, 100, 100);

This code creates two instances of the TMyRect class. Although these two instances
are identical in terms of their structure, they are completely separate in memory.
Each instance has its own data. In the first case, the Left field would have a value
of 100. In the second case it would have a value of 0. It's like new cars on the
showroom floor: Every model of a particular car comes from the same mold, but they
are all separate objects. They all vary in their color, upholstery style, features,
and so on.

Methods
Methods are functions and procedures that belong to your class. They are
local to the class and don't exist outside the class. Methods can be called only
from within the class itself or through an instance of the class. They have access
to all public, protected, and private fields of the class. Methods can be declared
in the private, protected, or public sections of your class. Good class design requires
that you think about which of these sections your methods should go into.
* Public methods , along with properties, represent the user interface to
the class. It is through the public methods that users of the class access the class
to gain whatever functionality it provides. For example, let's say you have a class
that plays and records waveform audio. Public methods might include methods named
Open, Play, Record, Save, Rewind, and so on.


Private methods are methods that the class uses internally to & quot;do
its thing. & quot; These methods are not intended to be called by users of the class;
they are private in order to hide them from the outside world. Frequently a class
has startup chores to perform when the class is created (for example, you have already
seen that the constructor is called when a class is created). In some classes the
startup processing might be significant, requiring many lines of code. To remove
clutter from the constructor, a class might have an Init method that is called from
the constructor to perform those startup tasks. This method would never be called
directly by a user of the class. In fact, more than likely bad things would happen
if this method were called by a user at the wrong time, so the method is private
in order to protect both the integrity of the class and the user.

Protected methods are methods that cannot be accessed by the outside world
but can be accessed by classes derived from this class. I haven't talked yet about
classes being derived from other classes; I'll save that discussion for a little
later when it will make more sense. I discuss deriving classes in the section & quot;Inheritance. & quot;


Methods can be declared as class methods . A class method operates more
like a regular function or procedure than a method of a class. Specifically, a class
method cannot access fields or other methods of the class. (In just a bit I'll tell
you why this restriction exists.) Most of the time, you will not use class methods,
so I won't go into any detail on them.

About Self
New Term: All classes have a hidden field called Self. Self is a pointer
to the instance of the class in memory.
Obviously, this will require some explanation. First, let's take a look at how
the TMyRect class would look if Self were not a hidden field:
TMyRect = class

private
Self : TMyRect;
Left : Integer;
Top : Integer;
Right : Integer;
Bottom : Integer;
Text : PChar;
public
function GetWidth : Integer;
function GetHeight : Integer;
procedure SetRect(ALeft, ATop, ARight, ABottom : Integer);
constructor Create;
constructor CreateVal(ALeft, ATop, ARight, ABottom : Integer);
destructor Destroy; override;
end;

This is effectively what the TMyRect class looks like to the compiler. When a
class object is created, the Self pointer is automatically initialized to the address
of the class in memory:

Rect := TMyRect.CreateVal(0, 0, 100, 100);
{ Now `Rect' and `Rect.Self' have the same value }
{ because both contain the address of the object in memory. }

& quot;But, & quot; you ask, & quot;what does Self mean? & quot; Remember that each
class instance gets its own copy of the class's fields. But all class instances share
the same set of methods for the class (there's no point in duplicating that code
for each instance of the class). How does the compiler figure out which instance
goes with which method call? All class methods have a hidden Self parameter that
goes with them. To illustrate, let's say you have a function for the TMyRect class
called GetWidth. It would look like this:
function TMyRect.GetWidth : Integer;

begin
Result := Right - Left;
end;

That's how the function looks to you and me. To the compiler, though, it looks
something like this:

function TMyRect.GetWidth : Integer;
begin
Result := Self.Right - Self.Left;
end;

That's not exactly accurate from a technical perspective, but it's close enough
for this discussion. In this code you can see that Self is working behind the scenes
to keep everything straight for you. You don't have to worry about how that happens,
but you need to know that it does happen.





CAUTION: Never modify the Self pointer. You can use it to pass a pointer
to your class to other methods or as a parameter in constructing other classes, but
don't change its value. Learn to treat Self as a read-only variable.





Although Self works behind the scenes, it is still a variable that you can access from within the class. As an illustration, let's take a quick peek into VCL. Most of the time, you will create components in VCL by dropping them on the form at
design time. When you do that, Delphi creates a pointer to the component and does all sorts of housekeeping chores on your behalf, saving you from concerning yourself with the technical end of things. Sometimes, however, you will create a component at
runtime. VCL insists (as all good frameworks do) on keeping track of which child objects belong to which parent. For example, let's say you want to create a button on a form when another button is clicked. You need to tell VCL what the parent of the
new button is. The code would look like this:

procedure TForm1.Button1Click(Sender: TObject);
var
Button : TButton;
begin
Button := TButton.Create(Self);
Button.Parent := Self;
Button.Left := 20;
Button.Top := 20;
Button.Caption := `Click Me';
end;

In this code, you can see that Self is used in the constructor (this sets the
Owner property of the button, but I'll get into that later when I cover VCL components
on Day 7, & quot;VCL Components & quot;) and also that it is assigned to the Parent
property of the newly created button. This is how you will use the Self pointer the
vast majority of the time in your Delphi applications.





NOTE: Earlier I said that class methods can't access class fields. The
reason this is true is because class methods don't have a hidden Self parameter;
regular methods do. Without Self, a method cannot access class fields.





Don't worry too much about Self right now. When you begin to use VCL, it will
quickly become clear when you are required to use Self in your Delphi applications.

A Class Example
Right now it would be nice if you could see an example of a class. Listing 3.1 shows a unit that contains a class called TAirplane. This class could be used by an aircraft controller program. The class enables you to command an airplane by
sending it messages. You can tell the airplane to take off, to land, or to change its course, altitude, or speed. First take a look at the unit and then I'll discuss what is going on within this class.

LISTING 3.1. AIRPLANU.PAS.
unit AirplanU;
interface
uses
SysUtils;
const
{ Airplane types. }
Airliner = 0;
Commuter = 1;
PrivateCraft = 2;
{ Status constants. }
TakingOff = 0;
Cruising = 1;
Landing = 2;
OnRamp = 3;
{ Message constants. }
MsgChange = 0;
MsgTakeOff = 1;
MsgLand = 2;
MsgReport = 3;
type
TAirplane = class
private
Name : string;
Speed : Integer;
Altitude : Integer;
Heading : Integer;
Status : Integer;
Kind : Integer;
Ceiling : Integer;
protected
procedure TakeOff(Dir : Integer); virtual;
procedure Land; virtual;
public
constructor Create(AName : string; AKind : Integer = Airliner);
function SendMessage(Msg : Integer; var Response : string;
Spd : Integer; Dir : Integer; Alt : Integer) : Boolean;
function GetStatus(var StatusString : string) : Integer; overload; & #194;virtual;
function GetStatus : Integer; overload;
function GetSpeed : Integer;

function GetHeading : Integer;

function GetAltitude : Integer;
function GetName : string;
end;
implementation
constructor TAirplane.Create(AName : string; AKind : Integer);
begin
inherited Create;
Name := AName;
Kind := AKind;
Status := OnRamp;
case Kind of
Airliner : Ceiling := 35000;
Commuter : Ceiling := 20000;
PrivateCraft : Ceiling := 8000;
end;
end;
procedure TAirplane.TakeOff(Dir : Integer);
begin
Heading := Dir;
Status := TakingOff;
end;
procedure TAirplane.Land;
begin
Speed := 0;
Heading := 0;
Altitude := 0;
Status := OnRamp;
end;
function TAirplane.SendMessage(Msg : Integer; var Response : string;
Spd : Integer; Dir : Integer; Alt : Integer) : Boolean;
begin
Result := True;
{ Do something based on which command was sent. }
case Msg of
MsgTakeOff :
{ Can't take off if already in the air! }
if status & lt; & gt; OnRamp then begin
Response := Name + `: I''m already in the air!';
Result := False;
end else
TakeOff(dir);
MsgChange :
begin
{ Check for bad commands and exit if any found. }
if Spd & gt; 500 then
Response := `Command Error: Speed cannot be more than 500.';
if Dir & gt; 360 then
Response := `Command Error: Heading cannot be over 360 & #194;degrees.';
if Alt & lt; 100 then
Response := Name + `: I''d crash!';
if Alt & gt; Ceiling then
Response := Name + `: I can''t go that high.';
if (Spd = 0) and (Dir = 0) and (Alt = 0) then
Response := Name + `: Huh?';
if Response & lt; & gt; `' then begin
Result := False;
Exit;
end;
{ Can't change status if on the ground. }
if status = OnRamp then begin
Response := Name + `: I''m on the ground.';
Result := False;
end else begin
Speed := Spd;
Heading := Dir;
Altitude := Alt;
Status := Cruising;
end;
end;
MsgLand :
{ Can't land if already on the ground. }
if status = OnRamp then begin
Response := Name + `: I''m already on the ground.';

Result := False;

end else
Land;
MsgReport :
begin
GetStatus(Response);
Exit;
end;
end;
{ Standard response if all went well. }
if Result then
Response := Name + `: Roger.';
end;
function TAirplane.GetStatus(var StatusString : string) : Integer;
begin
StatusString := Format(`%s, Altitude: %d, Heading: %d, ` +
`Speed: %d', [Name, Altitude, Heading, Speed]);
Result := Status;
end;
function TAirplane.GetStatus : Integer;
begin
Result := Status;
end;
function TAirplane.GetSpeed : Integer;
begin
Result := Speed;
end;
function TAirplane.GetHeading : Integer;
begin
Result := Heading;
end;
function TAirplane.GetAltitude : Integer;
begin
Result := Altitude;
end;
function TAirplane.GetName : string;
begin
Result := Name;
end;
end.

ANALYSIS: Look first at the class declaration in the interface section.
Notice that the TAirplane class has one overloaded function called GetStatus. When
called with a string parameter, GetStatus will return a status string as well as
the status data member (the string parameter is a variable parameter). When called
without a parameter, it just returns Status. Note that the only way to access the
private fields is via the public methods. For example, you can change the speed,
altitude, and heading of an airplane only by sending it a message. To use an analogy,
consider that an air traffic controller cannot physically change an aircraft's heading.
The best he can do is send a message to the pilot and tell him to change to a new
heading.





NOTE: This class would benefit greatly from properties. As I said earlier,
I'm not ready to discuss properties in detail at this point in the book, so I'll
have to admit that this class could be much better than it is and move on.





Now turn your attention to the definition of the TAirplane class in the interface
section. The constructor performs some initialization chores. You have probably noticed
that the SendMessage function does most of the work. A case statement determines
which message was sent and takes the appropriate action. Notice that the TakeOff
and Land procedures cannot be called directly (they are protected) but rather are
called through the SendMessage function. Again, as an air traffic controller, you
can't make an aircraft take off or land, you can only send it a message telling it
what you want it to do.
There's something else here that I haven't discussed yet. Note the virtual keyword.
This specifies that the function is a virtual method.
New Term: A virtual method is a method that is automatically
called if a method of that name exists in the derived class.
I'll discuss virtual methods in the next section, but I wanted to point them out
to you now.
The book's code contains a program called Airport, which enables you to play air
traffic controller. (You can find the book's code at the Web site http://www.mcp.com/info.
Type in the book's ISBN: 0-672-31286-7.) The program first sets up an array of TAirplane
classes and then creates three instances of the TAirplane class. You can send messages
to any airplane by selecting the airplane, setting up the parameters for the message,
and then clicking the Execute button. Clicking the button results in a call to the
selected airplane's SendMessage function. When you send a message, you get a response
back from the airplane, and that response is displayed in a memo component. Run the
program and play with it to get a feel for how it works. Figure 3.1 shows the Airport
program running.

Inheritance
One of the most powerful features of classes in Object Pascal is that they can
be extended through inheritance.
New Term: Inheritance means taking an existing class and adding
functionality by deriving a new class from it.

New Term: The class you start with is called the base class or ancestor class , and the new class you create is called the derived class .

To illustrate, let's go back to the TAirplane class. The civilian and military
worlds are quite different, as you know. To represent a military aircraft, I can
derive a class from TAirplane and add functionality to it:

TMilitaryPlane = class(TAirplane)
private
TheMission : TMission;
constructor Create(AName : string; AType : Integer);
function GetStatus(var StatusString : string) : Integer; override;
protected
procedure TakeOff(Dir : Integer); override;
procedure Land; override;
procedure Attack; virtual;
procedure SetMission; virtual;
end;

A TMilitaryPlane has everything a TAirplane has, plus a few more goodies. Note
the first line of the class declaration. The class name in parentheses after the
class keyword is used to tell the compiler that I am inheriting from another class.
The class from which I am deriving this class is the base class and, in this case,
is the TAirplane class.





NOTE: When you derive a class from another class, the new class gets all
the functionality of the base class plus whatever new features you add. You can add
fields and methods to the new class, but you cannot remove anything from what the
base class offers.





You'll notice that in the private section there is a line that declares a variable
of the TMission class. The TMission class is not shown here, but it could encapsulate
everything that deals with the mission of a military aircraft: the target, navigation
waypoints, ingress and egress altitudes and headings, and so on. This illustrates
the use of a field that is an instance of another class. In fact, you'll see that
a lot when programming in Delphi.

Overriding Methods
I want to take a moment here to discuss virtual methods. Note that the TakeOff
procedure is a virtual method in the TAirplane class (refer to Listing 3.1). Notice
that TakeOff is called by SendMessage in response to the MsgTakeOff message. If the
TMilitaryPlane class did not provide its own TakeOff method, the base class's TakeOff
method would be called. Because the TMilitaryPlane class does provide a TakeOff
method, that method will be called rather than the method in the base class.
New Term: Replacing a base class method in a derived class is called
overriding the method.
In order for overriding to work, the method signature must exactly match that
of the method in the base class. In other words, the return type, method name, and
parameter list must all be the same as the base class method. In addition, the method
in the derived class must be declared with the override keyword.





NOTE: Object Pascal also has dynamic methods . Dynamic methods can
be treated the same as virtual methods as far as most programmers are concerned.
The difference is in the way the method pointers are stored in the class's virtual
method table (VMT). It's not important for you to understand the difference right
now, but I wanted you to know about dynamic methods in case you encounter them looking
through any of the Delphi examples or VCL source code. For the most part, you can
treat dynamic methods in your programs just as you would treat virtual methods.





You can override a method with the intention of replacing the base class method,
or you can override a method to enhance the base class method. Consider the TakeOff
method, for example. If you want to completely replace what the TakeOff method of
TAirplane does, you would override it and supply whatever code you want:

procedure TMilitaryPlane.TakeOff(Dir : Integer);
begin
{ New code here. }
end;

But if you want your method to take the functionality of the base class and add to it, you would first call the base class method and then add new code. Calling the base class method is done with the inherited keyword--for example,

procedure TMilitaryPlane.TakeOff(Dir : Integer);
begin
{ First call the base class TakeOff method. }
inherited TakeOff(Dir);
{ New code here. }
end;

By calling the base class method, you get the original behavior of the method
as written in the base class. You can then add code before or after the base class
call to enhance the base class method. Note that the TakeOff method is declared in
the protected section of the TAirplane class. If it were in the private section,
this would not work because even a derived class cannot access the private members
of its ancestor class. By making the TakeOff method protected, it is hidden from
the outside world but still accessible to derived classes.





NOTE: There is an exception to the rule of protected versus private
access. If a derived class is declared in the same unit as the base class, the private
fields and methods of the base class are available to the derived class. If the derived
class is declared in a separate unit, only protected fields and methods of the base
class are available to the derived class.





When you derive a class from another class, you must be sure to call the base
class's constructor so that all ancestor classes are properly initialized. Calling
the base class constructor is also done with the inherited keyword. Look again at
the constructor for the TAirplane class:

constructor TAirplane.Create(AName : string; AKind : Integer);
begin
inherited Create;
Name := AName;
Kind := AKind;
Status := OnRamp;
case Kind of
Airliner : Ceiling := 35000;
Commuter : Ceiling := 20000;
PrivateCraft : Ceiling := 8000;
end;

end;
Notice that the base class Create constructor is called to ensure that the class
is properly created. & quot;Hey, wait a minute! & quot; I can hear some of you saying.
& quot;The TAirplane class doesn't have a base class! & quot; Actually, it does. If
no base class is specified when the class is declared, the base class is automatically
TObject. Be sure to call the base class constructor in your class constructor. Figure
3.2 illustrates the concept of inheritance.
FIGURE 3.2. An
example of inheritance.
You can see in Figure 3.2 that the class called F16 is descended from the class
called MilitaryFighter. Ultimately, F16 is derived from TAirplane because TAirplane
is the base class for all of the airplane classes.

Class Keywords: is and as
Object Pascal has two operators that pertain specifically to classes. The is operator
is used to determine whether a class is of a specific type. Let's go back to the
example of the TAirplane and TMilitaryPlane classes. Let's say you have an instance
of a class called Plane. The class might be an instance of the TAirplane class, it
might be an instance of the TMilitaryPlane class, or it might be an instance of a
different class altogether. You can use the is operator to find out. For example:

if Plane is TMilitaryPlane then
Attack;

The is operator returns a Boolean value. If the variable is of the requested type, is returns True. If the variable is not of the requested type, is returns False. The is operator will also return True if the requested class type is an ancestor
of the variable. For example, because TMilitaryPlane is derived from TAirplane, the following will be True:

if Plane is TAirplane then
DoSomething;






NOTE: Because all classes are derived from TObject, the following will
always be True:
if AnyClass is TObject then

DoSomething;







The is operator is not used as much as the as operator. The as operator is used
to cast a pointer to a specific class type. It looks like this:
with Plane as TMilitaryPlane do







Attack;






The as operator is usually used in conjunction with the with operator (yes, this
conversation is a bit confusing). In this code snippet the Plane variable is a pointer
that could be an instance of the TAirplane class, the TMilitaryPlane class, or neither.
The as operator is used to cast the pointer to a TMilitaryPlane type and then the
Attack method is called. If the Plane variable is not an instance of the TMilitaryPlane
class (or one of its ancestor classes), this cast will fail and the Attack method
will not be called. If, however, the Plane variable is a pointer to an instance of
the TMilitaryPlane class, the cast will succeed and the Attack method will be called.

Summary
Today you have learned about classes in Object Pascal. A well-designed class is
easy to use and saves many programming hours. I'd even go so far as to say a well-designed
class is a joy to use--especially when it's your own creation.
The lessons of these first three days are important to understand as you progress
through this book. If they don't make complete sense to you yet, don't despair. As
you continue through the next days, you will see these concepts repeated and put
to use in programs that have more practical application than the short, incomplete
examples you've been working with thus far.





CAUTION: Learning Object Pascal can and will lead to brain overload! It's
natural and you shouldn't worry about it. You might put down this book for the evening,
turn out the lights, and think, & quot;I'll never get it. & quot; Trust me, you will.
Sometimes it's necessary to take a couple of days off and let it all soak in. In
fact, if I thought I could get by with it, I'd make Day 4 a blank chapter called
& quot;A Day of Rest. & quot; Take it a little at a time, and one of these days you'll
be just like Archimedes--you'll be running around your office or your house shouting
& quot;Eureka! & quot; because the light just came on in your head. But keep track of
your clothes, will you? The neighbors could be watching.





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 quiz questions in Appendix A, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A


Q How can I keep a method private to the outside world but enable derived
classes to call it?

A Make it protected. A protected method is not accessible to users of
your class but is accessible to derived classes.


Q What does data abstraction mean?


A Data abstraction means hiding the details of the class that the users
of the class don't need to see. A class might have dozens of fields and methods but
only a few that the user can see. Only make visible (public) the methods that a user
needs to know about to use the class.


Q What is an object?


A Effectively, an object is any block of code that can be treated as a
separate entity in your programs. An object in Object Pascal generally means a class.
In Delphi that definition is expanded to include VCL components. ActiveX controls
are also objects.


Q Can my class have more than one constructor?


A Yes. Your class can have as many constructors as needed.


Q Are all VCL objects pointers?


A Yes. Because all VCL objects are allocated from the heap, all VCL objects
are pointers.


Q Pointers confuse me. Am I alone?


A No! Pointers are one of the most confusing aspects of Object Pascal
programming. After you have used Delphi for a period of time, you will come to understand
pointers.



Quiz


1. How do you clear a set of all values?

2. What is the purpose of having private fields and methods?


3. How can you keep fields private and yet enable users to read and set
their values?


4. When is a class's destructor called?


5. What does it mean to override a method of the base class?


6. How can you override a base class method and still get the benefit
of the operation the base class method performs?


7. What operator is used to dereference a pointer?


8. Can a class contain other class instances as fields?


9. What keyword is used to specify a pointer that has no value?


10. What is the as keyword used for?



Exercises



1. Write a class that takes a person's height in inches and returns the
height in feet.

2. Derive a class from the class in exercise 1 that also returns the height
in meters, centimeters, or millimeters. (Hint: There are 25.4 millimeters in an inch.)

3. Take a day off. You've earned it!









& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > !info!.txt

===================================================
===== Plik pochodzi ze strony: ============
===== http://www.programowanie.of.pl ============
===================================================
===== SERVICE FOR PROGRAMMERS 20001 =========
===================================================


delphi4.zip > apb.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Appendix B -- Delphi Internet Resources








Teach Yourself Borland Delphi 4 in 21 Days







- B -


Delphi Internet Resources



INPRISE Corporation
Commercial Web Sites
User Web Sites
Newsgroups
Publications








Delphi has been around for several years now, so there are some very good sources
of information about it available on the Internet. These resources fall into four
basic categories: commercial Web sites (including Borland's own Web site), user Web
sites, newsgroups, and publications. What follows is a brief description of some
of these categories.
Be sure to check out my errata page for the book at www.home.turbopower.com/
~kentr/delphi, as well as the book's Web site for examples and sample code (www.mcp/com/info).

INPRISE Corporation
INPRISE Corporation (formerly Borland International) is the best source of information
on Borland products. The INPRISE Web site (www.inprise.com) has the latest news on
Delphi, any patches that are available, FAQ pages, and listings of vendors that sell
Delphi components or supporting utilities. Be sure to check this site from time to
time to see whether there is any new news on Delphi.

Commercial Web Sites
Commercial Web sites are sites that belong to companies that sell Delphi components
commercially. Table B.1 contains a partial list of sites (in no particular order)
that you might want to check out.

TABLE B.1. COMMERCIAL WEB SITES.



Company Name (Web Address)
Description/Features


TurboPower Software ( www.turbopower.com )
With more than 10 years in the business, TurboPower is the oldest vendor of tools
for Borland products. TurboPower specializes in Delphi and C++Builder components
and utilities. Its AsyncProfessional is the premier serial communications component
package.


Raize Software Solutions ( www.raize.com )
General-purpose VCL components.


DevSoft ( www.dev-soft.com )
Home of IP*Works, Internet components for C++Builder and Delphi.


Out and About Productions ( www.o2a.com )
DTalk speech enabling components.


Skyline Tools ( www.imagelib.com )
Home of ImageLib, an image manipulation component library.


QuSoft AS ( www.qusoft.com )
Makers of QuickReport.


TeeChart Pro ( www.teemach.com )
Home of the TeeChart component.


SCT Associates, Inc. ( www.sct-associates.com )
Home of the Ace Reporter.


Engineering Objects International ( www.inconresearch.com/eoi )
Engineering and Scientific components.


Luxent Development Corp. ( www.luxent.com )
Success Ware, Light Lib, and AS/400 Middleware.



This is by no means a complete list of commercial sites. For a more complete list,
refer to the INPRISE Delphi Tools Page at www.inprise.com/delphi/deltools.html.

User Web Sites
The following are Delphi user sites set up by Delphi users and for Delphi users.
The sites are listed in no particular order. These pages contain links to other pages,
so when you get started, you can enjoy many hours of browsing. In particular, the
Delphi Deli has a huge list of links.
Delphi32.com
www.delphi32.com
Dr. Bob's JBuilder, C++Builder, and Delphi Clinic
www.drbob42.com
The Delphi Deli
www.delphideli.com
The Delphi Super Page
sunsite.icm.edu.pl/delphi
Torry's Delphi Pages
www.torry.ru
The Delphi Games Creator
www.users.dircon.co.uk/~zeus

Newsgroups
INPRISE sponsors newsgroups so that Borland users can help one another with questions
about using Borland products. These are private newsgroups that INPRISE has set up
for its users. All you have to do is point your newsreader to forums.inprise.com,
and you will find thousands of messages in dozens of newsgroups. For complete information,
see the Newsgroups page on the INPRISE Web page (www.inprise.com/
newsgroups).

Publications
There are several Delphi periodical publications. Some of the major publications
are
listed here. Most of these sites offer a free trial issue:
Delphi Developer's Journal (The Cobb Group)
www.cobb.com/ddj
Visual Developer (Coriolis Group)
www.coriolis.com
Delphi Developer (Pinnacle Publishing)
www.pinpub.com/delphi
Delphi Informant (Informant Communications Group)
www.informant.com/delphi
Delphi Magazine
www.itecuk.com/delmag






& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > apa.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Appendix A -- Answers to the Quiz Questions









Teach Yourself Borland Delphi 4 in 21 Days







- A -


Answers to the Quiz Questions



Day 1
Day 2
Day 3
Day 4
Day 5
Day 6
Day 7
Day 8
Day 9
Day 10
Day 11
Day 12
Day 13
Day 14
Day 15
Day 16
Day 17
Day 18
Day 19
Day 20
Day 21
Bonus Day








This appendix provides the answers to the quiz sections at the end of each chapter.

Day 1



1. What is the filename extension of a Pascal unit?


.pas


2. What is the name of the keyword that marks the section in which variables
are declared?


var


3. What does the IntToStr function do?


The IntToStr function converts an integer value into a Pascal string.


4. What is the purpose of the uses list in a Pascal unit?


The uses list in a Pascal unit lists the units that this unit is dependent on.
The compiler needs to be able to see those units in order to compile the current
unit.


5. Are the following two declarations different? Why or why not?






var
top : Integer;

Top : Integer;







The declarations are identical because the Pascal language is not case sensitive.


6. How do you concatenate Pascal strings?


By using the + operator. (You can also use the StrCat function for null-terminated
strings.)


7. How can you embed a control character in a string?


By using the # symbol followed by the ASCII value of the character you want to
embed.


8. What is the maximum length of a short string?


255 characters.


9. Look at this line of code:






MyArray : array [0..10] of Byte;







How many bytes can this array hold?


11 bytes (0 through 10).


10. What is the index number of the first element of an array, 0 or 1?


That depends on how the array is declared. This array has a base index of 0:






Array1 : array [0..9] of Integer;







This array, however, has a base index of 1:






Array2 : array [1..10] of Integer;




Day 2



1. What statements are executed in the event an if expression evaluates
to True?


The statement immediately following the if statement. If a code block follows
an if statement, the entire code block will be executed.


2. How many return values can a function return?


One. On the other hand, if you implement variable parameters, a function can
effectively return more than one value.


3. Besides syntax, what is the difference between a while loop and a repeat
loop?


A while loop checks the conditional expression at the beginning of the loop.
A repeat loop checks the conditional expression at the end of the loop.


4. What do the Break and Continue procedures do?


The Break procedure is used to break out of a loop. The statement following the
body of the loop will be executed after a call to Break. The Continue procedure forces
program execution back to the top of the loop.


5. What is a global variable?


A variable that is in scope anywhere in the program. It can be accessed by any
function in the program.


6. Can a record contain a mixture of data types (Char, Integer, Word,
and so on)?


Yes, a structure can contain any number and type of data members.


7. How do you access the members of a record?


With the dot operator (.). Here's an example:






record.LastName = & quot;Noble & quot;;







8. How many functions or procedures can a program have?


There is no practical limit to the number of functions or procedures a program
can have.


9. Can a function call another function or procedure?


Yes, functions and procedures can, and often do, call other functions or procedures.


10. Is it legal to have arrays of records?


Yes, you can have arrays of records just as easily as you can have arrays of
integers, bytes, or strings.



Day 3



1. How do you clear a set of all values?


By assigning an empty set constructor to the set--like this:






Font.Style := [];






2. What is the purpose of having private fields and methods?

Private data members protect data from being modified directly by users of the
class. Private data members can be modified through public member functions or properties,
but not directly.


3. How can you keep fields private and yet enable users to read and set
their values?


By using methods or properties.


4. When is a class's destructor called?


The destructor is called when the object is destroyed.


5. What does it mean to override a method of the base class?


To override a method means to replace a method in the base class with a method
in your derived class. The new method must have the exact same name, parameters,
and return type to override the base class method.


6. How can you override a base class method and still get the benefit
of the operation the base class method performs?


Call the base class function from within the overridden function:






procedure MyClass.DoIt;
begin
Inherited DoIt;
{ do some other stuff }

end;







7. What operator is used to dereference a pointer?


The pointer operator (^).


8. Can a class contain other class instances as fields?


Yes. It's very common.


9. What keyword is used to specify a pointer that has no value?


The nil keyword.


10. What is the as keyword used for?


To cast a pointer from a derived type to an ancestor type, or vice versa.



Day 4



1. How do you invoke the main window's Customize dialog box?


Right-click any toolbar and choose Customize from the context menu.


2. When you have the Customize dialog box open, how do you add buttons
to a toolbar?


Simply drag an item from the Commands page to a toolbar and drop it where you
want the item to appear on the toolbar.


3. How do you remove buttons from a toolbar?


Drag unwanted buttons off the bottom of the toolbar and drop them.


4. What's the easiest way to place multiple components of the same type
on a form?


Hold the Shift key when you click the component in the Component palette. Each
time you click on the form, a new component will be placed.


5. What's the easiest way to place a component in the center of the form?


Double-click the component's button in the Component palette.


6. List the file types needed to build an application in Delphi.


The .dpr, .pas, and .dfm files.


7. What VCL method do you use to display a form modelessly?


The Show method.


8. What VCL method do you use to display a form modally?


The ShowModal method.


9. How can you attach an event to an event handler that has been previously
defined?


In the Object Inspector, switch to the Events page. In the value column next
to the event, click the drop-down arrow button. A list of compatible event handlers
is displayed. Choose one.


10. When using the Object Inspector, how can you enumerate the choices
for a particular property?


Double-click the value column next to the property name in the Object Inspector.
Each time you double-click, the value changes to the next item in the list.



Day 5



1. Are all components visible at design time?


No. Only visual components can be seen at design time.


2. Is the OpenDialog component a visual component or a nonvisual component?


It's a nonvisual component. Although it is displayed at runtime, it is considered
nonvisual because it is not visible at design time.


3. What is the name of the VCL class that represents a Delphi form?


TForm.


4. Do all versions of Delphi ship with the same set of components?


No. The Professional version comes with more components than the Standard version.
Likewise, the Client/Server version comes with more components than does the Professional
version.


5. Are all VCL classes ultimately derived from TObject?


Yes.


6. Name one nonvisual VCL component.


TOpenDialog, TSaveDialog, TRegistry, TColorDialog, TTimer, TImageList, TFontDialog,
and many more, are all nonvisual VCL components.


7. Do all components share certain common properties?


Yes. All components are ultimately derived from TComponent, so they all have
the properties found in TComponent, such as Name and Owner.


8. Name two common properties that all visual components share.


Common properties that all visual components share include Top, Left, Owner,
Parent, Width, Height, and so on.


9. Can two or more components share the same event handler?


Yes.


10. What is the VCL terminology for a Windows device context? What is
the name of the VCL class that encapsulates device contexts?


A canvas is a Windows device context. VCL encapsulates device contexts through
the TCanvas class.



Day 6



1. When do you use Ctrl+drag in selecting components?


When selecting components that are children of another component (components
on a panel, for example).


2. What significance does the first component selected have when aligning
a group of components?


It is the anchor component. It retains its position, and all other components
are aligned to it.


3. What is the quickest method to select a group of components?


Drag a bounding rectangle around (or just touching) them.


4. How can you make a group of components all have the width of the group's
widest component?





Select all the components you want to modify. Then choose Edit | Size from the
main menu and choose the Grow to Largest radio button.





5. What happens when you double-click a component on a form?


The default event handler for that component is displayed in the Code Editor.
In the case of many components, the OnClick event handler will be displayed. In some
special cases (such as the Image component), a dialog box is displayed.


6. What does the Align property's alClient option do?


It forces the component to fill the entire client area of its parent, regardless
of how the parent (usually a form) is sized.


7. What does the ellipsis following a menu item mean?


Traditionally, it means that choosing that menu item will result in a dialog
being displayed.


8. What two ways can you move a menu item?


In the Menu Designer, you can drag the menu to a new location or you can use
cut and paste.


9. How do you add menu accelerators to menu items?


When typing the caption for the menu item, add the ampersand ( & amp;) before the
shortcut key you choose as the shortcut for that menu item. For example, the Caption
for the File | Exit menu item would read E & amp;xit.


10. How do you initially disable a menu item?


Set its Enabled property to False.



Day 7



1. Can you change the Name property of a component at runtime?


Yes, but it's a very bad idea.


2. What property is used to enable and disable controls?


The Enabled property.


3. How can you tell at runtime that a button is disabled?


Its text is grayed out.


4. What is the difference between the long hint and the short hint?


The long hint is used for the status bar text, and the short hint is used for
the tooltip text.


5. Name three of the four methods that can be used to tell a control to
repaint itself.


Invalidate, Repaint, Refresh, and Update.


6. How many types of combo boxes are there?


Three: simple, drop-down, and drop-down list.


7. How is the ModalResult property used for button components?


When a button with a ModalResult property set to a whole number is clicked, the
form will close. The value of the ModalResult property for the button clicked will
be the return value from the ShowModal method.


8. What component is often used as a container for other components?


The Panel component. Several others qualify, too.


9. What is the return value from the Execute method for an OpenDialog
component if the user clicks OK to close the dialog box?


true.


10. How do you make the SaveDialog component into a Save As dialog box?


Just change its Title property to Save As.



Day 8



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


Use the Inherit option when you want all the features of the base object and
you want the inherited object to change if the base object changes.


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


To save a project to the Object Repository, choose Project|Add to Repository
from the main menu.


3. What happens to inherited forms when you change the base form?


When you change the base form, all the inherited forms change to reflect the
change made to the base form.


4. Where in the form's class declaration do you place user method declarations?


You place user method declarations in the private or public sections of the class
declaration. Never place user declarations in the Delphi-managed section of the class
declaration (unless you know what you are doing).


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


In the implementation section of the unit.


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


You can tell who wrote an object in the Object Repository by switching to the
Details view. The object's author is listed there.


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


You add or delete pages in the Object Repository from the Object Repository configuration
dialog box (which you get by selecting Tools | Repository from the main menu).


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


It is easier to create a new application by using the Application Wizard in almost
all cases.


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


For small applications, static linking is usually better than dynamic linking
(no runtime packages).


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


Yes, you can easily create a string table with a text editor. You only need to
understand the basic layout of a string table.



Day 9



1. How can you quickly switch between a unit's form and source code when
working with Delphi?


Use the F12 key to quickly switch between the Form Designer and the Code Editor.


2. If you remove a file from your project via the Project Manager, is
the file removed from your hard drive?


No, it is only removed from the project.


3. How do you set the main form for an application?


Go to the Forms page of the Project Options dialog box and select the form you
want to be the main form in the Main form combo box.


4. What does it mean if you do not have Delphi Auto-create forms?


You will have to take the responsibility of creating the forms before using them.


5. How do you add new items to your unit using the Code Explorer?


Right-click and choose New from the Code Explorer context menu. Type in the declaration
for the new item and hit Enter.


6. What is the significance of generating debug information for your application?


When debug information is generated, you will be able to step through your code
during debugging sessions.


7. What is the Find in Files option used for?


Find in Files is used to find text in files.


8. What is the keyboard shortcut for saving a file in the Code Editor?


Ctrl+S (assuming that you're using the default keymapping).


9. How do you set a bookmark in an editor window? How many bookmarks are
available?


Set a bookmark with Ctrl+K+0 through Ctrl+K+9. There are 10 bookmarks available.


10. How do you set a file to read-only in the Code Editor?


Choose Read Only from the Code Editor context menu.



Day 10



1. How do you set a breakpoint on a particular code line?


Click in the gutter (the left margin) on that code line. You can also press F5
or choose Toggle Breakpoint from the Code Editor context menu.


2. What is an invalid breakpoint?


A breakpoint that is inadvertently set on a source code line that generates no
compiled code.


3. How do you set a conditional breakpoint?


Set the breakpoint, choose View|Debug Windows|Breakpoints from the main menu,
click the breakpoint in the Breakpoint List window, and then choose Properties from
the Breakpoint List context menu. Set the condition in the Condition field of the
Edit Breakpoint dialog box.


4. How can you change the properties of an item in the Watch List?


Double-click the item in the Watch List window. The Watch Properties dialog box
is displayed. Modify the properties as needed.


5. What's the quickest way to add a variable to the Watch List?


Click the variable and press Ctrl+F5 (or choose Add Watch at Cursor from the
Code Editor context menu).


6. What tool do you use to view the data fields and methods of a class?


The Debug Inspector is used to view classes and records.


7. How do you trace into a method when stepping with the debugger?


Use F7 or Run|Trace Into to step into a method.


8. How can you change the value of a variable at runtime?


Click the variable and then choose Evaluate/Modify from the Code Editor context
menu (or choose Run|Evaluate/Modify from the main menu). Change the value in the
Evaluate/Modify dialog box.


9. How can you send your own messages to the Event Log?


You can send your own messages to the Event Log by calling the Windows API function,
OutputDebugString.


10. What does the Integrated debugging option on the Debugger Options
dialog box do?


When this option is on and the program is run from the IDE, the program runs
under control of the debugger. When this option is off, the program runs without
using the debugger.



Day 11



1. How is the transparent color used for icons and cursors?


The background shows through wherever the transparent color is used on the icon
or cursor.


2. How do you choose a color in the Image Editor?


Click a color in the color palette.


3. How do you select an area on a bitmap to cut or copy?


Choose the Marquee tool and then drag a rectangle with the mouse. You could also
use Edit|Select All to select the entire image or the Lasso tool.


4. What is the maximum number of colors allowed in an Image Editor bitmap?


256.


5. What is a cursor's hot spot?


The exact pixel in the cursor that is used to report the screen coordinates to
Windows when the mouse is clicked.


6. Can WinSight spy on hidden windows?


Yes. WinSight shows hidden windows as well as visible windows.


7. What's the fastest way to locate a window in the WinSight Window Tree?


Choose Window|Follow to Focus from the main menu and then click on the window
you want to spy on.


8. How can you have your editor files automatically saved each time your
program runs through the debugger?


Turn on the Editor files option on the Preferences page (Autosave options section)
of the Environment Options dialog box.


9. Where do you go to rearrange the contents of the Component Palette?


Use the Palette page of the Environment Options dialog box.



Day 12



1. What component can you use to draw graphics on a form?


Although you can draw directly on the form's canvas, the PaintBox component enables
you to draw in a predefined area of a form (the area the PaintBox occupies).


2. Which TCanvas property controls the fill color of the canvas?


The Brush property.


3. What does a clipping region do?


The clipping region defines a canvas area within which drawing can take place,
but outside which no drawing takes place. Any drawing outside the clipping region
won't be displayed.


4. What function do you use to draw multiple lines of text on a canvas?


The DrawText function with the DT_WORDBREAK flag.


5. What TCanvas method can be used to draw a bitmap with a transparent
background?


The BrushCopy method.


6. Which TCanvas method do you use to copy an entire bitmap to a canvas?


You can use several, but the easiest and fastest is the Draw method. Others include
BrushCopy, StretchDraw, and CopyRect.


7. How do you save a memory bitmap to a file?


With the SaveToFile method.


8. What component do you use to play a wave file?


The TMediaPlayer component. To play a wave file using the Windows API, use the
PlaySound function.


9. What is the TimeFormat property of TMediaPlayer used for?


The TimeFormat property is used to set the time format based on the media type
being played. Some media types can use several time formats (CD audio, for example).


10. Can you record wave audio with the MediaPlayer component?


Yes, but you have to jump through some hoops.



Day 13



1. How do you attach an event handler to a toolbar button's OnClick event?


In the Object Inspector, click the Event tab. Click the drop-down arrow next
to the OnClick event. Choose an event handler from the list.


2. Can you put controls other than buttons on toolbars?


Yes. You can put virtually any type of component on a toolbar. Combo boxes are
commonly found on toolbars.


3. What is the name of the TActionList event you respond to when doing
command enabling?


The OnUpdate event.


4. What does the SimplePanel property of the StatusBar component do?


It forces the status bar to have a single panel.


5. How do you change the status bar text manually?


For a simple status bar, use the following:






StatusBar.SimpleText := `Text';







6. How do you enable and disable menu items and buttons?


For individual components, set the component's Enabled property to True to enable
the component or False to disable it. For several components that have a common task,
create an Action for that task and set the Action's Enabled property accordingly.

7. How do you access the printer in a Delphi application?

Through the Printer function.


8. What method do you call to begin printing with the TPrinter class?


The BeginDoc method.


9. What method of TPrinter do you call when you want to start a new page
when printing?


The NewPage method.


10. How do you change the cursor for a component at runtime?


Modify the component's Cursor property.



Day 14



1. How do you set the help file that your application will use?


Use the Project Options dialog (Application page) or set the HelpFile property
of the Application object at runtime.


2. How do you implement F1 key support for a particular form or dialog
box?


Just assign a nonzero value to the HelpContext property. Make sure that there
is a corresponding help context ID in the help file and that the help file has been
set for the application.


3. What method do you call to display the index for your help file?


The HelpCommand method.


4. What types of objects can an exception raise?


Any class derived from Exception.


5. Is it legal to have more than one except statement following a try
statement?


No, there can only be one except statement.


6. How do you raise an exception?


With the raise keyword--for example,






raise EMyException.Create(`An error occurred.');







7. What is the default value of the TRegistry class RootKey property?


\HKEY_CURRENT_USER


8. Must you call CloseKey when you are done with a key?


No. The TRegistry destructor will close the key for you. You shouldn't leave
a key open indefinitely, though.


9. What is the difference between SendMessage and PostMessage?


PostMessage posts the message to the Windows message queue and returns immediately.
SendMessage sends the message and doesn't return until the message has been handled.


10. What is the name of the VCL method that is used to send a message
directly to a component?


The Perform method.



Day 15



1. What is the base (or parent) interface for all COM interfaces?


The base interface from which all other interfaces are derived is IUnknown.


2. What is a GUID?


A GUID is a 128-bit integer that uniquely identifies a COM object (interface,
class, or type library).


3. What happens when a COM object's reference count reaches 0?


When a COM object's reference count reaches 0, the COM object is unloaded from
memory by Windows.


4. What is the name of the Delphi utility used when working with type
libraries?


The utility used to modify type libraries is the Type Library Editor.


5. How do you create GUIDs when writing COM objects in Delphi?


You don't have to specifically create GUIDs when creating COM objects in Delphi.
Delphi creates the GUIDs for you automatically (trick question, I know). If you want
to specifically create a GUID in your code, you can press Ctrl+Shift+G in the Code
Editor. Delphi will generate and insert a GUID in your code.


6. What do you choose from the Object Repository when creating an ActiveX
from a VCL component?


To create an ActiveX control from an existing VCL component, choose the ActiveX
Control item in the Object Repository.


7. Can you use Delphi-created ActiveX controls in Visual Basic?


Yes, you can use Delphi ActiveX controls in VB. You must be sure that the ActiveX
contains version information, but otherwise a Delphi ActiveX should work in VB with
no trouble.


8. After your ActiveX control is built and registered, how do you install
it to the Delphi Component palette?


To install an ActiveX control to the Delphi Component palette, choose Component
| Import ActiveX Control from the Delphi main menu.


9. How do you unregister an ActiveX that you have created?


To unregister an ActiveX control, you can either choose Run | Unregister ActiveX
Server from the main menu or you can run the TREGSVR utility on the ActiveX with
the -u switch. You can also uninstall an ActiveX control from the Import ActiveX
dialog box.


10. Can you use the ActiveX controls created in Delphi on a Web page?


Yes. Delphi ActiveX controls are designed to be deployed on a Web page.



Day 16



1. What is a local database?


A database that resides on the user's machine rather than on a database server.
This term usually refers to Paradox or dBASE tables.


2. What is the purpose of the BDE?


The BDE provides your Delphi application access to databases.


3. Are a dataset and a table the same thing? If not, explain the difference.


No, they are not the same thing. A dataset might include an entire table's contents,
or it might contain only a small subset of the table.


4. Name one advantage of cached updates.


Cached updates reduce network traffic, enable you to modify a read-only dataset,
and enable you to make several changes and then either commit or roll back all changes
at once.


5. What is a stored procedure?


An application that acts on a database and resides on a database server.


6. What is the purpose of the SQL property of the TQuery component?


The SQL property contains the SQL statements to execute when the Open or Execute
methods are called.


7. Name one reason you might want to use your own TDatabase object instead
of the default.


To allow automatic login to a database.


8. Why would you want to keep a connection to a remote database open even
when you are not currently using the connection?


To reduce the time required to log in to the database each time a connection
is requested.


9. What does the TBatchMove component do?


TBatchMove enables you to create or modify one dataset with the contents of another
dataset.


10. What is a BDE alias?


A set of parameters that describes a database connection.



Day 17



1. What's the fastest and easiest way to create a database form?


With the Database Form Wizard.


2. How do you control the order and number of columns that appear in a
DBGrid component?


With the Columns Editor. (Right-click a DBGrid component and choose Columns Editor
from the context menu.)


3. What component enables you to display a dataset in table format?


The DBGrid component.


4. How can you add or remove buttons from a DBNavigator?


By modifying the VisibleButtons property.


5. What component do you use to display BLOB image data?


The DBImage component.


6. What property is common to all data-aware components?


The DataSource property (among others).


7. What property is used to select the field that a data component is
linked to?


The DataField property.


8. Can you rearrange the columns in a DBGrid component?


Yes. Use the Columns Editor at design time or drag and drop them at runtime.


9. Which component is used to edit and display text fields in a database?


The DBEdit component.


10. What does BLOB stand for?


Binary large object.



Day 18



1. What method do you call to create a database table at runtime?


CreateTable.


2. What does the Edit method of TTable do?


The Edit method puts the dataset in edit mode so that records can be modified.


3. What method do you call when you want to apply the changes made to
a record?


The Post method.


4. How do you create a new data module?


Through the Object Repository.


5. Is a data module a regular form?


A data module is very similar to a regular form but not the same.


6. What method do you call to print a QuickReport?


The Print method.


7. What type of QuickReport band displays the dataset's data?


A detail band.


8. What component is used to display the page number on a report?


The QRSysData component can display page numbers, the current date, the current
time, and more.


9. How can you preview a report at design time?


Right-click the report and choose Preview from the context menu.


10. What is the QRExpr component used for?


The QRExpr component is used to display the results of an expression (usually
a calculated result).



Day 19



1. How do you load a DLL using static loading?


In your calling application declare any functions or procedures contained in
the DLL using the extern keyword. The DLL will automatically load when the application
starts.


2. How do you load a DLL using dynamic loading?


Use the Windows API function LoadLibrary.


3. How do you call a function or procedure from a DLL that has been loaded
statically?


Call the function or procedure just as you would call a regular function or procedure.


4. What steps do you have to take to ensure that a function or procedure
in your DLL can be called from outside the DLL?


The function or procedure must be exported from the DLL. Place the function or
procedure name in the exports section of the DLL.


5. In the case of a DLL that has been dynamically loaded, can you unload
the DLL at any time or only when the program closes?


You can unload the DLL any time you want (using the FreeLibrary function).


6. What must you do to display a Delphi form contained in a DLL from a
non-Delphi program?


Create an exported function that the calling application can call. In this function,
create the form and display it. Be sure the function is declared with the stdcall
keyword.


7. What is the name of the keyword used to declare functions and procedures
imported from a DLL?


The external keyword.


8. How do you add resources to a DLL?


Link a compiled resource file (.res or .dcr) to the DLL with the $R compiler
directive--for example,






{$R Resources.res}







9. Does a resource DLL need to contain code as well as the resources?


No. A resource DLL doesn't need any code.


10. Can a DLL containing resources be loaded statically (when the program
loads)?


It's possible, but there's rarely any reason to load a resource DLL statically.
You need the return value from LoadLibrary to access resources in the DLL, so you
might as well use dynamic loading for resource DLLs.



Day 20



1. Must a property use a write method? Why or why not?


No. You can use direct access instead.


2. Must a property have an underlying data field? Why or why not?


No, a property doesn't have to have an underlying data field, but most properties
usually do. A property that doesn't store a value is unusual.


3. Can you create a component by extending an existing component?


Absolutely. That's the easiest way to create a new component.


4. What happens if you don't specify a write specifier (either a write
method or direct access) in a property's declaration?


The property becomes read-only.


5. What does direct access mean?


The underlying data member for the property is modified and/or read directly
(no read method and no write method).

6. Must your properties have a default value? Why or why not?

No, default values for properties are optional. Published properties should have
a default value, though. Properties that are string properties cannot have a default
value (and some other types of properties as well).


7. Does the default value of the property set the underlying data field's
value automatically?


No, the default value only displays a value in the Object Inspector at design
time. You must set the underlying data member to the default value in the component's
constructor.


8. How do you install a component on the Component Palette?


Choose Component | Install from the main menu.


9. How do you specify the button bitmap that your component will use on
the Component Palette?


Create a .dcr file with the same name as the component's source. The .dcr should
contain a 24 & #165;24 bitmap with the same name as the component's classname. Be sure
that the .dcr is in the same directory as the .pas file for the component.


10. How do you trigger a user-defined event?


Just call the event after checking whether an event handler exists for the event:






if Assigned(FOnMyEvent) then

FOnMyEvent(Self);




Day 21



1. Do Delphi and C++Builder project files have the same filename extension?


No, Delphi and C++Builder project files do not have the same filename extension.
Delphi projects have a .dpr extension and C++Builder projects have a .bpr extension.


2. Do Delphi and C++Builder form files have the same filename extension?


Yes. Both Delphi and C++Builder have .dfm filename extensions.


3. Can you use packages from third-party component vendors in both Delphi
and C++Builder?


In most cases, you can use packages from third-party component vendors in both
Delphi and C++Builder. In some cases, the packages will have to be rebuilt with C++Builder.
Ask the component vendor for packages compatible with C++Builder.


4. Can you open a Delphi form file in C++Builder?


Yes. You cannot edit the form (add or remove components), but you can view the
form.


5. Can you edit a Delphi form file using the C++Builder Form Designer?


No, you cannot edit a Delphi form file using the C++Builder Form Designer. However,
you can edit the Delphi form as text by choosing View As Text from the C++Builder
context menu.


6. Can you use a C++Builder source unit in Delphi?


With the current version of Delphi, you cannot use a C++Builder source unit in
Delphi.


7. Which is better, Delphi or C++Builder?


Neither Delphi or C++Builder is better. Both have their strengths. It depends
largely on whether you prefer Pascal or C++.



Bonus Day



1. What control do you use to display Web pages?


The THMTL control.


2. What control do you use to connect to newsgroups?


The TNMNNTP control.


3. What is the name of the method used to display an HTML document with
the THTML control?


RequestDoc.


4. What event is generated when an HTML document has completed loading?


The OnEndRetrieval event.


5. What control do you use to send email messages?


The TNMSMTP control.


6. What control do you use to retrieve email messages?


The TNMPOP3 control.


7. What is the name of the method used to send mail with the TNMSMTP control?


SendMail.


8. Can you freely distribute the Internet Explorer ActiveX control?


No, you must obtain a license from Microsoft to distribute the Internet Explorer
ActiveX control.


9. What is the name of the utility used to register ActiveX controls?


TREGSVR.EXE.


10. What company provides the bulk of the Internet controls that come
with Delphi?


NetMasters.








& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > ch15.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Ch 15 -- COM and ActiveX









Teach Yourself Borland Delphi 4 in 21 Days







- 15 -


COM and ActiveX



Understanding COM

COM Terminology
Reference Counting
The IUnknown Interface
Creating a COM Object
Adding Code

Understanding ActiveX

Using Third-Party ActiveX Controls
Creating New ActiveX Controls
Changing the ActiveX Palette Bitmap

Web Deploying ActiveX Controls and ActiveForms

Web Deployment Options
Web Deploy

Summary

Q & amp;A
Quiz
Exercises









OLE, ActiveX, COM, DCOM, VCL, CORBA, MTS. . . the industry certainly doesn't lack
for acronyms when it comes to the subject of component architecture! In this chapter,
I explain some of these acronyms and at least mention the others in passing. I'll
explain what these terms mean and try to shed some light on the often-confusing world
of COM and ActiveX. Specifically, I cover


What COM is

Creating COM objects

The Delphi Type Library Editor

Creating ActiveX controls

Creating ActiveForms

Deploying ActiveX controls


I'd be lying if I said that COM, ActiveX, and OLE are easy to understand. They
are not. It can be very confusing at first. I can't do justice to this subject in
one chapter. My goal for this chapter is to give you enough of a background on these
architectures so that you can better understand the acronyms you see bandied about
these days. You will also get some good hands-on training in creating COM and ActiveX
controls. Fortunately, Delphi takes a great deal of the pain out of dealing with
these APIs.

Understanding COM
You can't talk about OLE and ActiveX without talking about COM, which stands for
Component Object Model.
New Term: COM ( Component Object Model ) is a Microsoft
specification for creating and implementing reusable components.
& quot;Components? I thought Delphi used VCL components. & quot; Certainly VCL components
are the most effective use of components in Delphi. They aren't the only possibility,
though. As you work through this hour, you will get a clearer picture of how COM
and ActiveX work with Delphi.
COM is the basis for both OLE and ActiveX. An analogy might be the TObject class
in VCL. All classes in VCL are ultimately inherited from TObject. Derived classes
automatically get the properties and methods of TObject. They then add their own
properties and methods to provide additional functionality. Similarly, OLE and ActiveX
are built on top of COM. COM is the foundation for all OLE and ActiveX objects.
As a component architecture, COM has two primary benefits:


COM object creation is language independent. (COM objects can be written in many
different programming languages.)

A COM object can be used in virtually any Windows programming environment including
Delphi, C++Builder, Visual C++, Visual Basic, PowerBuilder, Visual dBASE, and many
more.







NOTE: One major drawback to COM is that it is heavily tied to the WinTel
(Windows/Intel) platform. So although you can use a COM object in many different
Windows programming environments, you can't necessarily use that COM object in a
UNIX programming environment. Recently Microsoft has tried to move COM to non-Windows
platforms, but it remains to be seen whether this attempt will ultimately succeed.
This chapter deals only with COM and ActiveX as they exist in the Win32 programming
environment.





You can use a number of different languages and environments to write COM objects.
You can create COM objects with Delphi, C++Builder, Visual C++, Visual Basic, and
probably a few other development environments. When created, a COM object can be
used in an even wider variety of development environments. A COM object created in
Delphi can be used by a VB programmer, a C++Builder programmer, or even a Visual
dBASE or PowerBuilder programmer.
A COM object is typically contained in a DLL. The DLL might have an extension
of .DLL or it might have an extension of .OCX. A single library file (DLL or OCX)
can contain an individual COM object or can contain several COM objects.

COM Terminology
COM is full of confusing terminology. The following sections explain some of the
terms used in COM and how the many pieces of COM fit together. All of these pieces
are interrelated, so you'll have to read the entire section to get the big picture.

COM Objects
New Term: A COM object is a piece of binary code that performs
a particular function.
A COM object exposes certain methods to enable applications to access the functionality
of the COM object. These methods are made available via COM interfaces. A COM object
might contain just one interface, or it might contain several interfaces. To a programmer,
COM objects work a lot like Object Pascal classes.

COM Interfaces
Access to a COM object is through the COM object's interface.
New Term: A COM interface is the means by which the user of a
COM object accesses that object's functionality.
A COM interface is used to access a COM object; to use the object, if you
will. The interface in effect advertises what the COM object has to offer. A COM
object might have just one interface, or it might have several. In addition, one
COM interface might implement one or more additional COM interfaces.
COM interfaces typically start with the letter I. The Windows shell, for example,
implements interfaces called IShellLink, IShellFolder, and IShellExtInit. Although
you can use any naming convention you like, the leading I universally and immediately
identifies the class as a COM interface to other programmers.
COM interfaces are managed internally by Windows according to their interface
identifiers (IIDs). An IID is a numerical value contained in a data structure (a
record). The IID uniquely identifies an interface.

COM Classes
New Term: A COM class (also known as a coclass ) is a class
that contains one or more COM interfaces.
You can't use a COM interface directly. Instead, you access the interface through
a coclass. A coclass includes a class factory that creates the requested interface
and returns a pointer to the interface. COM classes are identified by class identifiers
(CLSIDs). A CLSID, like an IID, is a numerical value that uniquely identifies a COM
class.

GUIDs
COM objects must be registered with Windows. This is where CLSIDs and IIDs come
into play. CLSIDs and IIDs are really just different names for the same base data
structure: the Globally Unique Identifier (GUID).
New Term: A GUID is a unique 128-bit (16-byte) value.
GUIDs are created by a special COM library function called CoCreateGUID. This
function generates a GUID that is virtually guaranteed to be unique. CoCreateGUID
uses a combination of your machine information, random number generation, and a time
stamp to create GUIDs. Although it is possible that CoCreateGUID might generate two
GUIDs that are identical, it is highly unlikely (more like a statistical impossibility).
Thankfully, Delphi programmers don't have to worry about creating GUIDs. Delphi
automatically generates a GUID when you create a new automation object, COM object,
ActiveX control, or ActiveForm control. GUIDs in Delphi are defined by the TGUID
record. TGUID is declared in System.pas as follows:

TGUID = record
D1: Integer;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;

When you create a new COM object, Delphi automatically creates the GUID for you. For example, here's the GUID for a test COM object I created:

Class_Test: TGUID = `{F34107A1-ECCF-11D1-B47A-0040052A81F8}';

Because GUIDs are handled for you by Delphi, you won't typically have to worry
very much about GUIDs. You will, however, see GUIDs a lot as you create and use COM
objects (including ActiveX controls).





TIP: If you need to generate a GUID manually, you can type Ctrl+Shift+G
in the Code Editor. Delphi will generate a GUID for you and insert it at the cursor
point.





Type Libraries
COM objects often use a type library.
New Term: A type library is a special file that contains information
about a COM object. This information includes a list of the properties, methods,
interfaces, structures, and other elements that the control contains. The type library
also provides information about the data types of each property and the return type
and parameters of the object's methods.
This information includes the data types in the object, the methods and properties
of the object, the version information, interfaces in the object, and so on. Type
libraries can be contained in the COM object as resources or as a standalone file.
Type library files have a .TLB extension. A type library is necessary if other developers
are going to use your COM objects as development tools. A COM object's type library
contains more information about the object than is available by simply querying the
object for its interfaces. The Delphi IDE, for example, uses the information found
in type libraries to display an ActiveX control on the Component palette. Users of
a COM object can examine the type library to see exactly what methods and interfaces
the object contains.

DCOM
Distributed COM (DCOM) is a subset of COM that provides the capability to use
COM objects across networks or across the Internet. DCOM extends COM to provide the
mechanism for using COM in a networking environment. A detailed discussion of DCOM
is beyond the scope of this book, but note that DCOM is definitely prevalent in certain
types of network programming.





NOTE: CORBA (Common Object Request Broker Architecture) is a competing
technology to DCOM. CORBA is platform-independent, which makes it more desirable
than DCOM in many ways. In addition, CORBA is an open architecture supported by a
consortium of software companies (unlike DCOM, which is a Microsoft-specific solution).
Fortunately, Delphi gives you the option of creating both DCOM and CORBA objects.






Reference Counting
Every COM object has a reference count. The reference count , naturally,
contains the number of processes that are currently using the COM object. A process
is any application or DLL that uses a COM object. Because a COM object can be used
by any number of processes at one time, reference counting is used to determine when
the COM object is no longer needed in memory.
When a COM object is created, its reference count is initialized to 1. The reference
count is incremented by one each time a process attaches to the COM object. When
a process detaches from the COM object, the reference count is decremented by one.
When the reference count gets to 0, the COM object is freed from memory.

The IUnknown Interface
All COM interfaces descend from a base interface called IUnknown. Table 15.1 lists
the methods of IUnknown.

TABLE 15.1. IUnknown METHODS.



Method
Description


QueryInterface
Queries the interface to obtain a list of supported interfaces.


AddRef
Increments the interface's reference count.


Release
Decrements the interface's reference count. When the reference count reaches 0, the
object is freed from memory.



I mention IUnknown primarily for historical reasons. Delphi programmers don't
really have to worry much about IUnknown as other programmers do. Delphi takes care
of handling reference counting and freeing the memory for the COM object. Delphi
also elevates dealing with COM objects to a level that makes an intimate knowledge
of IUnknown all but obsolete.

Creating a COM Object
To help bring this into perspective, let's create a COM object. This COM object
will be ridiculously simple but should illustrate how to use and build COM objects
in Delphi. The COM object you create will have these characteristics:



Type
Name
Description


property
X
The first number to multiply.


property
Y
The second number to multiply.


method
DoIt
A method that multiplies the two numbers and returns the result.



The following sections explain the process of creating the COM object.

Creating an ActiveX Library
The first step in creating a COM object is to create a DLL project that will contain
the COM object's code. Delphi uses the term & quot;ActiveX Library & quot; to refer
to all COM library projects. This description isn't entirely accurate, but it's close
enough. Perform these steps to create an ActiveX Library:



1. Close all projects. Choose File|New from the main menu to display the
Object Repository.


2. Click on the ActiveX tab to show the ActiveX page (see Figure 15.1).
Double-click the ActiveX Library icon.



Figure 15.1. The
Object Repository's ActiveX page.



3. Choose File|Save and save the project as ComTest.



That's all there is to this particular step. Delphi creates a DLL project for
you and is waiting for your next move.

Creating the Actual Object
The next step is to create the COM object itself. This step is also relatively
simple. Perform these steps to do so:



1. Choose File|New from the Delphi main menu. The Object Repository is
displayed. Click on the ActiveX page.


2. Double-click on the COM Object icon. Delphi displays the COM Object
Wizard, as shown in Figure 15.2.



FIGURE 15.2. The
COM Object Wizard.






THE COM OBJECT WIZARD


Let me take a moment to talk about the COM Object Wizard. The Class Name field is
used to specify the class name for your COM object. Type the class name here, but
don't prepend the class name with either a T as you would for a Delphi class, nor
an I as is customary for interfaces. Delphi will take care of creating the class
and interface names automatically.


The Instancing field is used to control how multiple instances of the COM object
are handled. Choices include Internal, Single Instance, or Multiple Instance. See
the & quot;COM object wizard & quot; topic in the Delphi help for descriptions of these
instancing options (you can click the Help button on the COM Object Wizard to display
the correct help topic).


The Threading Model field is used to specify how client applications can call
your COM object. Choices include Single, Apartment, Free, or Both. Again, see the
Delphi help for descriptions of the threading models.


The Implemented Interfaces field is where you add the names of any interfaces
that your COM object will implement. If you have an interface called IMyFileIO and
you want to use that interface in your new COM object, you would type IMyFileIO in
this field.


The Description field is used to supply a description for the COM object. The
description is optional, but it's a good idea to provide one.
When the Include Type Library check box is checked, Delphi will create a type library
for the COM object. Creating a type library enables your COM object to be used by
client applications.





Okay, let's get back to work:



3. Enter Multiply in the Class Name field.


4. Enter Test COM Object in the Description field.


5. Check the Include Type Library check box. The other fields on the dialog
box can be left at their default values.


6. Click OK to close the dialog box.



When you click the OK button, Delphi creates a unit for the COM object's class
and displays the Type Library Editor, as shown in Figure 15.3. Before continuing,
I need to take a moment to talk about the Type Library Editor.

Using the Type Library Editor
The Type Library Editor is used to manipulate a type library. The Type Library
Editor enables you to add or remove interfaces, add properties and methods to interfaces,
remove elements from interfaces, and create host of other COM elements such as enumerations,
records, or coclasses. The Type Library Editor makes it easy to add elements to a
type library. You'll learn about adding elements in the next section when you add
properties and a method to the COM object.
On the left side of the Type Library Editor is the Object pane. The Object pane
contains a tree view control. At the top of the tree view hierarchy is the type library
itself. Below the type library are elements contained in the type library. In Figure
15.3, you see two elements: the IMultiply interface and the Multiply coclass.
On the right side of the Type Library Editor is the Information pane. This pane
provides information about the object currently selected in the Object pane. The
information presented in the information pane varies with the type of object selected.
The Attributes page shows the type library name, its GUID, version, help string,
help file, and so on.





NOTE: Remember earlier when I said that Delphi programmers don't need to
worry much about GUIDs? The COM object you just created already has a GUID, as does
the type library itself. Delphi creates these GUIDs for you automatically. As I said
before, you'll see GUIDs a lot as you work with COM objects, but you don't have to
worry about creating them.





When the type library node is selected, the Information pane shows a tab labeled
Uses. When you click on this tab you will see a list of type libraries that this
type library relies on. In almost all cases, this list will include the OLE Automation
library, but it can contain others as well. The exact libraries a particular type
library relies on depends on the type and complexity of the COM object the type library
describes.
The Text page shows the type library definitions in IDL syntax. IDL is sort of
a scripting language used to create binary type library files. You shouldn't change
any of the text on this tab unless you know exactly what you are doing. You might,
however, refer to the Text page for reference. This is probably of more value to
experienced programmers than to beginners.
Other pages might be displayed in the Information pane depending on the type of
object selected. For complete details, be sure to read the & quot;Type Library Editor & quot;
help topic in the Delphi help.
You will learn more about the Type Library Editor as you work through the rest
of the chapter. Now let's get back to creating the COM object.

Adding Properties and Methods to the COM Object
Before going further, you should save the project again. You didn't realize it,
but Delphi created a new unit when you created the COM object in the previous step.
Choose File|Save All from the main menu and save the unit as MultiplyU.
Now you are ready to make the COM object do something. Remember, this COM object
is incredibly simplistic, so it won't do much, but it will at least do something .





ADDING PROPERTIES


First you will add properties to the COM object. Here are the steps:






1. Click on the IMultiply node in the Type Library Editor's Object pane.
Notice that the Information pane shows the interface's name, GUID, and version. Notice
also that the Parent Interface field shows the ancestor of IMultiply as IUnknown.
If you recall, I said earlier that IUnknown is the base (or parent) interface from
which all other interfaces are derived. Delphi automatically assumes a base interface
of IUnknown. You can change the base interface to some other interface if you want
by choosing an interface from the list of available interfaces. Other interfaces
in the list are themselves derived from IUnknown or one of its descendants.


2. Right-click and choose New|Property from the context menu. The Type
Library Editor adds two new nodes to the Objects pane under the IMulitply interface.
The cursor is in editing mode so that you can type the name of the new property.


3. Type X for the property name and then press the Enter key. Both of
the new nodes change to X. There are two nodes to each property because, by default,
a property is assumed to be a read/write property. COM requires a Get method to read
a property and a Put method to write to a property, hence the two entries. Click
on either of the two nodes labeled X. Notice the Invoke Kind field in the Information
pane as you select first one X node and then the other. Notice that the field changes
from Property Set to Property Get.


4. Notice in the Information pane that the Type field says Integer. That's
the data type you want for this property, so you don't need to change the type.


5. Create another new property but this time use a different approach.
Locate the New Property button on the Type Library Editor toolbar. Click the drop-down
arrow next to the New Property button. Choose Read|Write from the list of property
types. The Type Library Editor creates the new property. Name this property Y. You
can accept the default data type of Integer for this property as well. Behind the
scenes Delphi is adding code to the project's units as you add elements.





Adding a Method
Next, you add a method. Perform these steps:



1. Select the IMultiply object in the Object pane and click the New Method
button on the Type Library Editor toolbar.


2. Name the method DoIt. Notice that the Invoke Kind field says Function
(as opposed to Property Get and Property Set).


Next you must set the method's parameters. The method will have this syntax:






function DoIt : Integer;







3. Click on the Parameters tab in the Information pane. Change the Return
Type field to Integer (choose Integer from the combo box). This method doesn't have
any parameters, so you can leave the Parameters list empty. After you have set the
return type, click the Attributes tab to display the Attributes page. This step isn't
strictly necessary, but does serve the purpose of taking you back to where you started.


4. Click the Refresh Implementation button on the Type Library Editor
toolbar.



Now that you have added the two properties and methods, it's time to see what
Delphi has been doing behind the scenes. Listing 15.1 shows the class's unit as it
appears after performing the steps up to this point. (Don't worry if your unit doesn't
look exactly like Listing 15.1. My version of Delphi might have added code in a slightly
different order than yours.)

LISTING 15.1. MultiplyU AFTER ADDING PROPERTIES AND A METHOD.
unit MultiplyU;
interface
uses
Windows, ActiveX, ComObj, ComTest_TLB;
type
TMultiply = class(TTypedComObject, IMultiply)
protected
function DoIt: Integer; stdcall;
function Get_X: Integer; stdcall;
function Get_Y: Integer; stdcall;
procedure Set_X(Value: Integer); stdcall;
procedure Set_Y(Value: Integer); stdcall;
{Declare IMultiply methods here}
end;
implementation
uses ComServ;
function TMultiply.DoIt: Integer;
begin
end;
function TMultiply.Get_X: Integer;
begin
end;
function TMultiply.Get_Y: Integer;
begin
end;
procedure TMultiply.Set_X(Value: Integer);
begin
end;
procedure TMultiply.Set_Y(Value: Integer);
begin
end;
initialization
TTypedComObjectFactory.Create(ComServer, TMultiply, Class_Multiply,
ciMultiInstance, tmSingle);
end.

This is the shell of the COM object. Notice that the TMultiply class is derived
from both TTypedComObject and IMultiply. (To C++ programmers, this might look like
multiple inheritance. It's not exactly multiple inheritance, but it is similar in
some ways.) You haven't seen the IMultiply class yet, but you will a bit later. You
must fill out this shell in order to make the COM object do something. You will do
that next.

Adding Code
You will now add code to the TMultiply class to make the COM object functional.
Perform these steps (refer to Listing 15.2 if necessary):



1. Display the MuliplyU.pas unit in the Code Editor. Add these lines to
the TMultiply class declaration, just above the protected keyword:






private
FX : Integer;
FY : Integer;







These are the declarations for the data fields that will hold the X and Y property
values.


2. Scroll down into the implementation section and locate the Get_X method
(use the Code Explorer if you like). Type this line of code in the method:






Result := FX;







3. Locate the Get_Y method and add this line:






Result := FY;







4. Locate the DoIt method and add this line of code:






Result := FX * FY;







This line of code multiplies the value of FX and FY and returns the result.


5. Scroll down further until you see the Set_X method. Type this line
of code in the Set_X method:






FX := Value;







6. Locate the Set_Y method and add this line:






FY := Value;




That's all you need to do. Your code should now look like Listing 15.2.

LISTING 15.2. THE COMPLETED MultiplyU UNIT.
unit MultiplyU;
interface
uses
Windows, ActiveX, ComObj, ComTest_TLB;
type
TMultiply = class(TTypedComObject, IMultiply)
private
FX : Integer;
FY : Integer;
protected
function DoIt: Integer; stdcall;
function Get_X: Integer; stdcall;
function Get_Y: Integer; stdcall;
procedure Set_X(Value: Integer); stdcall;
procedure Set_Y(Value: Integer); stdcall;
{Declare IMultiply methods here}
end;
implementation
uses ComServ;
function TMultiply.DoIt: Integer;
begin
Result := FX * FY;
end;
function TMultiply.Get_X: Integer;
begin
Result := FX;
end;
function TMultiply.Get_Y: Integer;
begin
Result := FY;
end;
procedure TMultiply.Set_X(Value: Integer);
begin
FX := Value;
end;

procedure TMultiply.Set_Y(Value: Integer);

begin
FY := Value;
end;
initialization
TTypedComObjectFactory.Create(ComServer, TMultiply, Class_Multiply,
ciMultiInstance, tmSingle);
end.

Although you were working on the MulitplyU unit, Delphi was busy building the
type library and a unit to contain the type library code. The unit has the same name
as the project with a trailing _TLB. This project is named ComTest. The full unit
name for the type library unit, then, is ComTest_TLB.pas. Listing 15.3 shows this
unit as it exists at this point. Remember, your unit might not look exactly like
Listing 15.3.

LISTING 15.3. THE ComTest_TLB.pas UNIT.
unit ComTest_TLB;
// ******************************************************************** //
// WARNING //
// ------- //
// The types declared in this file were generated from data read from a //
// Type Library. If this type library is explicitly or indirectly (via //
// another type library referring to this type library) reimported, or //
// the `Refresh' command of the Type Library Editor activated while //
// editing the Type Library, the contents of this file will be //
// regenerated and all manual modifications will be lost. //
// ******************************************************************** //
// PASTLWTR : $Revision: 1.11.1.55 $
// File generated on 6/8/98 7:16:51 PM from Type Library described below.
// ******************************************************************** //
// Type Lib: D:\Borland\D4\Bin\ComTest.tlb
// IID\LCID: {7CDAFB76-FF36-11D1-81F1-0040052A83C4}\0
// Helpfile:
// HelpString: ComTest Library
// Version: 1.0
// ******************************************************************** //
interface
uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL;
// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used: //
// Type Libraries : LIBID_xxxx //
// CoClasses : CLASS_xxxx //
// DISPInterfaces : DIID_xxxx //
// Non-DISP interfaces: IID_xxxx //
// *********************************************************************//
const
LIBID_ComTest: TGUID = `{7CDAFB76-FF36-11D1-81F1-0040052A83C4}';
IID_IMultiply: TGUID = `{7CDAFB77-FF36-11D1-81F1-0040052A83C4}';
CLASS_Multiply: TGUID = `{7CDAFB79-FF36-11D1-81F1-0040052A83C4}';
type
// *********************************************************************//
// Forward declaration of interfaces defined in Type Library //
// *********************************************************************//
IMultiply = interface;
// *********************************************************************//
// Declaration of CoClasses defined in Type Library //
// (NOTE: Here we map each CoClass to its Default Interface) //
// *********************************************************************//
Multiply = IMultiply;
// *********************************************************************//
// Interface: IMultiply
// Flags: (0)
// GUID: {7CDAFB77-FF36-11D1-81F1-0040052A83C4}
// *********************************************************************//
IMultiply = interface(IUnknown)
[`{7CDAFB77-FF36-11D1-81F1-0040052A83C4}']
function Get_X: Integer; stdcall;
procedure Set_X(Value: Integer); stdcall;
function Get_Y: Integer; stdcall;
procedure Set_Y(Value: Integer); stdcall;
function DoIt: Integer; stdcall;
end;
CoMultiply = class
class function Create: IMultiply;
class function CreateRemote(const MachineName: string): IMultiply;
end;
implementation
uses ComObj;

class function CoMultiply.Create: IMultiply;

begin
Result := CreateComObject(CLASS_Multiply) as IMultiply;
end;
class function CoMultiply.CreateRemote(const MachineName: string): & #194;IMultiply;
begin
Result := CreateRemoteComObject(MachineName, CLASS_Multiply) as & #194;IMultiply;
end;
end.

Notice that this unit contains the declaration for the IMultiply interface. As
you can see, IMultiply is derived from IUnknown. Notice also that this unit contains
the coclass Multiply.
It is important to understand that this unit is regenerated each time you compile
an ActiveX library project. It is generated from the type library file. Note the
warning at the top of the unit. The comments are telling you that any changes you
make to this file will be lost the next time the COM object is rebuilt. It really
doesn't do any good to modify the type library source file, because it will be regenerated
automatically.

Building and Registering the COM Object
Now you are ready to compile the ActiveX library project. This step compiles the
COM object and builds the DLL in which the COM object resides. After building the
COM object, you can register it. Here are the steps:



1. Choose Project|Build ComTest from the main menu. Delphi will build
the DLL containing the COM object.


2. Choose Run|Register ActiveX Server from the main menu. This step registers
the COM object with Windows. If you fail to perform this step, you will get an exception
that says, & quot;Class not registered & quot; when you attempt to access the COM object.



Delphi registers the COM object DLL with Windows. After the DLL has been registered,
Delphi displays a message box, as shown in Figure 15.4.
FIGURE 15.4. Delphi
reporting the COM object successfully registered.



When Windows registers the COM object, it adds information about the object to
the Registry. Figure 15.5 shows the Registry entry created when the COM object was
registered with Windows.

FIGURE 15.5. The
Registry key created when the COM object was registered.





NOTE: Delphi ships with a utility called TREGSVR.EXE that can be used to
register an ActiveX control from the command line. To register a control called MYSTUFF.OCX,
you would run TREGSVR from a command prompt like this:
tregsvr mystuff.ocx







To unregister an ActiveX, use the -u switch as follows:
tregsvr -u mystuff.ocx







Sometimes this is more convenient than loading an ActiveX project in Delphi and
registering or unregistering the control from the IDE.






NOTE: In this exercise I had you create a COM object. You could also have
used an automation object. An automation object derives from IDispatch rather than
IUnknown. IDispatch provides the additional functionality required for a COM object
to act as an automation server (an object that can control one application from another).






Your COM object is now ready to use.

Building an Application That Uses the COM Object
A COM object doesn't do you much good if you can't use it. In this step, you create
an application that uses the COM object you just created. Follow these steps:



1. Create a new application. Place a Button component and a Label component
on the form. Save the project as ComApp and the main form's unit as ComAppU.


2. Switch to the Code Editor and locate the uses list for the main unit.
Add these units to the uses list:






ComObj
ComTest_TLB






This ensures that the code that references the COM object will compile.

3. Double-click the form's button to create an OnClick event handler.
Modify the OnClick handler so that it looks like this:






procedure TForm1.Button1Click(Sender: TObject);
var
Mult : IMultiply;
Res : Integer;
begin
Mult := CreateComObject(CLASS_Multiply) as IMultiply;
if Assigned(Mult) then begin
Mult.Set_X (20);
Mult.Set_Y (60);
Res := Mult.DoIt;
Label1.Caption := IntToStr(Res);
end;
end;




This code first declares a pointer to the IMultiply interface called Mult and
an Integer variable to hold the result. Next, the CreateComObject function is called
with a parameter of CLASS_Multiply. CLASS_Multiply is a constant that contains the
GUID for the COM object class (refer to Listing 15.3).
The return value from CreateComObject is assigned to the Mult pointer. Notice
that I use the as operator to cast the return value to an IMultiply pointer. CreateComObject
returns an IUnknown pointer, so the as operator is used to cast the IUnknown pointer
to an IMultiply pointer.
After the COM object is created, I assign values to the X and Y properties. After
that I call the DoIt method of the COM object and display the result in the Label
component.





NOTE: In the real world, I would have written the preceding procedure differently.
For example:
procedure TForm1.Button1Click(Sender: TObject);
begin
with CreateComObject(CLASS_Multiply) as IMultiply do begin
Set_X(20);
Set_Y(60);
Label1.Caption := IntToStr(DoIt);
end;
end;







I wrote the procedure the way I did to illustrate each step.





Run the program. When you click the form's button, the label should change to
say & quot;1200 & quot; (the product of 20 * 60). That's it! Your COM object works.
This COM object can be used from Visual Basic, Visual C++, C++Builder, or any other
development environment that supports COM.

Understanding ActiveX
ActiveX is a relatively new term for a technology that has been around
for awhile. Originally ActiveX controls were called OCX controls . The term
OCX is still widely used in some circles. An ActiveX control typically has
a filename extension of either DLL or OCX.
An ActiveX control is essentially a COM object in disguise. The primary difference
between an ActiveX control and a COM object is that an ActiveX control has a design-time
interface. An ActiveX control also has code that enables it to be deployed on a Web
page or over a network. ActiveX is a subset of COM, so everything you learned about
COM objects in the first part of the chapter applies to ActiveX controls as well.

Using Third-Party ActiveX Controls
There isn't a lot to know about installing and using third-party ActiveX controls.
All you have to do is import the ActiveX into the IDE and begin using the control.
To see how this works, let's do a quick exercise. This exercise requires that you
have Microsoft Internet Explorer installed on your system. If you don't have Internet
Explorer installed, skip this exercise. (You won't be missing anything because I
show you how to install an ActiveX control you have built in the section & quot;Build,
Register, and Install the Control. & quot;) Perform these steps:



1. Choose Component|Import ActiveX Control from the main menu. The Import
ActiveX dialog box is displayed.


2. Scroll down through the list of installed components until you find
Microsoft Internet Controls (the exact text of the item will depend on the version
of Internet Explorer you have installed on your system). Select the item. Figure
15.6 shows the Import ActiveX dialog box after this step.



FIGURE 15.6. The
Import ActiveX dialog box.



Notice the Class names list box in the middle of the page. This list box contains
a list of ActiveX controls in the selected file (SHDOCVW.DLL in this case).


3. The Palette page field shows ActiveX. This is a the palette page where
the new controls will be installed. Click on this field and type ActiveXTest.


4. Leave the Unit dir name and Search path fields on their default settings
and click the Install button. The Install dialog box comes up and asks what package
you want the controls installed into. (All controls, whether they are VCL or ActiveX,
must be in a package.)


5. Click on the Into new package tab. Enter MSIE in the File name field
and Internet Explorer Package in the Description field.


6. Click the OK button. Delphi creates a new package called MSIE.dpk and
prompts you to build and install the package. Click Yes to install the package. After
the package is built, Delphi displays a message box telling you which controls were
added to the Component palette. Click Yes to dismiss the message box.


7. Scroll the Component palette to find the ActiveXText tab. You should
see two or three controls on that page of the Component palette (again, depending
on the version of Internet Explorer you have installed). The components are ready
for use.



Experiment with the new controls and see how they work. You probably won't get
very far without documentation, but at least you get a sense for how installing an
ActiveX works. (For a more complete explanation of using Internet Explorer as an
ActiveX, see the section, & quot;Using Internet Explorer as an ActiveX Control & quot;
on the Bonus Day, & quot;Building Internet Applications. & quot;)





NOTE: You must have a design-time license in order to use an installed
ActiveX control. A design-time license is a file with an .LIC extension. In some
cases you can import ActiveX controls to the Delphi Component palette without a design-time
license, but you will get an error message when you attempt to place the control
on your form.





To remove the Internet Explorer controls from the Component palette, choose Component|Install
Packages from the main menu. Locate the Internet Explorer Package in the Design packages
list box and click the Remove button. The ActiveXTest tab is removed from the Component
palette.





NOTE: Deploying an application that uses ActiveX controls requires special
attention. Deploying applications using ActiveX controls is covered in detail on
the Bonus Day in the section & quot;Deploying Internet Applications. & quot;





Creating New ActiveX Controls
There are two ways to create an ActiveX control in Delphi:


From an existing VCL component

From scratch using an ActiveForm


In this section, you create an ActiveX control using both of these methods.

Creating an ActiveX Control from an Existing VCL Component
Creating an ActiveX control from an existing VCL component is downright simple.
After you create a component, you can turn it into an ActiveX control in no time
at all. I haven't talked about creating components yet, so I don't want to go into
a lot of detail on creating components now (covered on Day 20, & quot;Creating Components & quot;).
What you will do, then, is create an ActiveX control from one of the VCL components
provided by Borland.
Generate the ActiveX Project with the ActiveX Control Wizard
The first step is to generate the ActiveX project. As always, Delphi does most
of the work for you. All you have to do is supply a few fields in the ActiveX Control
Wizard. Here are the steps:



1. Choose File|Close All to close all projects and then choose File|New
from the main menu. The Object Repository is displayed.


2. Click the ActiveX page and then double-click the ActiveX Control icon.
The ActiveX Control Wizard is displayed (see Figure 15.7).



FIGURE 15.7. The
ActiveX Control Wizard.



3. Select TButton from the list of classes in the VCL Class Name combo
box. The next four fields are automatically filled in with default values. Because
this is just a test, you can leave those fields on their default values. The fields
are self-explanatory, so I don't need to go over each one.


4. The Threading Model is set to Apartment. Leave this setting as it is.
Other threading models include Single, Free, and Both. See the Delphi help for more
information on threading models.


5. Check the Include Design-Time License check box. When this option is
checked, Delphi will create a design-time license for the control. The design-time
license will prevent other programmers from using the control unless they have the
license.


6. Check the Include Version Information check box. This will enable you
to add version info to the control via the Project Options dialog box.


7. Check the Include About Box check box as well. When this box is checked,
Delphi will automatically create an About dialog box for the control. Click OK to
close the ActiveX Control Wizard.



Delphi will create a project file (ButtonXControl1.bpr) and three units for the
project. The first unit is the TButtonX class unit (ButtonXImp1.pas). The second
unit is the type library file for the control, named ButtonXControl1_TLB.pas. This
file contains the information Delphi needs to create the type library for the control.
The third file, About1.pas, is the unit for the About box. If you want to customize
the About box, you can do that at this time. The About box is just another Delphi
form, so feel free to customize it in any way you like.





NOTE: Version info is required in order for your ActiveX controls to work
in Visual Basic.





Build, Register, and Install the Control
Because you aren't making any modifications to the control itself, you can jump
right to building the control and registering it. This is the same process you went
through when you registered the COM object you created earlier. An extra step is
required when implementing ActiveX controls, though, because ActiveX controls have
a design-time interface. Try this:



1. Choose Project|Build ButtonXControl1 from the main menu. Delphi builds
the ActiveX project.


2. Choose Run|Register ActiveX Server from the main menu. The ActiveX
control is registered and Delphi displays a message box telling you that the OCX
is registered (ActiveX projects have a default extension of .OCX). Click OK to dismiss
the message box.


3. Choose Component|Import ActiveX Control from the main menu. Choose
ButtonXControl1 Library (Version 1.0) from the list of installed controls (had you
not performed step 2, this entry would not have been present in the list of installed
controls). The class name of the button, TButtonX, shows in the Class names list
box.


4. Set the Palette page field to ActiveX. Click Install to continue.


5. The Install dialog box is displayed. You are going to install the control
into the default Delphi user package DCLUSR40.BPL. The File name field should already
contain this package. If it doesn't, choose it from the combo box. The Description
field now says Delphi User's Components. Click the OK button to install the control.


6. Click Yes to the message box regarding building and installing DCLUSR40.BPL.
Click OK when the message box confirming the installation is displayed. The control
is now installed.



Test the ActiveX Control
Now you can test your new ActiveX control. First, create a new project.





NOTE: When you create a new project, Delphi will prompt you to save the
package file (DCLUSR40.DPK) and the ActiveX control project. Whether you save these
files is up to you. My intention was to have you create a quick ActiveX. There's
really no need to save the files. If, however, you think you might want to save the
files to examine them later, save the files.





Now follow these steps:



1. Locate the ActiveX tab on the Component palette.


2. The last control in the list is the ButtonX control. Select it.


3. Place a ButtonX control on your form. Notice that the button doesn't
have a default caption as a regular VCL button does.


4. Change the Caption property to Test. The button's caption changes just
as a VCL button's caption would change.


Notice the list of properties in the Object Inspector. They are mostly the same
properties you would see on a VCL button (the ActiveX was created from a VCL TButton
after all), but you might have noticed that the Value column looks slightly different.
Remember, this is an ActiveX control and is intended to be used in any environment
that hosts ActiveX controls. For that reason, some of the property values are expressed
in a more generic way.


5. Double-click the button and you will find that nothing happens. An
ActiveX control doesn't have the capability to automatically create an event handler
when you double-click the button like a VCL component does. Instead, switch to the
Events page and double-click the Value column next to the OnClick event. An event
handler is generated. Type this line of code:






MessageDlg(`Hey, it works!', mtInformation, [mbOK], 0);







6. Run the program and test the button to ensure that it works. When you
have verified that the button works, close the program.


7. Bring up the form and right-click on the button. Choose About from
the context menu. The control's About box is displayed. The About box is not customized
in any way, but you can go back and do that later if you want (provided you saved
the file earlier).








NOTE: The idea behind one-step ActiveX is to take a working VCL component
and create an ActiveX control from that component. Most of the time, you won't have
to modify the ActiveX code in any way. However, you certainly can modify the ActiveX
code after it has been generated by Delphi if you so desire. Be aware, though, that
if you regenerate the ActiveX code from your original VCL component, all changes
made to the ActiveX source will be lost.
NOTE: You can create ActiveX controls only from windowed VCL controls (controls
derived from TWinControl or one of its descendents). The list of VCL controls from
which you can build an ActiveX control contains all installed components that specifically
meet this criteria.





Unregister the ActiveX Control
After experimenting with your new ActiveX control, you should unregister it so
that it doesn't occupy space in the Registry. To unregister the ActiveX control,
do this:



1. Choose Component|Import ActiveX Control from the main menu.


2. Select the ActiveX in the list of installed ActiveX controls and click
the Remove button.


3. Click Yes on the confirmation dialog box to have Delphi unregister
the ActiveX.



Alternatively, you can load the ActiveX project (if you previously saved it) and
choose Run|Unregister ActiveX Server from the main menu.





NOTE: If all else fails, you can always locate the ActiveX control in the
Registry and delete the key for that control. Use the Registry Editor's find function
to find the key (search for the control's name or its GUID). Naturally, you want
to be careful when editing the Registry manually.





Creating ActiveForms
Creating an ActiveForm is almost as easy as creating an ActiveX from an existing
VCL component. Naturally, you can create a complex ActiveX containing many components
on a single form. Contrary to what its name implies, however, an ActiveForm can be
used to create a simple ActiveX control from scratch (a colored button, for example).
In other words, ActiveForms are not only for creating fancy forms with dozens of
gadgets. They are for creating single-use ActiveX controls as well.
In this section, you will create an ActiveForm. The ActiveForm will have two edit
controls, a label and a button. The button will take the contents of the two edit
controls, multiply them together, and display the result in the label. Yes, I know
it doesn't require a lot of imagination to stick with the & quot;multiply two numbers & quot;
idea, but my goal is to show you how to create an ActiveForm with the minimum amount
of code. Keeping the code to a minimum allows you to focus on the ActiveForm creation
process without getting bogged down in code.
Create the ActiveForm
Creating an ActiveForm is so easy it's amazing. Follow these steps:



1. Close all projects and then choose File|New from the main menu. The
Object Repository is displayed.


2. Double-click the ActiveForm icon. The ActiveForm Wizard is displayed.
This dialog box is identical to the ActiveX Control Wizard except for the fact that
the VCL Class Name field is grayed (it doesn't apply here).


3. Enter MyFormX in the New ActiveX Name field.


4. Change the Implementation Unit field to read MyFormImpl.pas.


5. Change the Project Name field to MyFormProj.dpr.


6. Leave the Thread Model set to Apartment. Check the Include Version
Information check box.


7. Click the OK button to continue.



Delphi creates the required units and displays a form.
Create the Form
An ActiveForm form is just a regular form at this stage. You can add controls
to the form, add code, and respond to events just like you do for a form that belongs
to an application. The one difference is that the title bar on an ActiveForm does
not appear on the control itself. It's just there at design time.
In this step, you will add components and code to make the ActiveForm functional.
As you work through the steps, it might help to refer to Figure 15.8 later in the
chapter, which shows the completed form. I'm going to give you the primary components
in the following steps and let you finish the rest on your own. Perform these steps:



1. Size the form to approximately 175 (width) by 275 (height).


2. Add an Edit component near the top-center of the form (see Figure 15.8).
Change its Name property to Num1Edit, its Text property to 0, and its Width to 50
or so (the exact width is not important). Change the AxBorderStyle property to afbRaised.


3. Click on the Edit component and copy it to the Clipboard; paste a new
component from the Clipboard. Place the new component below the first. Change its
Name property to Num2Edit.


4. Place a Label component below the two edit controls. This label will
display the results. Change the label's Name property to ResultLbl and its Caption
property to 0.


5. Place a Button component on the form to the right of the Edit components.
Change its Name to GoButton and its Caption to Go!.


6. Double-click the button and make the OnClick event handler look like
this:






procedure TMyFormX.GoButtonClick(Sender: TObject);
begin
try
ResultLbl.Caption := IntToStr(
StrToInt(Num1Edit.Text) * StrToInt(Num2Edit.Text));
except
on EConvertError do
MessageDlg(`Oops! You entered an invalid value.',
mtError, [mbOK], 0);
end;
end;







This code simply extracts the values of the two edit controls, multiplies them
together, and displays the result in the ResultLbl label. The exception handling
code displays a message box if the user enters invalid values. An EConverError exception
will be raised if the conversion from text to integer fails (if one of the edit controls
contains text, for example).


7. Add additional labels to match Figure 15.8.


8. Choose View|Type Library from the main menu. In the Information page,
change the Help String field to My Test ActiveForm Library. This is the text that
will be displayed in the Import ActiveX dialog box when you install the ActiveForm.


9. Save the project. Accept the default filenames. (You specified them
in the ActiveForm Wizard.) Figure 15.8 shows the completed form.



FIGURE 15.8. The
finished ActiveForm.

Build, Register, and Import the ActiveForm
Now you can build, register, and import the ActiveForm. When built, the ActiveForm
is like any other ActiveX control. Because you've done this several times now, I'm
not going to go over every step. Follow these steps:



1. Choose Project|Build MyFormProj from the main menu.


2. When the project is built, choose Run|Register ActiveX Server from
the main menu.


3. Choose Component|Import ActiveX Control from the main menu. Install
My Test ActiveForm Library (Version 1) into the DCLUSR40 package. Install to the
ActiveX page or any other page you choose.



The ActiveForm is now installed as an ActiveX control.
Try the ActiveForm
Now it's time to take the ActiveForm for a test drive. This will be fairly simple:



1. Create a new application.


2. Click the ActiveX tab on the Component palette and choose MyFormX button
(the one with the default Delphi icon).


3. Place a MyFormX control on your form.


4. Run the program and test out the ActiveX.



That's all there is to it. With Delphi, great-looking ActiveX controls are a breeze
to create! There simply is no better development environment for creating ActiveX
controls than Delphi, bar none.

Changing the ActiveX Palette Bitmap
Ultimately you will want to change the bitmap of the ActiveX from the default
that Delphi provides to one of your own design. Changing the bitmap requires following
these steps:



1. Create a binary resource file (.RES) with Image Editor.


2. Create a 24 & #165;24 bitmap. Give the bitmap a numeric name (2 for example).


3. Link the resource file to the ActiveX project with the $R compiler
directive. (Linking resources was discussed on Day 8, & quot;Creating Applications
in Delphi & quot; and is discussed further on Day 20, & quot;Creating Components. & quot;)


4. Modify the ActiveX class factory creation routine in the implementation
unit (the ActiveForm's .PAS file). A typical class factory creation routine looks
like this (it's in the initialization section at the bottom of the unit):






TActiveFormFactory.Create(
ComServer,
TActiveFormControl,
TMyFormX,
Class_MyFormX,
1, { Change this number. }
`',
OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL,
tmApartment);







Notice the line I have marked with a comment. This parameter of TActiveFormFactory.Create
is the resource number of the bitmap you want displayed on the Component palette.
If you saved the new bitmap with a name of 2, you would replace the 1 in this code
snippet with a 2.


5. Rebuild, reregister, import, and install the ActiveForm again. The
new bitmap should now show up in the Component palette.



Alternatively, you can modify the ActiveForm project's .RES file and change the
bitmap named 1 to look like you want.

Web Deploying ActiveX Controls and ActiveForms
One of the great features of ActiveForms is that you can use them on a Web page.
In order to use an ActiveForm on a Web page, you must use the Web Deploy option.
Using Web Deploy requires a Web server, so I can't effectively walk you through the
Web deployment process. I can, however, give you a little insight into the process.
When you choose Web Deploy, Delphi performs two tasks:


Builds the ActiveX control and copies the file to the Web Deploy target directory

Creates an HTML file that contains the code needed to load the ActiveX control


The locations of these files is determined by the Web deployment options. Let's
look at that next.

Web Deployment Options
Before you can use Web Deploy, you must set the Web deployment options. To set
the Web deployment options, choose Project|Web Deployment Options from the main menu.
The Web Deployment Options dialog box is displayed, as shown in Figure 15.9.
Figure 15.9. The
Web Deployment Options dialog box.

At the bottom of the Web Deployment Options dialog box is a check box labeled
Default. Check this box if you want the settings you have specified to be the new
defaults for future projects. Most of the time, you will deploy to the same Web site,
so you will probably want to set the defaults after you have everything set up just
the way you want it.

Project Page: Directories and URLs Section
The Directories and URLs section is where you specify the target location for
your ActiveX. The Target dir field is used to specify the directory where Delphi
will copy the ActiveX after it is built. This field must be a directory--it cannot
be an URL location.
If you are like me, you might not have direct access to the directory where your
Web site is located. (TurboPower's Webster Royland keeps access pretty tightly controlled.)
If that is the case, you will have to specify a local directory in this field and
then later use your Web publishing software to publish the files to your Web site.
The Target URL field is used to specify the page where the ActiveX will reside
on the server. This page is used by Delphi when it creates the HTML page that shows
the control. For example, the HTML file that Delphi created for me is shown in Listing
15.4. (I had to break a couple of lines because they were too long for the page.)

LISTING 15.4. THE HTML CODE GENERATED BY DELPHI FOR THE ACTIVEX FILE.
& lt;HTML & gt;
& lt;H1 & gt; Delphi 4 ActiveX Test Page & lt;/H1 & gt; & lt;p & gt;
You should see your Delphi 4 forms or controls
embedded in the form below.
& lt;HR & gt; & lt;center & gt; & lt;P & gt;
& lt;OBJECT
classid= & quot;clsid:52FB5B97-EDA3-11D1-B47B-0040052A81F8 & quot;
codebase= & quot;http://www.home.turbopower.com/~kentr/test/MyFormProj.cab
#version=1,0,0,0 & quot;
width=275
height=175
align=center
hspace=0
vspace=0
& gt;
& lt;/OBJECT & gt;
& lt;/HTML & gt;

Notice the URL in the codebase statement. This is the path I typed in the Target
URL field of the Web Deployment Options dialog box. By the way, you can copy the
entire OBJECT tag from the Delphi-generated HTML code directly to your Web page's
HTML source when you get ready to officially deploy your ActiveX code.





NOTE: The name of the HTML file created by Delphi is the project name with
an extension of .htm.





The HTML dir field of the Web Deployment Options dialog box is used to specify
the location where Delphi will place the HTML code it generates (refer to Listing
15.4). As with the Target dir field, if you don't have direct access to your Web
site's directories, you will have to specify a local directory and then publish the
HTML file to your Web site.

Project Page: General Options Section
This section is where you specify the global Web deployment options. The Use CAB
file compression field determines whether the ActiveX is compressed. Compressing
the ActiveX reduces the size of your ActiveX, making downloading the control that
much faster. I used CAB compression on the ActiveForm created earlier in the project
and the ActiveX size went from 312KB in OCX form to 204KB in CAB form. Windows takes
care of automatically decompressing and registering the ActiveX, so there's no real
disadvantage to using CAB compression.
The Include file version number indicates whether Delphi should include the version
number in the codebase statement (again refer to Listing 15.4). The version tag is
optional, so you don't specifically need it. Note, however, that some browsers won't
load the ActiveX if the version tag is present (Netscape Navigator with an ActiveX
plug-in, for example).
The Auto increment release number will automatically increment the version number
in the ActiveX's version info each time the ActiveX is deployed.
The Code sign project option plays an important role in ActiveX deployment. When
this option is on, Delphi will code sign the ActiveX. Code signing is the
process of attaching a binary signature to a file. This binary signature identifies
the company that created the ActiveX, among other information.
Code signing is important because Internet Explorer expects ActiveX controls to
be code signed. If Internet Explorer's security level is set to High or Medium, any
ActiveX controls that are not code signed will not load. Simply put, if you are going
to deploy your ActiveX controls so that the public can use them, they must be code
signed.
The Code Signing page of the Web Deployment Options dialog box contains the information
needed to code sign the ActiveX. Delphi does not provide the credentials file or
the private key needed to code sign files. To obtain a credentials file and private
key, you will need to contact Microsoft. For more information, search the Microsoft
Web site for the terms & quot;Digital Signing & quot; and & quot;Certificate Authority. & quot;
The Deploy required packages and Deploy additional files options are used if you
have built your ActiveX with runtime packages or if there are additional files that
must ship with your control. If you choose either of these options, you must specify
the packages or additional files on the Packages and Additional Files pages of the
Web Deployment Options dialog box.





NOTE: When in doubt, click the Help button in the Web Deployment Options
dialog box. Delphi help explains each of the pages of this dialog box.





Web Deploy
After you set the deployment options, you are ready to deploy your ActiveX. To
deploy the ActiveX, simply load the ActiveX project and choose Project|Web Deploy
from the Delphi main menu. Delphi will build the ActiveX and deploy it based on the
settings in the Web Deployment Options dialog box. If you elected to use CAB compression,
Delphi will compress the ActiveX into a CAB file as well. Remember, if you don't
have direct access to your Web site's directories, you will have to publish the ActiveX
and HTML file to your Web site before you can test the ActiveX.
The act of deploying the ActiveX is trivial--setting the Web deployment options
is the hard part. Figure 15.10 shows the example ActiveForm created earlier running
on a Web page.
FIGURE 15.10. The
test ActiveForm running on a Web page.





NOTE: ActiveX controls have virtually no security restrictions. Be careful
when downloading ActiveX controls from unknown (or unverified) sources. When downloaded,
an ActiveX control has access to your entire system. Be equally careful when writing
your own ActiveX controls. Make absolutely sure that your ActiveX control won't do
anything to negatively impact other users' machines.





Summary
I won't lie to you--there's a lot more to COM and ActiveX than what is presented
here. For example, I didn't talk about OLE. OLE, like ActiveX, is a subset of COM.
OLE adds a layer to COM to enable applications to link and embed OLE automation servers
in a container application. Still, you learned a great deal about COM and ActiveX
today. Most importantly, you found out how to create COM objects, ActiveX controls,
and ActiveForms. You also learned a bit about Web Deploy and how to use it to deploy
your ActiveX controls on a Web page.


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, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A



Q Do I have to understand the inner workings of COM to write ActiveX controls
in Delphi?


A Although some understanding of COM is certainly helpful, it is not vital
to creating ActiveX controls with Delphi. Delphi makes it easy to create ActiveX
controls without an in-depth understanding of COM.


Q What is a type library?


A A type library is a binary file that describes the interfaces, data
types, methods, and classes in a COM library (including ActiveX).


Q Are OLE and COM the same thing?


A No. COM is the base upon which OLE is built. OLE is much more complex
and convoluted than COM. Certainly OLE has more functionality, but OLE is a bloated
beast that is best avoided if possible.


Q I noticed some nifty-looking ActiveX controls registered on my system, so
I installed them on the Delphi Component palette. They show up on the Component palette
but when I try to use one of the controls, I get an error message about a design-time
license. Why is that?


A In short, you are not authorized to use those controls in a design environment
(Delphi, for example). ActiveX users must deploy and register their ActiveX controls
on every system that uses those controls. To prevent anyone from freely using those
controls, the control vendors require a design-time license before the control can
be used at design time. When you purchase an ActiveX control from a vendor, you get
the design-time license.


Q I created an ActiveX control and placed it on my form. The program used
to work fine, but now when I try to run the program I get an exception that says,
Class not registered. Why is that?


A Every ActiveX control must be registered on the system on which it is
being used. You might have inadvertently unregistered the ActiveX control on your
system some time after you originally installed the control. To reregister the control,
load the ActiveX project in Delphi and choose Run|Register ActiveX Server. Alternatively,
you can register the OCX file with the TREGSVR.EXE utility.


Q I created and installed an ActiveForm and everything went fine. Later I
wanted to change the ActiveForm. I couldn't compile the ActiveForm project, though,
because I kept getting an error, Could not create output file MyProj.OCX. What's
wrong?


A You need to remove the ActiveForm control from the Delphi Component
palette before you can rebuild the control. When the control is installed in Delphi,
its OCX file is loaded by the Delphi IDE and cannot be overwritten.


Q Web Deploy confuses me. There are so many options. Am I the only one who
doesn't understand this stuff?


A Certainly not. After you have worked through the Web Deploy process
a few times, it's not nearly so daunting as it might appear the first time you try
it.


Q I'm having problems getting my ActiveX to work on a Web page. I keep getting
an error from Internet Explorer when I try to load the page. The error says, Your
current settings prohibit ActiveX controls. What's wrong?


A Your ActiveX control is not code signed. An ActiveX control must be
code signed before Internet Explorer will download the control when the user's security
settings are set to either medium or high.



Quiz



1. What is the base (or parent) interface for all COM interfaces?


2. What is a GUID?


3. What happens when a COM object's reference count reaches 0?


4. What is the name of the Delphi utility used when working with type
libraries?


5. How do you create GUIDs when writing COM objects in Delphi?


6. What do you choose from the Object Repository when creating an ActiveX
from a VCL component?


7. Can you use Delphi-created ActiveX controls in Visual Basic?


8. After your ActiveX control is built and registered, how do you install
it to the Delphi Component palette?


9. How do you unregister an ActiveX that you have created?


10. Can you use the ActiveX controls created in Delphi on a Web page?



Exercises



1. Create a simple COM object from scratch. It isn't important what the
COM object does, just that you go through the steps of creating it.


2. Write a Delphi application that uses the COM object created in exercise
1 (don't forget to register the COM object).


3. Create an ActiveX control from a VCL TEdit component. Install the control
to the Delphi Component palette and use it on a form.


4. If you have access to Visual Basic, take the ActiveX created in exercise
3 and use it in a VB application.


5. Create an ActiveForm control of your own design.


6. If you have a Web site, deploy the ActiveForm created in step 5 and
display it on a Web site page.


7. Extra Credit: Modify the ActiveForm control created in step 5 so that
it uses a custom bitmap on the Delphi Component palette rather than the default bitmap.









& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.


delphi4.zip > apc.htm

' + pPage + ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ';
zhtm += ' ' + pPage + ' ';
zhtm += ' ';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}

//-- & gt;





Teach Yourself Borland Delphi 4 in 21 Days -- Appendix C -- Building Internet Applications









Teach Yourself Borland Delphi 4 in 21 Days







- Bonus -


Building Internet Applications



Internet Components Available in Delphi
Building a Web Browser

Who Needs Yet Another Browser?
First Steps in Building Your Browser
Adding a Progress Indicator
Some Finishing Touches

Using Internet Explorer as an ActiveX Control
Sending Mail
Deploying Internet Applications
Summary
Workshop

Q & amp;A
Quiz

Exercises








Are you one of those people who think the Internet is just a fad, something that
will never last? If so, let me tell you something...you're wrong! The Internet is
huge and it's getting bigger every day. In part, the Internet is about the Web and
about people spending hour after hour browsing the Web. But the Internet is also
about file transfers, email, and electronic commerce. The Internet is big business.
It isn't going away anytime soon, so you might want to polish your Internet programming
skills. Thankfully, Delphi makes it easy to experiment with Internet programming
and to do serious Internet programming as well.
Today, you look at some aspects of Internet programming with Delphi. There's a
whole world waiting out there on the Internet, so let's get to it.

Internet Components Available in Delphi
The Internet components in Delphi are located on the Internet page of the Component
palette and come in two categories. The first category is a group of components provided
by NetMasters. With one exception, these are native VCL components. The exception
is the THTML control, which is an ActiveX control. Table BD.1 lists the NetMasters
controls and provides a description of each control. The controls are listed in the
order in which they appear on the Component palette.

TABLE BD.1. NETMASTERS ACTIVEX INTERNET CONTROLS.



Control
Description


TNMDayTime
Obtains the date and time from Internet daytime servers.


TNMEcho
Sends text to and receives text from Internet echo servers.


TNMFinger
Obtains information about a user from an Internet finger server.


TNMFTP
Performs file transfers between networked machines using FTP (File Transfer Protocol).


TNMHTTP
Performs file transfers using HTTP (Hypertext Transport Protocol). Hypertext documents
are normally viewed in a Web browser. You use THTTP to retrieve Web documents that
don't need to be displayed in a Web browser.


TNMMsg
Sends simple ASCII text messages using TCP/IP.


TNMMSGServ
Receives messages sent with the TNMMsg control.


TNMNNTP
Sends messages to and receives messages from Internet news servers using NNTP (Networking
News Transfer Protocol).


TNMPOP3
Retrieves email messages from mail servers using POP3 (Post Office Protocol).


TNMUUProcessor
Encodes or decodes MIME or uuencoded files.


TNMSMTP
Sends email through SMTP (Simple Mail Transfer Protocol) mail servers.


TNMStrm
Sends data streams to a network or Internet stream server.


TNMStrmServ
Receives streams sent by the TNMStrm control.


TNMTime
Obtains the date and time from Internet time servers.


TNMUDP
Transfers data across networks using UDP (User Datagram Protocol).


TPowersock
Encapsulates the Winsock API.


TNMGeneralServer
Used for general TCP/IP servers.


THTML
Displays HTML (Hypertext Markup Language) files. This is a Web



browser component.


TNMURL
Converts URL data to a readable string and string data to URL format.



The second category of controls includes native VCL components provided by Borland.
The TClientSocket and TServerSocket components come with both the Professional and
Client/Server versions of Delphi. The Web Broker components (TWebDispatcher, TPageProducer,
TQueryTableProducer, and TDataSetTableProducer) come with only the Client/Server
version. The VCL Internet components are listed in Table BD.2.

TABLE BD.2. NATIVE VCL INTERNET COMPONENTS.



Component
Description


TClientSocket
Manages a TCP/IP client socket connection.


TServerSocket
Manages a TCP/IP server socket connection.


TWebDispatcher
Converts an ordinary data module to a Web module.


TPageProducer
Enables building of dynamic HTML pages.


TQueryTableProducer
Takes TQuery results and generates an HTML document from them.


TDataSetTableProducer
Takes TDataSet records and generates an HTML document from them.



These two groups of controls give you all the power you need to build high-quality
Internet applications.

Building a Web Browser
One of the most visible Internet programming tasks is building a Web browser.
Certainly it's the most glamorous. The good news is that it can also be the easiest.

Who Needs Yet Another Browser?
You might be wondering why anyone would want to build a Web browser. After all,
the world already has Netscape Navigator and Internet Explorer, so who needs another
browser? True enough, you probably aren't going to try to build a Web browser that
competes with Netscape or Microsoft. On the other hand, consider a company that has
hundreds or even thousands of employees who need access to the Web. It can be very
costly to license thousands of copies of a commercial Web browser. On the other hand,
you can write a quality Web application with Delphi in just a few hours, thereby
saving the company a lot of money.
Another reason a company might want a custom Web browser is to restrict access
to the Web. For example, there might be sites on the Internet that employees have
to visit from time to time. A custom Web browser enables access to authorized sites
on the Web but not to any other (unauthorized) sites. In fact, a custom browser is
perfect for your kids!
Finally, one of the most compelling reasons for a custom Web browser is an intranet .
An intranet is a Web site that is local to a particular network. An intranet can
contain a variety of information for a company's internal use--information on company
benefits, company policies, an employee address book, meeting schedules, or even
information on the company bowling league. A custom Web application can provide access
to an intranet and prevent access to the Internet.
So with that in mind, you'll build a simple Web browser. You'll probably be surprised
how easy it is.

First Steps in Building Your Browser
The THTML control is a ready-to-use Web browser. All you have to do is place one
of these controls on a form and call the RequestDoc method. Well, that might be a
little oversimplified, but you can display a Web document from anywhere on the Internet
as easily as that. With that in mind, let me show you how quickly you can write a
Web browser application. Here are the first steps:



1. Start with a new application. Change the form's Name property to WebMain
and the Caption property to EZ Web Browser.


2. Place a Panel component on the form and change its Align property to
alTop and its Height property to 60. Clear the Caption property.


3. Place a ComboBox component on the panel. Move it near the top of the
panel and make it as wide as the panel itself. Change the Name property to URLComboBox.
Change the Text property to an URL of your choice (try http://www.turbopower.com).
Double-click the Constraints property and change the AnchorHorz constraint to akStretch.


4. Place a StatusBar component on the form. It will automatically position
itself at the bottom of the form. Change its Name property to StatusBar and its SimplePanel
property to True.


5. Place an HTML control in the middle of the form. Change its Align property
to alClient. The HTML control fills the screen. Change the Name property to HTML.


Now your form should look similar to the one shown in Figure BD.1. If your application
doesn't look exactly like Figure BD.1, adjust as necessary or just leave it. (A little
individuality isn't a bad thing, after all.)




At this point, you should save the project. Save the form as WebBrwsU.pas and
the project as WebBrows.dpr. Now you'll add just enough functionality to make the
browser do something useful.


FIGURE BD.1. Your
new Web browser after the first steps.




6. Click on the URL combo box. Generate an event handler for the OnClick
event. Type the following code in the event handler:






if URLComboBox.Text & lt; & gt; `' then
HTML.RequestDoc(URLComboBox.Text);







The RequestDoc method loads the requested document after first checking that
the combo box contains text.


7. Now generate an event handler for the OnKeyPress event. Type the following
code in the event handler:






if Key = Char(VK_RETURN) then begin
Key := #0;
if URLComboBox.Text = `' then
Exit;
URLComboBoxClick(Sender);
end;







This code first checks the Key parameter to see whether the Enter key was pressed.
If so, it sets Key to 0 and calls the URLComboBoxTest method (created in step 6).
Setting Key to 0 prevents the speaker from beeping when the Enter key is pressed.
The call to the URLComboBoxClick method loads the URL into the Web browser.


8. Now compile and run the program. Type an URL in the combo box and press
Enter. If you typed in a valid URL, the page will load in the HTML control.



Wow! A Web browser in 15 minutes! Notice that the browser acts like any other
Web browser...well, sort of. You need to add a lot of functionality, but it's a start.





NOTE: If you are fortunate enough to have a full-time Internet connection,
your new browser will work immediately. If you are using dial-up networking with
auto dial enabled, the dialer will start automatically and connect to your Internet
service provider (ISP). If you don't have dial-up networking installed, you have
to connect to the Internet manually before running the program.






Adding a Progress Indicator
You now have a good start on your Web browser. One of the features you are missing
is some status information on each page as it loads. What you'll do is add a routine
that updates the status bar as a page loads. You make use of the THTML control's
OnUpdateRetrieval and OnEndRetrieval events to obtain periodic status updates. You
will use the GetBytesTotal and GetBytesDone methods to calculate a percentage and
then display the percentage loaded in the status bar. Ready?



1. Click on the HTML control on your form. Generate an event handler for
the OnUpdateRetrieval event. Add code to the event handler so that it looks like
this:






procedure TWebMain.HTMLUpdateRetrieval(Sender: TObject);
var
Total : Integer;
Done : Integer;
Percent : Integer;
begin
Total := HTML.RetrieveBytesTotal;
Done := HTML.RetrieveBytesDone;
if (Total = 0) or (Done = 0) then
Percent := 0
else
Percent := ((Done * 100) div Total);
StatusBar.SimpleText := Format(
`Getting Document: %d%% of %dK', [Percent, Total div 1024]);
end;







2. Now generate an event handler for the OnEndRetrieval event. Type this
code in the event handler:






StatusBar.SimpleText := `Done';




Take a closer look at the code in step 1. There isn't very much to it. The GetBytesTotal
method tells you how many bytes are in the document or the embedded object currently
being loaded (objects include images). The GetBytesDone method gives the number of
bytes that have been retrieved for the page or object up to this point. From there
it's a simple matter to calculate the percentage of the object that has been retrieved.
Finally, you format a string with the information obtained from the HTML control
and send it to the status bar. The code in step 2 simply updates the status bar after
the entire document has been loaded.
Run the program again and watch what happens when you load a page. The status
bar shows the percentage loaded for the page and for any embedded objects.

Some Finishing Touches
Now for some finishing touches. First, you'll add some buttons beneath the URL
combo box. (For a peek at the finished product, see Figure BD.2.) Here goes:



1. Place a button on the panel beneath the URL combo box. Make its Name
property GoBtn and change its Caption to Go!.


2. Generate an event handler for the OnClick event for the new button.
Enter the following code in the event handler:






URLComboBoxClick(Self);







3. Place another button on the panel, just to the right of the first button.
Change the Name to StopBtn and the Caption to Stop.


4. Generate an event handler for the OnClick event for this button and
type the following code in the event handler:






HTML.Cancel(0);

StatusBar.SimpleText := `Done';







5. Place a third button on the panel to the right of the other two buttons.
Change the Name property to ReloadBtn. Change the Caption property to Reload.


6. Create an event handler for the OnClick event for this button and enter
the same code as in step 2:






URLComboBoxClick(Self);







7. Place a fourth (and final) button on the panel. Change the Name to
SourceBtn and change the Caption to View Source.


8. Create an event handler for the OnClick event and enter this code:






HTML.ViewSource := not HTML.ViewSource;
if HTML.ViewSource then
SourceBtn.Caption := `View Document'
else
SourceBtn.Caption := `View Source';




Your form should now look like the one shown in Figure BD.2.
FIGURE BD.2. The
EZ Web Browser with buttons in place.
These steps introduce a couple of new THTML elements. The Cancel method stops
the process of loading a document. The ViewSource property is used to toggle between
viewing the document as HTML source or as a regular HTML document.
Now run the program again. Check out the new buttons and see what they do. In
particular, give the View Source button a try.
Okay, you're almost done with your Web browser. Let's add a couple of more features.
You are going to respond to two more events of the THTML control in order to provide
more status information.



1. Generate an event handler for THTML's OnDoRequestDoc event. Type this
code in the event handler:






StatusBar.SimpleText := `Connecting to ` + URL + `...';







2. Now create an event handler for the OnBeginRetrieval event. When the
event handler appears, type this code:






StatusBar.SimpleText := `Connected...';
URLComboBox.Items.Insert(0, URLComboBox.Text);




In this sequence, step 1 makes use of the OnDoRequestDoc event, which is generated
when a document is requested. The URL parameter of the DoRequestDoc event handler
is the URL of the site to which you are connected. As long as the URL is provided,
you can use it to build a string to display in the status bar. Step 2 adds a little
more status information when the document actually starts to load. It also takes
the URL and adds it to the URL combo box's list. You need to make sure that you have
connected to a site before adding the URL to the list of visited sites.
Congratulations, you have finished (or nearly finished) your first Internet application.
Figure BD.3 shows the EZ Web Browser in operation.
FIGURE BD.3. The
finished EZ Web Browser displaying a page.
Hey, that's good work! There are some things that your Web browser doesn't do,
but it does a lot, so you can be proud. Stand back and admire your work. You can
take your new creation and add some new features of your own. One feature you might
want to add is a list of URLs that can be used to implement browse buttons (back
and next). You could also replace the standard buttons with a toolbar and add glyphs
to the toolbar's buttons. If you really want to add the ultimate touch, provide an
animation while the document is loading so that your users can tell when your browser
is doing something. You can do that most easily with a TImageList component, although
the TAnimate component would work, too.
The THTML control has several properties that I didn't cover. Most of these properties
have to do with user preferences such as background color, the color of links, the
color of visited links, the various fonts used for each heading size, and so on.
I'm not going to explain those properties because they are easy to figure out. For
the most part you can just accept the default values for these properties. If you
want to customize your browser further, though, you can certainly spend some time
reviewing the properties list for the THTML control.
Listing BD.1 lists the browser program's main unit.

LISTING BD.1. WebBrwsU.pas.
unit WebBrwsU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, OleCtrls, NMHTML, ComCtrls;
type
TWebMain = class(TForm)
Panel1: TPanel;
URLComboBox: TComboBox;
StatusBar: TStatusBar;
HTML: THTML;
GoBtn: TButton;
StopBtn: TButton;
ReloadBtn: TButton;
SourceBtn: TButton;
procedure URLComboBoxClick(Sender: TObject);
procedure URLComboBoxKeyPress(Sender: TObject; var Key: Char);
procedure HTMLUpdateRetrieval(Sender: TObject);
procedure HTMLEndRetrieval(Sender: TObject);
procedure Gobtnclick(Sender: Tobject);
procedure StopBtnClick(Sender: TObject);
procedure ReloadBtnClick(Sender: TObject);
procedure SourceBtnClick(Sender: TObject);
procedure HTMLDoRequestDoc(Sender: TObject; const URL: WideString;
Element: HTMLElement; DocInput: DocInput;
var EnableDefault: WordBool);
procedure HTMLBeginRetrieval(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
WebMain: TWebMain;
implementation
{$R *.DFM}
procedure TWebMain.URLComboBoxClick(Sender: TObject);
begin
if URLComboBox.Text & lt; & gt; `' then
HTML.RequestDoc(URLComboBox.Text);
end;
procedure TWebMain.URLComboBoxKeyPress(Sender: TObject; var Key: Char);
begin
if Key = Char(VK_RETURN) then begin
Key := #0;
if URLComboBox.Text = `' then
Exit;
URLComboBoxClick(Sender);
end;
end;
procedure TWebMain.HTMLUpdateRetrieval(Sender: TObject);
var
Total : Integer;
Done : Integer;
Percent : Integer;
begin
Total := HTML.RetrieveBytesTotal;
Done := HTML.RetrieveBytesDone;
if (Total = 0) or (Done = 0) then
Percent := 0
else
Percent := ((Done * 100) div Total);
StatusBar.SimpleText := Format(
`Getting Document: %d%% of %dK', [Percent, Total div 1024]);
end;
procedure TWebMain.HTMLEndRetrieval(Sender: TObject);
begin
StatusBar.SimpleText := `Done';
end;
procedure TWebMain.GoBtnClick(Sender: TObject);
begin
URLComboBoxClick(Self);
end;
procedure TWebMain.StopBtnClick(Sender: TObject);
begin
HTML.Cancel(0);
StatusBar.SimpleText := `Done';
end;
procedure TWebMain.ReloadBtnClick(Sender: TObject);
begin
URLComboBoxClick(Self);
end;
procedure TWebMain.SourceBtnClick(Sender: TObject);
begin
HTML.ViewSource := not HTML.ViewSource;
if HTML.ViewSource then
SourceBtn.Caption := `View Document'
else
SourceBtn.Caption := `View Source';
end;
procedure TWebMain.HTMLDoRequestDoc(Sender: TObject; const URL: WideString;
Element: HTMLElement; DocInput: DocInput; var EnableDefault: WordBool);
begin
StatusBar.SimpleText := `Connecting to ` + URL + `...';
end;
procedure TWebMain.HTMLBeginRetrieval(Sender: TObject);
begin
StatusBar.SimpleText := `Connected...';
URLComboBox.Items.Insert(0, URLComboBox.Text);
end;

end.

Using Internet Explorer as an ActiveX Control
If you have Microsoft Internet Explorer installed on your system, you can use
it as an ActiveX control. The first thing you need to do is import the control into
Delphi's Component palette. After that, you can place it on a form just as you would
any control. First, let me show you how to import Internet Explorer. (I showed you
how to import ActiveX controls on Day 15, & quot;COM and ActiveX, & quot; but it doesn't
hurt to review.) Here are the steps:



1. Choose Component | Import ActiveX Control from the main menu. The Import
ActiveX dialog box is displayed.


2. Locate Microsoft Internet Controls (Version 1. x ) in the list
of ActiveX controls (see Figure BD.4). If you have Internet Explorer 3 installed,
the version number will be 1.0. If you have Internet Explorer 4 installed, the control
will be listed as version 1.1. Notice that the Class names field shows TWebBrowser
as the control contained in this file. If you have Internet Explorer 4, the Class
names field will also show TWebBrowser_V1 (the original WebBrowser control) and TShellFolderViewOC.



FIGURE BD.4. The
Import ActiveX dialog box.





3. Click Install to install the control (the rest of the fields on this
dialog box can be left on their default values).


4. The Install dialog box appears asking for a package name. Click on
the Into new package tab at the top of the dialog and type IE in the File name field.
(You can type a description if you want, but it's not necessary.) Click OK to create
the package.


5. A confirmation dialog box appears asking whether you want to build
and install the package. Click Yes to create the package.



Delphi builds the package and then displays a dialog box saying that the TWebBrowser
control has been installed. Now you can try out the control:



1. First, choose File | Close All to close all windows, and then create
a new application.


2. Click the ActiveX tab on the Component palette. Choose the WebBrowser
control and drop it on your main form. Size the control as desired, but leave room
on the form for a button.


3. Drop a Button component on the form. Double-click the button to generate
an OnClick event handler. Type the following code in the event handler (use any URL
you want):






WebBrowser1.Navigate(`http:\\www.turbopower.com',
EmptyParam, EmptyParam, EmptyParam, EmptyParam););







As you might surmise, the Navigate method loads the document in the Web browser.


4. Click the Run button to run the program.



When the program runs, click the form's button. The Web page will load and be
displayed in the WebBrowser control.
When you install this control, Delphi creates a unit called SHDocVw_TLB.pas. You
can look at this unit to see what properties and methods are available for TWebBrowser.





TIP: You can find documentation for the WebBrowser control on Microsoft's
Web site. Search the Microsoft site for the text Reusing the WebBrowser Control.






Notice that you can't redistribute the TWebBrowser control itself without obtaining
a license from Microsoft. However, if you know your users have Internet Explorer
installed on their systems, your application will work because the control is already
installed. You still have to register the control on your user's machine for your
application to run. See & quot;Deploying Internet Applications & quot; later in this
chapter for more information on registering ActiveX controls.

Sending Mail
There are many reasons you might want to send mail from one of your applications.
The good news is that sending email isn't difficult at all. Maybe you want your program
users to be able to email you with any problems they encounter. In that case, your
application could pop up a form containing a Memo component and a Send button. Your
users can type text in the Memo component, press the Send button, and the message
is emailed to you. You can even go so far as to attach a log file from your application
to diagnose any problems the user is having.
The TNMSMTP control is used for sending mail through an SMTP server. SMTP is an
odd protocol in that it doesn't require authenticated logon to the server (at least
on most SMTP servers). You can simply connect to any mail server, send the email
message, and disconnect. The Host property is used to specify the host name of the
mail server to which you want to connect. Most of the time, you can just use mail
as the host name. Specifying mail tells the TNMSMTP control to connect to your local
mail server, whether it's your ISP's mail server or your company's mail server.
If you want, you can specifically set the mail server name (such as mail.mycompany.com),
but it isn't usually necessary. If you specify an incorrect mail server, you get
an ESockError exception. The Port property is used to specify the port on which to
connect. The default SMTP port is port 25. The Port property defaults to a value
of 25, so you shouldn't have to change this property.
All the pertinent information for the mail message itself is contained in the
PostMessage property. This property is a class that contains properties such as ToAddress,
FromAddress, Subject, Body, and so on. You fill in the appropriate fields of the
PostMessage property and then send the message.
Before you can send a mail message, you need to connect to the SMTP server. This
is done with the Connect method:

SMTP.Host := `mail';
SMTP.Connect;

After you're connected, you can send the email. The OnConnect event handler is
a good place for this code because you know that you are connected to an SMTP server.
For example:

procedure TMainForm.SMTPConnect(Sender: TObject);
begin
with SMTP.PostMessage do begin
FromAddress := `bilbo@baggins.com';
ToAddress.Add(`gandolf@baggins.com');
Subject := `Test';
Body.Add(`This is a test');
end;
SMTP.SendMail;
end;

This code sets up the FromAddress, ToAddress, Subject, and Body parameters of
the PostMessage property and then sends the message with the SendMail method. It's
as simple as that. Notice that the ToAddress and Body properties of PostMessage are
TStringLists. The mail message body can contain several lines of text, so it's not
surprising that the Body property is a TStringList. The ToAddress property is also
a TStringList, so you can specify several recipients for the message.





NOTE: The FromAddress and ToAddress fields are required fields. All other
fields (even the message body) are optional.





After you know the message has been sent successfully, you can disconnect from
the SMTP server. The OnSuccess event is generated when the mail has been sent. Your
OnSuccess event handler might be as simple as the following:


void __fastcall tform1.smtpsuccess(tobject *sender then
begin
SMTP.Disconnect;
end;



You can send several messages per connection, of course. If you have several messages
to send, you don't have to disconnect from the server and reconnect for each message.
Just connect once, send all your messages, and then disconnect from the server.
Your mail message might go through without incident, or it might fail in one way
or another. In either case, you must be prepared to disconnect from the server. The
OnFailure event is generated if the mail message fails to go through, so you can
use that event to disconnect from the server as well as the OnSuccess event. The
book's code, found at http://www.mcp.com/info, contains a simple mail program that
illustrates how to send mail with TNMSMTP.

Deploying Internet Applications
If your Internet application uses only the VCL Internet components, there is nothing
special to do when you deploy your application unless you are using runtime packages.
If you are using runtime packages, you need to ship INET40.BPL, and if you are using
the page producer components, ship INETDB40.BPL as well.
Deploying an application that uses ActiveX controls, however, requires more work.
ActiveX controls must be registered on the machine on which your application will
run. The easiest way to register ActiveX controls is with a good installation program.
InstallShield Express comes with Delphi Professional and Client/Server versions,
so you should try that program. Another good installation program is Wise Install
from Great Lakes Business Solutions. The better installation programs register, as
part of the installation process, the ActiveX controls that your application uses.
If you don't use a commercial installation program, you have to manually register
any ActiveX controls your application uses. The TREGSVR.EXE utility is used to register
and unregister ActiveX and OCX controls. This utility is in your Delphi 4\Bin directory.
For example, to install the EZ Web Browser application you created earlier today,
you ship the following files:

HTML.OCX
NMOCOD.DLL
NMSCKN.DLL
NWM3VWN.DLL
NMORENU.DLL
WEBBROWS.EXE

After you install all these files, you have to run TREGSVR.EXE on the HTML.OCX
and NMOCOD.DLL files to register them. From the command line, you type

TREGSVR HTML.OCX

Do the same for NMOCOD.DLL. The THTML control is now registered on your user's
machine and your program will run as intended.





NOTE: If you don't register your ActiveX controls properly, your users
will see a message box that says, & quot;Class not registered. & quot; when they attempt
to run your program. Your application will then terminate, leaving your users wondering
what went wrong.





To unregister a control, use the /u switch as follows:

TREGSVR /u HTML.OCX

Here again, a good installation program will have an uninstall option that takes
care of this for you.
As you can see, ActiveX controls require a bit of work to install after your application
is built. If you aren't aware of the fact that you need to register the ActiveX controls,
it can lead to confusion for both you and your users. By the way, the files needed
to deploy an application using the THTML control total about 900KB, so using ActiveX
controls can be expensive in terms of disk space. I prefer to use native VCL controls
whenever possible for exactly this reason.

Summary
Today you learned about the Internet components provided in Delphi. You built
a simple but usable Web browser with the THTML control, and you learned how to send
mail using the TNMSMTP control. Internet programming is big business right now. It
certainly can't hurt to have some Internet programming experience.

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, & quot;Answers
to the Quiz Questions. & quot;

Q & amp;A



Q What components or controls should I use to create a TCP/IP client/server
application?


A Use the TClientSocket and TServerSocket components.


Q Can I create Web pages from my database tables?


A Yes. The TQueryTableProducer and TDataSetTableProducer components create
an HTML document from a database table. You can learn more about these components
by examining the sample projects in the Delphi 4\Demos\Webserv directory.


Q The TNMSMTP control is for receiving mail and the TNMPOP3 control is for
retrieving mail. Why are there two controls for email operations?


A There are two controls because there are two separate mail protocols:
one for sending mail (SMTP) and one for retrieving mail (POP3).


Q When were most of the Internet protocols (SMTP, POP3, FTP, UDP, and so on)
defined?


A You might be surprised to learn that many protocols used in Internet
programming have been around for 20 years or more even though the Internet is much
younger than that. The Internet protocols were originally designed for the UNIX platform.



Quiz



1. What control do you use to display Web pages?


2. What control do you use to connect to newsgroups?


3. What is the name of the method used to display an HTML document with
the THTML control?


4. What event is generated when an HTML document has completed loading?


5. What control do you use to send email messages?


6. What control do you use to retrieve email messages?


7. What is the name of the method used to send mail with the TNMSMTP control?


8. Can you freely distribute the Internet Explorer ActiveX control?


9. What is the name of the utility used to register ActiveX controls?


10. What company provides the bulk of the Internet controls that come
with Delphi?



Exercises



1. Write an application for sending email. The application's main form
should have fields for the from address, the to address, the mail subject,
and the message text.


2. Add Forward and Back buttons to the EZ Web Browser application and
make them operational.


3. Extra Credit: Add animation to the EZ Web Browser application so that
the user can tell when the browser is loading a document.








& #169; Copyright , Macmillan Computer Publishing. All
rights reserved.