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
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.
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.
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.
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, "Creating Applications in Delphi.") |
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 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, "The Delphi IDE Explored," 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 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, "Projects, the Code Editor, and the Code Explorer.") 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.
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:
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.
You can also select multiple components so that you can act on them as a group. This is accomplished in one of three ways:
To select all components on the form, choose Edit | Select All from the main menu.
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:
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.
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.
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:
FIGURE 6.4. The form with a panel and six buttons.
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 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.
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.
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:
NOTE: After a paste operation, the component just pasted will be selected.
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.
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.
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.
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.
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.
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:
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.
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:
FIGURE 6.11. The form with the buttons randomly placed.
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à! 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:
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.
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.
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.
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:
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.
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.
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.
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, "Beyond the Basics." 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:
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.
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:
The form will now look like Figure 6.16.
FIGURE 6.16. The ScratchPad form up to this point.
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):
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.
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.
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:
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.
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.
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, "Place a MainMenu component on the form" rather than "Click on the Standard tab on the Component palette. Click the MainMenu button and click on the form to place the component." Don't worry, I'll still give plenty of details when new operations are introduced.
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.
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.
NOTE: The ampersand (&) 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&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:
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.
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 "File" { MENUITEM "Open", 100 MENUITEM "About", 101 }}
The following menu resource, however, is legal:
MENU_1 MENU BEGIN POPUP "File" BEGIN MENUITEM "Open", 100 MENUITEM "About", 101 ENDEND
This applies only to menus inserted with Insert From Resource and not to menu resources in general.
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:
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:
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 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:
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:
Too easy, right? Yes, but that's what Delphi is all about.
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:
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.
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.
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.
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:
That's all you have to do; Delphi takes care of it from there. The shortcuts function without you having to write any code.
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:
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.
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.
Before writing the code, you need to add the usual OpenDialog and SaveDialog components to the form:
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.
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.
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.
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.
unit SPMain; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Â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;
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 > 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(
`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 > 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 <> `' 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.
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.
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:
Okay, just a few more things and you'll be done. You need to change the Name property for the new menu items:
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:
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!
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.
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.
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.