One of the most difficult aspects of learning how to use a new programming environment is finding your way around: getting to know the basic menu structure, what all the options do, and how the environment works as a whole. If you are new to programming or new to Object Pascal, this task is complicated by the fact that you have to learn a new program (the Delphi IDE) and learn a new language at the same time. It can be overwhelming at times. I'll do my best to make learning the Delphi IDE an enjoyable experience. For the most part, you will learn by example, which is more interesting (not to mention more effective).
So, without further ado, take a look at Figure 4.1 and let's get on with it. Oh, by the way, if you have used Delphi before, you might find this chapter elementary. If that is the case, you might want to at least skim the chapter lightly to catch any tidbits that you did not previously know, particularly features that are new to Delphi 4.
FIGURE 4.1. The Delphi IDE.
The Delphi IDE consists of these main parts:
I can't cover all these in a single chapter, so over the next several chapters I will show you around the Delphi IDE and examine each of these features in detail. I'll start today by discussing projects and how they are used in writing Delphi applications. After that you'll look at the Delphi toolbar and the Component palette. Then I'll move to discussing forms in greater detail than I have up to this point.
Along the way you'll create some sample programs to illustrate various aspects of Delphi. You'll also take a closer look at the Object Inspector. This will be a warm-up for Day 6, "Working with the Form Designer and the Menu Designer," when you learn all about the Delphi Form Designer. Later today, I will cover the IDE's dockable windows. Dockable windows make it easy to customize the IDE to suit your tastes and your work style.
For starters, let's look at the way Delphi views applications and how it has simplified the process of creating programs.
As you know by now, a lot goes on behind the scenes as you write a Delphi application. In fact, more goes on than I have told you about up to this point. It's not vital that you know every detail about what happens behind the scenes as you write a Delphi application, but it is a good idea to have a general overview.
New Term: A project is a collection of files that work together to create a standalone executable file or dynamic link library (DLL).
New Term: In addition to a single project, Delphi enables you to create what is known as a project group. A project group is a collection of Delphi projects.
A project group is used to manage a group of Delphi projects that work together to form a complete software product. I'll talk about project groups in more detail on Day 9, "Projects, the Code Editor, and the Code Explorer." For now, you only need to understand that Delphi creates a new, unnamed project group for you each time you start Delphi (provided you haven't turned on the option to save the desktop when you close Delphi). Any new projects you create will go into that project group. You can save the project group if you like, or you can treat the default project group as temporary.
Delphi manages a project through the use of several support files. To illustrate, let's create a simple application to get a look at some of what goes on when Delphi builds an executable file for your program. Perform the following steps:
You should see a total of about eight files. (The exact number depends on the Delphi IDE options.) First, let me tell you what happens when Delphi builds an application; then I'll explain what each of these files is for.
NOTE: Files with extensions that begin with a tilde (~) are backup files. Delphi might create several backup files depending on the number of source files in the project and the project options you have set. Project options are discussed on Day 9.
When you first create a project, Delphi creates a minimum of four files (assuming a typical Delphi GUI application):
The project source file is the file that contains the Delphi startup code. You can view the project source file by choosing Project | View Source from the main menu. The main form unit contains the class declaration and definition for the main form's class. Delphi will create an additional unit file for each new form you create. The main form resource file and project resource file are binary files that describe the main form and the application's icon.
When you tell Delphi to compile the project, it compiles the project source, the main form unit, and any other units in the project. Several things happen during this process. First, the Object Pascal compiler compiles the project's units into binary object files. Then, the resource compiler compiles any resources, such as the program's icon and form files, into binary resource files. Next, the linker takes over. The linker takes the binary files the compiler created, adds any library files the project needs, and binds them all together to produce the final executable file. When it's all over, you have a stand-alone program that can be run in the usual ways.
Okay, but what are all those files for? Table 4.1 lists the file extensions Delphi uses with a description of the role that each file type plays.
Extension | Description |
.pas | The Object Pascal source files. There will be one for each unit, as well as any other source files that you add to the project. |
.dfm | The form file. This file is actually a binary resource file (.res) in disguise. It is a description of the form and all its components. Each form has its own .dfm file. |
.dsk | The project desktop file. This file keeps track of the way the desktop appeared when you last saved (or closed) the project. All the open windows' sizes and positions are saved so that when you reopen the project it looks the same as you left it. This file is created only if you turn on the option to save your desktop (Environment Options dialog box). |
.dof | The project options file. This file contains the project options as set in the Project Options dialog. |
.exe | The final executable program. |
.cfg | The project configuration file. This file primarily contains the current compiler and linker settings for the project. |
.dcu | The compiled binary object files. These are the files that the compiler produces when it compiles your Object Pascal units. |
.dpr | The project source file. |
.res | The compiled binary resource file. |
NOTE: Delphi has other associated file extensions as well. For example, the .bpg extension is used to denote a project group, the .dpk extension is used to designate a Delphi package source file, and the .bpl extension represents a compiled package. Packages are discussed in detail on Day 8, "Creating Applications in Delphi," and project groups are discussed on Day 9.
The files that Delphi produces can be divided into two categories: files Delphi relies on to build the project and files that Delphi creates when it compiles and links a project. If you were to move your source files to another computer, for example, you wouldn't have to move all the files, just the files Delphi needs to build the application. Conveniently, the source files happen to be the smallest files in the project. It does not take a lot of disk space to back up just the project source files.
The minimum set of files consists of the .pas, .dfm, and .dpr files. All other files are files that Delphi will re-create when you compile the program. The desktop file (.dsk) is one that you might want to hang on to because it keeps track of the state your project was in when you last worked on it.
NOTE: In addition to the source files I've mentioned, some applications use a resource script file (resource scripts have an .rc extension). Resource scripts are text files that are used to define resources such as bitmaps, icons, or cursors. If you use a resource script, be sure to keep it with the project if you move the project to another location. Resource script files are not commonly used with Delphi projects.
Figure 4.2 illustrates how Delphi compiles source files and links them to form the final executable file.
FIGURE 4.2. The Delphi compile/link process.
TIP: If you find yourself running low on hard disk space, you can delete some of the Delphi files from projects you are not currently working on. It is safe to delete the files with a .dcu extension. These files will be regenerated when the project is built, and there is no use in keeping them for noncurrent projects.
CAUTION: Do not delete any files from the Delphi directories other than the Examples directory. If in doubt, don't delete!
Earlier I mentioned that most applications of any size have several source files, which are called units. Each time you create a new form, Delphi does the following:
Initially Delphi assigns a default name of Form1 to the form and Unit1.pas to the form's unit. The second form created for the project would have a default name of Form2, and so on. Each time you create a new form, Delphi creates a new unit (.pas) and form file (.dfm) for that form.
NOTE: As soon as you create a new project, you should save it with a meaningful name. Likewise, every time you create a new form, you should save it with a descriptive name. This makes it easier to locate forms and units when you need to make modifications. Remember, you can use long filenames when naming your units.
NOTE: When writing a technical book, a difficult situation often arises. I want to use meaningful examples to reinforce the presentation of information. In order to write those examples, I have to use techniques or methods that I haven't talked about yet. But I can't talk about those methods until I've given you some good, meaningful examples. But I can't...well, you see my dilemma. So I'm going to digress a little here and talk about the main menu, toolbar, and Component palette. As you read the next section, remember that I'm off on a tangent, but for a good reason.
The Delphi main menu has all the choices necessary to make Delphi work. Because programming in Delphi is a highly visual operation, you might not use the main menu as much as you might with other programming environments. Still, just about anything you need is available from the main menu if you prefer to work that way. I'm not going to go over every item on the main menu here because you will encounter each item as you work through the next several chapters.
The Delphi toolbars provide a convenient way of accomplishing often-repeated tasks. A button is easier to locate than a menu item, not to mention that it requires less mouse movement. The Delphi main window toolbars are illustrated in Figure 4.3. (The Component palette has been removed for clarity.)
FIGURE 4.3. The Delphi main window toolbars.
If you are like me, you often forget to use the toolbar. But I'm telling you: Don't forget to learn and use the toolbar! As the old saying goes, "Do as I say, not as I do." If you take the time to learn the toolbar, it will save you time and make you more efficient in the long run. One of the reasons you bought Delphi was to produce Windows applications quickly, so you might as well make the most of it.
The Delphi toolbar is fully customizable. You can place the toolbars anywhere you want on the main window. You can rearrange the placement of the menu, the toolbars, or the Component palette to suit the way you work.
Customizing the toolbars is remarkably easy. Delphi enables you to add buttons to the toolbar, remove buttons, and rearrange buttons however you see fit. To configure a toolbar, right-click on the toolbar to display the context menu. Choose Customize from the context menu. When you choose this menu item, the Customize dialog box is displayed.
The Customize dialog box contains three tabs:
FIGURE 4.4. Customizing the toolbar.
Feel free to customize the Delphi IDE any way you like. It's your development environment, so make it work for you.
The Delphi Component palette is used to select a component or other control (such as an ActiveX control) in order to place that control on a form. The Component palette is a multipage window. Tabs are provided to enable you to navigate between pages. Clicking on a tab displays the available components or controls on that page.
Placing a component on a form is a two-step process. First, go to the Component palette and select the button representing the component you want to use. Then click on the form to place the component on the form. The component appears with its upper-left corner placed where you clicked with the mouse.
You have already seen the Component palette's basic operations, but it has a couple of other features that you haven't seen yet. The following sections explain these features.
So far you have placed only one component at a time on a form. You can easily place multiple components of the same type without selecting the component from the Component palette each time. To place multiple components on a form, press and hold the Shift key as you select the component from the Component palette. After you select the component, you can release the Shift key.
The component's button on the Component palette will appear pressed and will be highlighted with a blue border. Click on the form to place the first component. Notice that the button stays pressed on the Component palette. You can click as many times as you like; a new component will be placed each time you click the form. To stop placing components, click the selector button on the Component palette (the arrow button). The component button pops up to indicate that you are done placing components.
Seeing is believing, so follow these steps:
TIP: It's fastest to place all components of a particular type on your form at one time using this technique. Components can always be rearranged and resized at a later time.
NOTE: When placing multiple copies of a particular component, it's easy to forget to click the arrow button when you're done. If you accidentally place more components than you intend, you can simply delete any extras.
Delphi provides a shortcut method of placing a component on a form. Simply double-click the component's button in the Component palette and the component will be placed on the form. The component will be centered on the form both horizontally and vertically. Components placed with this method can be moved to another location on the form just like components placed in the usual method.
NOTE: Each time you double-click a button on the Component palette, a component will be placed on the center of the form in the component's default size. If you repeatedly double-click the component button, multiple copies of the component will be placed on the form. Each component will be placed in the center of the form and will be stacked on top of the previous one. It will appear as if you have a single component, so you might not realize that you have several components occupying the same space. If you accidentally place multiple components, just click the extra components and delete them from the form.
When you place the mouse cursor over the Component palette and right-click, you will see a menu specific to the Component palette (see Figure 4.5).
FIGURE 4.5. The Component palette context menu.
The Show Hints item toggles the tooltips on and off for the component buttons. Unless you really dislike tooltips, this should be left on. The Hide item on the context menu hides the Component palette. In order to show the Component palette again, you have to choose Component Palette from the toolbar context menu.
The Help item on the context menu brings up Delphi help with the Component Palette page displayed. The Properties item brings up the Palette page of the Environment Options dialog box, where you can customize the Component palette. Here you can add and remove pages of the Component palette. You can also add, remove, or rearrange the order of components on the individual pages. I'll discuss this in more detail on Day 11, "Delphi Tools and Options," when you learn about setting the environment options.
When the Component palette is sized small enough so that it cannot display all its tabs, you will see scroll buttons in the upper-right corner of the Component palette. Click these scroll buttons to display tabs not currently in view. Likewise, if a particular page of the Component palette contains more buttons than will fit the width of the display window, scroll buttons will be enabled to allow you to scroll through the available buttons. Figure 4.6 shows the Component palette with both types of scroll buttons enabled.
FIGURE 4.6. The Component palette scroll buttons.
The Component palette is not terribly complicated, but a basic understanding of its use is vital for programming with Delphi. Now that you've finished with this quick overview of the Delphi main window, you can return to the main topic again.
To illustrate how Delphi uses units, you can create an application with multiple forms. You'll create a simple application that displays a second form when you click a button:
FIGURE 4.7. The form up to this point.
procedure TMainForm.ShowFrom2Click(Sender: TObject); begin SecondForm.ShowModal;end;
At this point you get a message box that says, Form `MainForm' references form `SecondForm' declared in unit `second' which is not in your USES list. Do you wish to add it? Click Yes and Delphi will add the unit Second to the uses list of the unit Main. Click the Run button again and this time the application will run. When you click the Show Form 2 button on the main form, the second form is displayed. You can close the second form by clicking the system close box on the form's title bar.
Rather than having Delphi prompt you to add a unit to your uses list, you can add units yourself. You can manually type the unit name in the uses list for the form, or you can choose File | Use Unit from the main menu. When you choose the latter method, the Use Unit dialog box is displayed, as shown in Figure 4.8. The Use Unit dialog box shows a list of available units. Choose the unit you want to add and click OK. Delphi will add the unit to the current form's uses list. Note that the Use Unit dialog box will show only those units that exist in the project and have not yet been included in this unit. Units that have already been included do not show up in the list of available units.
FIGURE 4.8. The Use Unit dialog box.
As you can see, Delphi does a good job of managing units for you. Later, when your programming needs are more sophisticated, you'll have to do a little more source file management, but at this stage of the game Delphi does most of the work for you.
Now let's take a moment to look at the different compiling options available to you when writing programs in Delphi.
Each time you click the Run button, Delphi compiles and links your program. But it doesn't necessarily compile every unit in the project. It only compiles any units that have changed since the last compile. This feature saves you time because you don't have to wait for the compiler to compile files that haven't changed. Delphi keeps track of which files have changed and which haven't, so you don't need to do anything special to use this feature--it's automatic.
Most of the time you want to see the results of any changes you have made in action. In those cases, click the Run button and the program is compiled, linked, and executed. Sometimes, however, you don't want to run the program. For example, you might just want to compile the program to see whether there are any errors.
Delphi has three menu items in addition to Run that enable you to control the compile/link process. If you choose the Project menu item on the main menu, you see three menu items called Compile, Build, and Syntax Check. These menu items' text changes to reflect the name of the active project. For example, when you first start Delphi, these menu items will say Compile Project1, Build Project1, and Syntax Check Project 1. (There are also menu items called Compile All Projects and Build All Projects, but I'll save that discussion for Day 9 when I discuss project groups.) Let's take these in order of simplest to most complex (from the compiler's perspective):
TIP: The keyboard shortcut for the Compile option is Ctrl+F9.
So far, you have been letting Delphi add units to your projects. Further on down the road you might have to do some hand-editing of your source files to add units or compiler directives. You might even end up editing the project source. From time to time things can get goofed up (hey, we all make mistakes). Performing a build will bring everything up-to-date so that you can better sort out any problems you might be running into. Sometimes a build resolves compiler and linker errors without the need for you to do anything further.
TIP: Any time you get unexpected (out of the ordinary) compiler or linker errors, first try a build. It could just be that something is out of sync, and a build might cure it. If performing a build doesn't fix the problem, you'll have to go to work figuring out where the problem lies.
Delphi gives you the option of displaying a compile status dialog box during compiles. You can turn on this option through the Environment Options dialog box (Preferences page). When this option is on, the compile status dialog box shows the filename of each unit as the unit is being compiled. If there are errors, the compile status dialog box will report There are errors and will list the number of errors that were detected as well as any warnings.
Figure 4.9 shows the compile status dialog box after detecting errors. Delphi compiles your projects so quickly that the compile status dialog box is not generally necessary. In fact, the compile status dialog box will increase your compile times because it takes time to display information in the compile status dialog box.
FIGURE 4.9. The compile status dialog box reporting errors.
Regardless of the method chosen to compile the project, if errors are detected, the Code Editor will come to the top and the message window at the bottom of the Code Editor will show a list of errors and warnings. The Code Editor highlights the line where the first error occurred. After a successful syntax check, compile, or build, you can immediately run the program via the Run button if you choose.
Delphi's strength is in its visual programming environment. That environment is tied directly to VCL and cannot be separated from it. To get the most out of Delphi, you will most likely be writing applications based on VCL. There are times, however, when you might want to write other types of applications.
New Term: A dynamic link library (DLL) is an external file that contains code that can be executed from a program or from another DLL.
Probably the most obvious type of "other" program you might want to build is a DLL. DLLs might seem a bit like black magic, but they are really not very complicated: They are simply bits of compiled code that you can call from your application. After you create the DLL, calling a function contained in a DLL is no different than calling a function contained in your main program. That's a bit of an oversimplification, but it is still accurate. DLLs are discussed on Day 19, "Creating and Using DLLs."
Another type of application you might write with Delphi is a Win32 console application.
New Term: A Win32 console application is a 32-bit program that runs in a DOS box under Windows 95 or Windows NT.
Console applications are useful for small utility programs, servers such as TCP/IP servers or mail servers, and a whole host of other possibilities. Basically, any application that does not require a graphical interface is a good candidate for a console application.
Before I continue with the discussion about the Delphi IDE, I need to spend some time explaining forms. You have seen several forms in action as you have worked through this book, and on Day 6 you are going to learn all about the Form Designer. Before you get there, you need some more background information on forms, so I'll cover that now.
Forms are the main building block of a Delphi application. Every GUI application has at least one form that serves as the main window. The main window form might be just a blank window, it might have controls on it, or it might have a bitmap displayed on it. In a typical Windows program, your main window would have a menu. It might also have decorations such as a toolbar or a status bar. Just about anything goes when creating the main window of your application. Each application is unique, and each has different requirements.
Forms are also used for dialog boxes. In fact, to the user there is no difference between a Delphi form acting as a dialog box and a true dialog box. (By "true dialog box" I mean a dialog box created the traditional way with a resource editor and a resource script file. This is how dialog boxes are created in some other programming environments. Delphi doesn't use traditional dialog boxes, so you will likely never have to deal with dialog boxes on this level.) Dialog boxes usually have several traits that distinguish them from ordinary windows:
There are certainly exceptions to every rule. Most dialog boxes have the usual characteristics, but some dialog boxes perform specialty tasks and as such depart from the norm in one way or another.
In a traditional Windows program (one written in Borland Pascal or using a framework such as OWL), a dialog box is created with a dialog box editor. In most cases, the dialog box editor is a visual tool that works somewhat like the Delphi Form Designer. When the user is done designing the dialog box, the visual representation of the dialog box is converted into a dialog box definition in a resource script file. To illustrate, take a look at the dialog box shown in Figure 4.10.
New Term: A resource script is a text file that is later compiled into a binary resource file by the resource compiler.
FIGURE 4.10. A typical About dialog box.
Figure 4.10 shows a typical About dialog box. It contains the program name, the copyright information, and the application's icon. The resource script definition for the dialog box is shown in Listing 4.1.
1: IDD_ABOUT DIALOG 58, 53, 194, 119 2: STYLE DS_MODALFRAME or WS_POPUP | 3: WS_VISIBLE or WS_CAPTION or WS_SYSMENU 4: CAPTION `About TMMPlayer Example Program' 5: FONT 8, `MS Sans Serif' 6: { 7: DEFPUSHBUTTON `OK', IDOK, 72, 96, 50, 14 8: CTEXT `TMMPlayer Example Program', -1, 48, 22, 128, 8 9: CTEXT `Copyright © 1996, by Kent Reisdorph', -1, 32, 47, 136, 8 10: CTEXT `March 15, 1996', -1, 24, 59, 146, 8 11: CONTROL `', 99, `button', BS_GROUPBOX | 12: WS_CHILD or WS_VISIBLE or WS_GROUP, 12, 4, 176, 70 13: CONTROL 1, 1, `static', SS_ICON | 14: SS_SUNKEN or WS_CHILD or WS_VISIBLE, 24, 17, 20, 20
15: }
The resource script contains information that Windows uses to build the dialog box at runtime. This information includes the number and type of controls on the dialog box, their size, position, text, options, and so on. Of course, the resource script also includes the same type of information for the actual dialog box.
Some Windows programmers don't use a dialog box editor at all, but prefer to write the dialog box definition from scratch with a text editor. Although I can't fault those programmers for creating dialog boxes in that manner, I can say that for most programmers to take that approach would be, er..., less than 100 percent efficient. It takes many times longer to create a dialog box in that manner as opposed to the visual approach.
Usually, all the application's dialog box definitions are contained in a single resource script file that has a filename extension of .rc. At some point in the program-creation process, the resource script is compiled into a .res file (the binary resource file), which then is linked to the .exe by the linker. At runtime the dialog box is displayed either modally or modelessly depending on the dialog box's intended purpose. When the dialog box is invoked, Windows loads the dialog box resource from the executable file, builds the dialog box, and displays it.
In Delphi, dialog boxes are simply another form. You create a dialog box just like you do a main window form or any other form. To prevent the dialog box from being sized, you can change the BorderStyle property to bsDialog or bsSingle. If you use bsDialog, your dialog box will have only the close box button on the title bar, which is traditional for dialog boxes. Other than that, you don't have to do anything special to get a form to behave like a dialog box. All Delphi forms have tabbing support built in. You can set the tab order by altering the TabOrder property of the individual controls on the dialog box.
New Term: A modal dialog box is one that must be dismissed before the user can continue using the application. The main window of an application is disabled while this type of dialog box is open. Most dialog boxes are modal.
New Term: A modeless dialog box is one that allows the user to continue to work with the application while the dialog box is displayed. The Find dialog box in some word-processing programs is an example of a modeless dialog box.
A Delphi dialog box (any Delphi form, actually) is modal or modeless depending on how it is displayed. To execute a modal dialog box, you call the ShowModal method of TForm. To create a modeless dialog box, you call the Show method.
You can now add an About box to the multiple-forms project you created earlier. If you don't have that project open, choose File | Open Project from the main menu or click the Open Project button on the toolbar and locate the file. (You should have saved it with the project name of Multiple.)
TIP: Delphi keeps a list of the files and projects you have used most recently. Choose File | Reopen to view the MRU (most recently used) list. The MRU list is divided into two parts. The top part shows the projects you have used most recently, and the bottom part shows the individual files that you have used most recently. Just click on one of the items to reopen that project or file.
First you'll add a button to the form that displays the About dialog box:
AboutBox.ShowModal;
You haven't actually created the About box yet, but when you do you'll name it AboutBox, so you know enough to type the code that will display the About box.
Now create the dialog box itself by following these steps:
FIGURE 4.11. The About box with text labels added.
TIP: The copyright symbol (") has an ASCII value of 169 in most typefaces. To create the copyright symbol, press and hold the Alt key and type the numbers 0169 on the numeric keypad (be sure Num Lock is on). When you let go of the Alt key, the copyright symbol appears. You can insert the ASCII value of any character this way. You must type all four numbers, though. For example, the ASCII value of a capital A is 65. To insert an A, you have to hold down Alt and type 0065 on the numeric keypad.
Next, you can add an icon to the About box:
At this point you need an OK button on the form. Let's branch out a little and take a look at a new component:
Let's add one final touch to the About box:
Your form should now look something like the one shown in Figure 4.12. Save the unit (File | Save) and give it the name About.
FIGURE 4.12. The finished About box.
Are you ready to compile and run the program? Not yet. You need to add the About unit to the main form's uses list. Perform these steps:
Now you're ready to run the program, so click the Run button. When the program runs, click the About button, and the About dialog box is displayed. Note that the dialog box is modal (you can't go back to the main window while the dialog box is displayed) and that it cannot be sized. The About form behaves in every way like a regular Windows dialog box.
NOTE: The common dialog box classes (TOpenDialog, TSaveDialog, TFontDialog, and so on) do not represent dialog boxes created as Delphi forms. Windows provides these dialog boxes as a set of common dialog boxes that all Windows applications can use (the actual dialog boxes are contained in a file called COMDLG32.DLL). The VCL dialog box classes encapsulate the common dialog boxes to make using them easier. Encapsulate in this sense means to take a common dialog box's functionality and roll it into a VCL component. The component makes it much easier to use the dialog box than it would otherwise be.
NOTE: Delphi includes several prebuilt forms that you can choose from to help you build dialog boxes as quickly as possible. I'll discuss those on Day 8.
A secondary window is a form that you display from your main window. So when is a form a secondary window and when it is a dialog box? When it really comes down to it, there is no difference between a secondary window and a dialog box in Delphi. You might have windows that resemble dialog boxes, and you might have other windows that resemble a traditional window.
In the grand scheme of things, they all are forms and it doesn't make much sense to differentiate between the terms dialog box and secondary form. It's all the same in the end. In traditional programming environments, you have to specifically create a dialog box or specifically create a secondary window in an application. Delphi frees you from that restriction and enables you to treat both dialog boxes and windows exactly the same.
So far, you have built only single document interface (SDI) applications. An SDI application has a single main window and typically displays dialog boxes as needed, but does not otherwise display child windows.
New Term: Some programs follow the multiple document interface (MDI) model. MDI applications consist of a main window (the MDI parent) and child windows (the MDI children).
Examples of programs that use the MDI model are the Windows System Configuration Editor (SYSEDIT) and the Windows 3.1 Program Manager.
One of the most obvious characteristics of the MDI model is that the MDI child windows are confined to the parent. You can drag the child windows within the parent window, but you cannot drag them outside the parent. MDI applications almost always have a Window item on their main menu. This menu usually contains items named Cascade and Tile, which enable you to display the MDI child windows in either a cascaded or tiled arrangement. When an MDI child is minimized, its icon is contained within the MDI parent's frame. When a regular (non-MDI) child window is minimized, its icon is placed on the Windows desktop.
To create an MDI application in Delphi, you must set the main form's FormStyle property to fsMDIForm. Each of the MDI child windows must have the FormStyle property set to fsMDIChild. Aside from that restriction, there is very little to creating an MDI application in Delphi. You simply create the main window form and one or more forms to be used as child windows, and you're off and running.
The TForm class has a lot of properties. Some of these properties are obscure and rarely used; others are widely used. I'll touch on the most widely used properties here. I won't include obvious properties such as Caption, Color, Left, Top, Width, and Height unless they have a particular feature you should be aware of.
The properties outlined in this section can be set at design time and also at runtime. Almost all these properties can be read at runtime as well.
ActiveControl The ActiveControl property is used to set the control that will have focus when the form is activated. For example, you might want a particular edit control to have focus when a dialog box form is displayed. At design time, the Value column for the ActiveControl property contains a list of components on the form. You can choose one of the components from this list to make that component the active control when the form is first displayed.
AutoScroll, HorzScrollBar, and VertScrollBar Together, the AutoScroll, HorzScrollBar, and VertScrollBar properties control the scrollbars for a form. If AutoScroll is set to True (the default), scrollbars automatically appear when the form is too small to display all its components. The HorzScrollBar and VertScrollBar properties each have several properties of their own that control the scrollbar operations.
BorderIcons The BorderIcons property controls which system buttons will appear on the form at runtime. Choices include the system menu, the minimize button, the maximize button, and the help button.
BorderStyle The BorderStyle property indicates what type of border the form will have. The default value is bsSizeable, which creates a window that can be sized. Non-sizable styles include bsDialog and bsNone.
ClientWidth and ClientHeight You can specify the client area width and height rather than the full form's width and height by using the ClientWidth and ClientHeight properties. (The client area of the form is the area inside of the borders and below the title bar and menu bar.) Use these properties when you want the client area to be a specific size and the rest of the window to adjust as necessary. Setting the ClientWidth and ClientHeight properties makes automatic changes to the Width and Height properties.
Constraints The Constraints property is used to set the maximum and minimum width and height of the form. Simply set the MaxHeight, MaxWidth, MinHeight, and MinWidth values as desired and the form will conform to those constraints.
DefaultMonitor The DefaultMonitor property determines which monitor the form will appear on in a multimonitor environment (such as Windows 98).
DockSite The DockSite property determines whether the form will act as a dock site for dockable components. Dock sites and dockable components are discussed on Day 13, "Beyond the Basics."
Font The Font property specifies the font that the form uses. The important issue to understand here is that the form's font is inherited by any components placed on the form. This also means that you can change the font used by all components at one time by changing just the form's font. If an individual control's font has been manually changed, that control's font will not be changed when the main form's font changes.
FormStyle This property is usually set to fsNormal. If you want a form to always be on top, use the fsStayOnTop style. MDI forms should use the fsMDIForm style and MDI child forms should use the fsMDIChild style. MDI forms and MDI child windows were discussed earlier in this chapter in the section "The Multiple Document Interface Model."
HelpContext and HelpFile The HelpContext property is used to set the help context ID for a form. If context help is enabled for a form, the Windows Help system will activate when the F1 key is pressed. The context ID is used to tell the Help system which page in the help file to display. The HelpFile property is the name of the help file that will be used when F1 is pressed.
Icon The Icon property sets the icon that is used on the title bar for the form when the form is displayed at runtime and also when the form is minimized. In some cases, setting this property has no effect. For example, when the FormStyle is set to fsDialog, the Icon property is ignored.
KeyPreview When KeyPreview is True, the form's OnKeyPress and OnKeyDown events will be generated when a key is pressed in any component on the form. Ordinarily, forms don't receive keyboard events when a component on the form has focus.
Position The Position property determines the size and position of the form when the form is initially displayed. The three basic choices are poDesigned, poDefault, and poScreenCenter:
Visible The Visible property controls whether the form is initially visible. This property is not particularly useful at design time, but at runtime it can be read to determine whether the form is currently visible. It can also be used to hide or display the form.
WindowState The WindowState property can be read to determine the form's current state (maximized, minimized, or normal). It can also be used to indicate how the form should initially be displayed. Choices are wsMinimized, wsMaximized, and wsNormal.
Some properties can be accessed only at runtime through code. The following are the most commonly used runtime properties.
ActiveMDIChild When read, the ActiveMDIChild property returns a pointer to the currently active MDI child window. This property is read only. If no MDI child is currently active or if the application is not an MDI application, ActiveMDIChild returns nil.
Canvas The form's canvas represents the drawing surface of the form. The Canvas property gives you access to the form's canvas. By using the Canvas property, you can draw bitmaps, lines, shapes, or text on the form at runtime. Most of the time you will use a Label component to draw text on a form, an Image component to display graphics, and a Shape component to draw shapes. However, there are times when you need to draw on the canvas at runtime and the Canvas property enables you to do that. The Canvas property can also be used to save an image of the form to disk. Canvases are discussed in more detail on Day 12, "Graphics and Multimedia Programming."
ClientRect The ClientRect property contains the top, left, right, and bottom coordinates of the client area of the form. This is useful in a variety of programming situations. For example, you might need to know the client area's width and height in order to place a bitmap on the center of the form.
Handle The Handle property returns the window handle (HWND) of the form. Use this property when you need the window handle to pass to a Windows API function.
ModalResult The ModalResult property is used to indicate how a modal form was closed. If you have a dialog box that has OK and Cancel buttons, you can set ModalResult to mrOK when the user clicks the OK button and to mrCancel when the user clicks the Cancel button. The calling form can then read ModalResult to see which button was clicked to close the form. Other possibilities include mrYes, mrNo, and mrAbort.
Owner The Owner property is a pointer to the owner of the form. The owner of the form is the object that is responsible for deleting the form when the form is no longer needed. The parent of a component, on the other hand, is the window (a form or another component) that acts as the container for the component. In the case of a main form, the application object is both the owner of the form and the parent of the form. In the case of components, the owner would be the form, but the parent could be another component such as a panel.
Parent The Parent property is a pointer to the parent of the form. See the previous section about Owner for an explanation of Owner versus Parent.
Forms are components, too. As such, forms have many methods in common with components. Common methods include Show, ShowModal, and Invalidate, to name just a few. There are some methods, however, that are specific to forms. As before, I'll discuss only the most commonly used methods.
The BringToFront method causes the form to be brought to the top of all other forms in the application.
The Close method closes a form after first calling CloseQuery to ensure that it's okay to close the form. The CloseQuery function in turn calls the OnCloseQuery event handler. If the Boolean variable passed to the OnCloseQuery handler is set to False, the form is not closed. If it is set to True, the form closes normally. You can use the OnCloseQuery event handler to prompt the user to save a file that needs saving and to control whether a form can close.
The Print method prints the contents of the form. Only the client area of the form is printed, not the caption, title bar, or borders. Print is handy for quick screen dumps of a form.
The ScrollInView method scrolls the form so that the specified component is visible on the form.
The SetFocus method activates the form and brings it to the top. If the form has components, the component specified in the ActiveControl property will receive input focus (see the ActiveControl property in the section "Design-Time Properties").
The Show and ShowModal methods display the form. The Show method displays the form as modeless, so other forms can be activated while the form is visible. The ShowModal method executes the form modally. Recall that a modal form must be dismissed before the user can continue to use the application.
Several form methods deal specifically with MDI operations. The ArrangeIcons method arranges the icons of any minimized MDI children in an MDI parent window. The Cascade method cascades all nonminimized MDI child windows. The Tile method tiles all open MDI child windows. The Next method activates (brings to the top) the next MDI child in the child list, and the Previous method activates the previous MDI child in the child list. The MDI methods apply only to MDI parent windows.
Forms can respond to a wide variety of events. Some of the most commonly used are listed in the following sections.
The OnActivate event occurs when the form is initially activated. The form might be activated as a result of its initial creation or when the user switches from one form to another. The Application object also has an OnActivate event that is generated when the user switches from another application to your application.
When an application is closed, the OnClose event is sent. OnClose calls the OnCloseQuery event to see whether it is okay to close the form. If the OnCloseQuery event returns False, the form is not closed.
The OnCreate event occurs when the form is initially created. Only one OnCreate event will occur for any instance of a particular form. Use the OnCreate handler to perform any startup tasks that the form needs in order to operate.
The OnDestroy event is the opposite of OnCreate. Use this event to clean up any memory a form allocates dynamically or to do other cleanup chores.
The OnDragDrop event occurs when an object is dropped on the form. Respond to this event if your form supports drag and drop.
Respond to the OnMouseDown, OnMouseMove, and OnMouseUp events in order to respond to mouse clicks and mouse movements on a form.
The OnPaint event occurs whenever the form needs repainting, which could happen for a variety of reasons. Respond to this event to do any painting that your application needs to display at all times. In most cases, individual components will take care of painting themselves, but in some cases you might need to draw on the form itself.
The OnResize event is sent every time the form is resized. You might need to respond to this event to adjust components on the form or to repaint the form.
The OnShow event occurs just before the form becomes visible. You can use this event to perform any processing that your form needs to do just before it is shown.
NOTE: When a form is created, many events are generated. Likewise, when a form is destroyed, several events are generated. But in what order are these events generated? When a form is created, the following events occur in this order (the constructor and AfterConstruction virtual methods are listed in addition to the events):
The form's constructor
OnCreate event
AfterConstruction method
OnShow event
OnActivate event
When a form is destroyed, the following events are generated in this order:
OnCloseQuery event
OnClose event
BeforeDestruction method
OnDestroy event
The form's destructor
In most applications, keeping the order straight generally is not important. In some cases, however, it can be critical. Knowing the order in which the event handlers, the constructor, and the destructor are called can save you some frustration when you really need to know.
An integral part of the Delphi IDE is the Object Inspector. This window works with the Form Designer to aid in the creation of components. I'm going to discuss the Form Designer on Day 6, but before I do I want to talk a little about the Object Inspector.
The Object Inspector is where you set the design-time properties that affect how the component acts at runtime. The Object Inspector has three main areas:
You have been using the Object Inspector quite a bit up to this point, so I'll review what you already know and show you a few features you probably don't know about.
Normally, you select a component by clicking the component on a form. The Component Selector provides an alternative way of selecting a component to view or modify. The Component Selector is a drop-down combo box that is located at the top of the Object Inspector window.
NOTE: Usually the quickest way to select a component is by clicking the component on the form. Choosing the component from the Component Selector is convenient if the component you are looking for is hidden beneath another component or is off the visible area of the form.
The Component Selector displays the name of the component and the class from which it is derived. For example, a memo component named Memo would appear in the Component Selector as
Memo: TMemo
The class name does not show up in the drop-down list of components; it only appears in the top portion of the Component Selector. To select a component, click the drop-down button to reveal the list of components and then click the one you want to select.
NOTE: The Component Selector shows only the components available on the current form and the name of the form itself. Other forms and their components will not be displayed until they are made active in the Form Designer.
After you select a component in the Component Selector, the component is selected on the form as well. The Properties and Events tabs change to display the properties and events for the selected component. (Remember that a form is a component, too.) Figure 4.13 shows the Object Inspector with the Component Selector list displayed.
FIGURE 4.13. The Component Selector list.
The Properties page of the Object Inspector displays all the design-time properties for the currently selected control. The Properties page has two columns: The Property column is on the left side of the Properties page and shows the property name; the Value column is on the right side of the Properties page and is where you type or select the value for the property.
If the selected component has more properties than will fit in the Object Inspector, a scrollbar will be provided so that you can scroll up or down to locate other properties.
NOTE: If you have multiple components selected on the form, the Object Inspector shows all the properties that those components have in common. You can use this feature to modify the properties of several components at one time. For example, to change the width of several components at one time, you can select all the components and then modify the Width property in the Object Inspector. When you press Enter or move to another property, all the components you selected will have their Width property modified.
Figure 4.14 shows the Object Inspector when a Memo component is selected.
FIGURE 4.14. The Object Inspector showing Memo component properties.
Properties can be integer values, enumerations, sets, other objects, strings, and other types. (Properties are discussed in detail tomorrow.) The Object Inspector deals with each type of property according to the data type of the property. Delphi has several built-in property editors to handle data input for the property. For example, the Top property accepts an Integer value. Because the Integer type is a basic data type, no special handling is required, so the property editor is fairly basic. The property editor for this type of property enables you to type a value directly in the Value column for integer properties such as Top, Left, Width, and Height.
NOTE: In most cases, the property editor does parameter checking for any properties in which you can enter an integer value. The Width property, for example, cannot be a negative number. If you attempt to enter a negative number for the Width of a control, Delphi will force the width to the minimum allowed for that control (usually 0). If you enter a string value for a property that expects an integer value, Delphi will display an error message. It is the job of the property editor to do parameter checking.
In many cases, the property editor for the property contains a list of items from which you can choose. Properties that have an enumeration or Boolean value as their base data type fall into this category. When you click the Value column with this type of property editor, you will see a drop-down button on the right side of the Value column. Clicking this button displays the list of possible values.
TIP: If you double-click the Value column for this type of property, the property editor will cycle through the possible choices. To quickly change a Boolean property, for example, simply double-click its value. Because the only choices are True and False, double-clicking the value has the effect of toggling the property's value.
If you look closely at the Object Inspector, you will see that some properties have a plus sign preceding their names. Properties that are sets and properties that are classes both have the plus sign in front of their names. The plus sign indicates that the property node can be expanded to show the set or, in the case of properties that are classes, the properties of that class. To expand a node, double-click on the Property column for that property (on the property name) or choose Expand from the Object Inspector context menu. To collapse the node, double-click it again or choose Collapse from the Object Inspector context menu.
To see an example of a set property, choose a form and then double-click the BorderIcons property in the Object Inspector. The node expands and you see four members of the set. You can turn on or off any of the four members as needed.
In the case of properties that are objects (instances of a VCL class), you have two choices in editing the property. First, you can click the Value column for the property and then click the button to the right side of the value (if one exists). This button is indicated by an ellipsis (...) on its face. Clicking this button invokes the property editor for that particular control. For example, click the Font property and then click the ellipsis button. The Choose Font dialog box is displayed so that you can select the font.
The second way you can edit this type of property is by expanding the property node. The property's properties (yes, it's true) will be displayed, and you can edit them just like any other property. Again, locate the Font property and double-click it. The TFont properties will be displayed. You can now modify the font's Height, Color, Name, and other properties.
Some properties have only the ellipsis button as a means of editing the property. Earlier you used the Image component to select an icon for the Multiple program's About box. As you found out then, the Image component's Picture property can be changed only by invoking that property's property editor. In that case, the property editor is the Delphi Picture Editor.
Rest assured that each property knows what it needs to do to present you with the correct property editor. You will see different types of property editors as you are introduced to new components and new properties.
The Events page lists all the events that the component is designed to handle. Using the Events page is pretty basic. In order to create an event handler for an event, you simply double-click in the Value column next to the event you want to handle. When you do, Delphi creates an event-handling function for you with all the parameters needed to handle that event. The Code Editor is displayed and the cursor is placed in the event handler. All you have to do is start typing code. The name of the function is generated based on the Name property of the component and the event being handled. If, for example, you have a button named OKBtn and are handling the OnClick event, the function name generated would be OKBtnClick.
You can let Delphi generate the name of the event-handling function for you or you can provide the function name for Delphi to use. To provide the function name yourself, type the name in the Value column next to the event and press Enter. The Code Editor is displayed, and so is the event-handling function, complete with the name you supplied.
NOTE: Delphi will remove any empty event handlers when you run, compile, or save a unit. For example, let's say you created an event handler for the OnCreate event but didn't type any code. The next time you run, compile, or save the unit, Delphi will remove the event handler you just created because it doesn't contain any code. This is the way Delphi is designed and it makes perfect sense, but it can be a bit puzzling if you aren't aware of what is going on. If you don't want Delphi to remove the event handler, either type code right away or type a comment line so that the event handler won't be removed.
After you create an event-handling function for a component, you can use that event handler for any component that handles the same event. Sometimes it's convenient to have several buttons use the same OnClick event, for example. To take it a step further, you might have a main menu item, a pop-up menu item, and a toolbar button all use the same OnClick handler. You will learn to appreciate this kind of code reuse as you gain experience with Delphi. Even though you are dealing with three different components, they can still share a common OnClick handler. The Value column of the Events page contains a drop-down button that can be used to display a list of all event handlers compatible with the current event. All you have to do is choose an event from the list.
A new feature in Delphi 4 is dockable windows.
New Term: A dockable window is a window that can be dragged from its current location (using the mouse) and docked to one of the IDE's dock sites.
New Term: A dock site is a specific location in the IDE where a dockable window can be docked. The IDE has several dock sites.
Just about every window in Delphi is dockable. This includes the Project Manager, the Code Explorer, the Object Inspector, the Watch List window, the Message window, and on and on. In fact, there are very few windows in Delphi that are not dockable.
Am I enamored with dockable windows just for the sake of dockable windows alone? Not in the least. I don't even bother with dockable windows in most Windows programs I own. In the Delphi IDE, however, dockable windows make me more productive and that's why I like them.
You can't talk about dockable windows without talking about dock sites. Sure, you can undock a window and drag it around on the screen, dropping it wherever you want. That just makes for a bunch of scattered windows all over your screen, though. In order for dockable windows to make sense, you have to have a place to dock them. In the Delphi IDE that usually means the Code Editor window.
The Code Editor has three dock sites. One dock site is along the left side of the Code Editor window. This dock site is where the Code Explorer is docked when you first start Delphi. Another dock site is along the bottom of the Code Editor window. The default Delphi configuration places the Message window in the bottom dock site (although you don't see the Message window unless there are messages to display). The third Code Editor dock site is along the left edge of the Code Editor window. These three dock sites are really all you need to fully customize the IDE.
There is one other type of dock site that I want to mention. If you have a tool window open (such as the Project Manager), you can dock another tool window to it. This enables two or more Delphi windows to be hosted within the same tool window. For example, the Code Explorer and Project Manager can both be docked in the same floating tool window. A floating tool window has five dock sites: right, left, top, bottom, and center.
When you dock a window to the center of a tool window, the tool window becomes a tabbed window. Each tab in the window contains the title of the window that tab represents. I realize that doesn't make a lot of sense, so in just a bit I'll show you how to dock two tool windows together.
Rather than trying to put the relationships between the various windows into words, I think an exercise is in order. I'll start with the most basic docking operations and move to the more complex. This exercise doesn't take long and should prove very enlightening. Here goes:
This exercise is simple but gives you an idea of what you can do with dockable windows at the most basic level. The next exercise is a little more interesting. Perform these steps:
FIGURE 4.15. The Code Explorer and Project Manager docked together in a tool window.
Are you starting to get the picture? You can have as many tool windows as you want in one tabbed window. Let's do one more exercise along the same lines. Often you want the debugger Watch List window in view while you are debugging (debugging is discussed on Day 10, "Debugging Your Applications"). Let me show you how you can keep the Watch List window handy at all times. Perform these steps:
Now you can click on the Watch List tab at the bottom of the Code Editor anytime you want to view your watches. The Message window takes care of itself, as it will come and go in the tabbed window any time there are messages to display. Figure 4.16 shows the IDE after performing the last exercise.
FIGURE 4.16. Four tool windows docked to the Code Editor.
Sometimes you don't want a particular window to be dockable. As nice as dockable windows are, sometimes it's hard to find a location to place a window when you don't want it docked. It seems like anywhere you try to put the window it wants to dock to something. The good news is that you can shut off the docking capability of any tool window.
Each of the dockable tool windows has a menu item at the bottom of its context menu called Dockable. If Dockable is checked, the window is dockable. If Dockable is not checked, the window is not dockable and you can place it anywhere in the IDE.
The dockable windows in the Delphi IDE are a great feature. You can arrange the tool windows you use most often in any way you want. You no longer have to go hunting for a Project Manager, Watch List, or Object Inspector hidden under other windows. The window you are looking for is just a couple of mouse clicks away.
To help solidify today's discussion of projects and forms, let's create an MDI application. This application will enable you to open and save graphics files such as bitmaps, icons, and metafiles. In order to complete the task, you should have a master plan. Here's what you need to do:
There's no point in dawdling (time is money!), so let's get right to it.
First you'll create the main window form. The main window for an MDI application must have the FormStyle property set to fsMDIForm. You also need to add a menu to the application, as well as File Open and File Save dialog boxes. Follow these steps:
Okay, now you've got the main part of the form done. Next you'll add a menu to the form. Because I haven't discussed the Menu Designer yet, you will take the easy route to creating a menu. To do that, you can take advantage of a Delphi feature that enables you to import a predefined menu, as follows:
FIGURE 4.17. The Menu Designer with the Insert Template dialog box open.
Now you should be back to the main form. Notice that you have a menu on the form. You can click on the top-level items to see the full menu. Don't click on any menu subitems at this point--you'll do that in a minute. Notice that there are a lot of menu items. You won't need all them, but for now you can just leave the extra items where they are.
Now you need to prepare the File Open and File Save dialog boxes:
Your form should now look like the one shown in Figure 4.18.
FIGURE 4.18. The form up to this point.
Now you are ready to write the code to implement the File | Open and File | Save As menu items. Delphi provides a slick way of writing menu handlers with a minimum amount of fuss. You haven't created the MDI child form yet, but you know enough about it to write the code for the menu handlers. Keep in mind that the application won't compile until you create the MDI child form. Here you go:
procedure TMainForm.Open1Click(Sender: TObject);
var Child : TChild; begin if OpenPictureDialog.Execute then begin Child := TChild.Create(Self); with Child.Image.Picture do begin LoadFromFile(OpenPictureDialog.FileName); Child.ClientWidth := Width; Child.ClientHeight := Height; end; Child.Caption := ExtractFileName(OpenPictureDialog.FileName); Child.Show; end;
end;
NOTE: In the Open1Click method the ExtractFileName function is used to extract just the filename from the path and filename contained in the FileName property of the OpenPictureDialog component. Related functions include ExtractFilePath, ExtractFileDir, ExtractFileDrive, and ExtractFileExt.
NOTE: Remember yesterday's discussion about calling Free for all objects created dynamically? Notice that I appear to be violating that rule in the preceding code. In reality I am not, because VCL will take the responsibility of freeing the memory allocated for the MDI child windows. Notice that the single parameter in the TChild constructor is Self. That tells VCL that the Owner of the MDI child is the MDI form window. When the MDI form is destroyed (when the application closes), it will be sure to delete all its MDI child objects.
procedure TMainForm.SaveAs1Click(Sender: TObject); begin if SavePictureDialog.Execute then with ActiveMDIChild as TChild do Image.Picture.SaveToFile(SavePictureDialog.FileName);end;
The code for the File | Save As menu item is simple. The first two lines check to see whether an MDI child window is active. If so, the File Save dialog box is displayed. If the user clicks OK, the image is saved to disk using the TPicture class's SaveToFile method.
NOTE: In the preceding code you see the as operator in use. The ActiveMDIChild property returns a pointer to a TForm object. What you actually need in this case is a pointer to a TChild object (your MDI child class, derived from TForm) so that you can access the Image property of the MDI child form. The as operator casts the ActiveMDIChild variable to a TChild pointer. If for some reason as is unable to perform the cast, the code block following the as statement is ignored.
Before going on, it is a good idea to save the project. Choose File | Save All from the main menu. Save Unit1 (the default name Delphi assigns to a new unit) as PctViewU and the project as PictView.
Now you can add code to the Window menu. This part is simple:
procedure TMainForm.Tile1Click(Sender: TObject); begin Tile;end;
procedure TMainForm.Cascade1Click(Sender: TObject); begin Cascade;end;
ArrangeIcons;
Okay, now you're done with the main form. You can now move on to creating the MDI child form.
The MDI child form is surprisingly simple. In fact, you don't have to write any code at all. Just follow these steps:
That's it for the form itself. Now let's put an Image component on the form. The Image component will display the graphics file selected by the user.
The form is fairly unimpressive at this point, but it should look similar to Figure 4.19.
FIGURE 4.19. The MDI child form with an Image component.
You still have to create the About box, but right now you're probably eager to try the program out. Go ahead and click the Run button. After a while, the program is displayed. You can choose File | Open and open any graphics file (any file with a .bmp, a .wmf, or an .ico extension, that is).
Notice that the MDI child window sizes itself to the graphic it contains. Open several files and then try out the Cascade and Tile options under the Window menu. If you want, you can save a file with a different name using the File | Save As menu item.
By now you should know enough about Delphi to create the About box on your own. Create the About box so that it looks something like the one in Figure 4.20. If you get stuck, you can jump back a few pages and review the steps you took to create the About box earlier in the chapter. Feel free to make your About box as personalized as you like.
FIGURE 4.20. The About box for the application.
After you create the box, you can take these steps to call the box from the menu:
NOTE: Delphi has full support for long filenames. I use the 8.3 file-naming convention in this book for reasons related to electronic publishing. For applications you write, you can take advantage of long filenames.
AboutBox.ShowModal;
That should do it for now. Click the Run button and try out the About item on the Help menu. Figure 4.21 shows the Picture Viewer program running with several child windows open.
FIGURE 4.21. The Picture Viewer program running.
At this point the program is functional, but it isn't polished by any means. Still, for a 30-minute programming job, it's not too bad! There are a few problems with the program as it stands right now. If you try to open a file that is not a graphic, you will find that the program will throw an exception. I discuss exceptions and exception handling on Day 14, "Advanced Programming." Also, you have a lot of extra menu items that you need to get rid of. I'll show you how to do that on Day 6 as you work more with the Menu Designer.
There are two problems with this program that I think you should deal with because they are easy to fix. First, did you notice that a blank MDI child window was displayed when the application started? That's because a Delphi application automatically creates all forms when the application runs. In the case of an MDI child, that means the window is displayed when the application becomes visible. You are creating each MDI child form as needed, so you don't need to have Delphi auto-create the form for you.
Fortunately, removing the MDI child window form from the auto-create list is easy. Follow these steps:
FIGURE 4.22. The Project Options dialog box.
Now run the program again. This time the blank MDI child is not displayed.
CAUTION: If you remove a form from the auto-create list, you must be sure to specifically create the form prior to using it. If you do not create the form, the pointer to the form is uninitialized, which means that the pointer has not yet been assigned a meaningful value. (Remember that the pointer is automatically created by Delphi.) Attempting to use the pointer will result in an access violation or erratic program behavior. After you remove a form from the auto-create list, it is your responsibility to make sure the form has been created before using it.
Your application has one other problem I need to address. When you click the close button on one of the MDI windows, you will find that the window minimizes instead of closing. Believe it or not this is the standard behavior as prescribed by Microsoft. Standard behavior or not, it's weird, so you can fix it so that clicking the close box actually closes the window (as any rational person would expect). To do so, follow these steps:
procedure TChild.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree;end;
The Delphi IDE can be intimidating until you become familiar with it. If you learn it a little at a time, it's not nearly so daunting. Today you learned more about the various pieces that make up the Delphi IDE. Then you learned about how projects are used to create an executable file. You also learned more about forms. You found out how Delphi deals with dialog boxes and other child windows. You found out more about the Object Inspector and how it can be used to change a component's properties. You also learned about the docking features of the Delphi IDE. Finally, you got to create a program that actually does something interesting. Tomorrow you'll find out about the visual component model.
The Workshop contains quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you have learned. You can find the answers to the quiz questions in Appendix A, "Answers to the Quiz Questions."
© Copyright, Macmillan Computer Publishing. All rights reserved.