Teach Yourself Borland Delphi 4 in 21 Days

Previous chapterNext chapterContents


- 7 -

VCL Components


As you know by now, components are much of what gives Delphi its power. Using the Form Designer, you can place a component on a form and modify its design-time properties. In some cases, that's all you have to do. If needed, you can also manipulate the component at runtime by changing its properties and calling its methods. Further, each component is designed to respond to certain events. I discussed properties, methods, and events on Day 5, "The Visual Component Model," so I'm not going to go over that again.

Today you will find out more about components. You will learn about often-used components and, as a result, learn about the Visual Component Library (VCL) classes that represent those components. As you go through this chapter, feel free to experiment. If you read something that you want to test, by all means do so. Learning by experience is as valuable as anything you can do, so don't be afraid to experiment.

A Review of Components

Let's review some of what you already know about components. Before doing that, though, I want to take a moment to explain the differences between a VCL component and a Windows control. Windows controls include components such as edit controls, list boxes, combo boxes, static controls (labels), and buttons, not to mention all the Win32 controls. Windows controls, by nature, don't have properties, methods, and events. Instead, messages are used to tell the control what to do or to get information from the control. To say that dealing with controls on this level is tedious and cumbersome would be an understatement.

A VCL component is a class that encapsulates a Windows control (not all VCL components encapsulate controls, though). A VCL component in effect adds properties, methods, and events to a Windows control to make working with the control easier. You might say that VCL takes a fresh approach to working with Windows controls. It could be said that all VCL components are controls, but not all controls are components. A VCL Edit component, for example, is a control, but a standard Windows edit control is not a VCL component. VCL components work with Windows controls to raise the job of dealing with those controls to a higher level.

Given that discussion, then, I will use the terms control and component interchangeably when referring to VCL components. (But I will never call a Windows control a component!)

Visual Components

Visual components include components such as edit controls, buttons, list boxes, labels, and so on. Most components you will use in a Delphi application are visual components. Visual components, as much as possible, show you at design time what the component will look like when the program runs.

New Term: Some components are visual components; others are nonvisual components. A visual component, as its name implies, is one that can be seen by the user at design time.

Nonvisual Components

New Term: A nonvisual component is one that cannot be seen by the user at design time.

Nonvisual components work behind the scenes to perform specific programming tasks. Examples include system timers, database components, and image lists. Common dialog boxes such as File Open, File Save, Font, and so on are considered nonvisual components as well. (They are nonvisual because they don't show themselves at design time. At runtime, they become visible when they are invoked.) The common dialog components are discussed later in the section "The Common Dialog Boxes."

When you place a nonvisual component on a form, Delphi displays an icon representing the component on the form. This icon is used to access the component at design time in order to change the component's properties, but the icon does not show up when the program runs. Nonvisual components have properties, methods, and events just like visual components do.

Now let's look at some of the common properties components share.

The Name Property

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

MyEdit: TEdit;

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

MyEdit.Text := `Jenna Lynn';

Delphi also uses the Name property when creating event-handler names. Let's say that you want to respond to the OnChange event for an Edit component. Normally, you double-click the Value column in the Object Inspector next to the OnChange event to have Delphi generate an event handler for the event. Delphi creates a default function name based on the Name property of the component and the event being handled. In this case, Delphi would generate a function called MyEditChange.

You can change the Name property at any time provided that you change it only via the Object Inspector. When you change a component's Name property at design time, Delphi goes through all the code that it previously generated and changes the name of the pointer and all event-handling functions.


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

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


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

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


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

Important Common Properties

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

The Align Property

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

The Color Property

The Color property sets the background color for the component. (The text color is set through the Font property.) Although the Color property is simple to use, there are a few aspects of component colors that should be addressed.

The way the Color property is handled in the Object Inspector is somewhat unique. If you click the Value column, you see the drop-down arrow button indicating that you can choose from a list of color values. That is certainly the case, but there's more to it than that. If you double-click the Value column, the Color dialog box will be displayed. This dialog box enables you to choose a color from one of the predefined colors or to create your own colors by clicking the Define Custom Colors button. Figure 7.1 shows the Color dialog box after the Define Custom Colors button has been clicked.

FIGURE 7.1. The Color dialog box.

NOTE: This is the same Color dialog box that will be displayed if you implement the ColorDialog component in your application.

If you choose a color from the Color dialog box, you see that the value of the Color property changes to a hexadecimal string. This string represents the red, green, and blue (RGB) values that make up the color. If you know the exact RGB value of a color, you can type it in (not likely!).

Most of the time you will probably choose a color from the list of color values provided. When you click the drop-down button to display the list of possible values, you will see what essentially amounts to two groups of values. The first group of colors begins with clBlack and ends with clWhite. These are the Delphi predefined colors; this list represents the most commonly used colors. To choose one of the listed colors, simply click the color in the list. If you can't find a color in the list that suits your needs, you can invoke the Color dialog box as discussed.

The second group of colors in the list begins with clScrollBar. This group of colors represents the Windows system colors. If you use colors from this list, your application will automatically adjust its colors when the user changes color schemes in Windows. If you want your application to follow the color scheme the user has chosen for his or her system, you should choose colors from this list rather than from the first list.

Use of color should be carefully considered. Proper use of color provides an aesthetically pleasing environment for the user. Abuse of colors makes for an obnoxious application that is annoying to use. Color is like a magnet to new programmers. It is common to want to throw lots of colors on a form because it's fun and easy, but don't get caught up in the fun at the expense of your users.

The Cursor Property

The Cursor property controls the cursor that is displayed when the user moves the mouse cursor over the component. Windows automatically changes cursors for some components. For example, Windows changes the cursor to an I-beam when the cursor is moved over an Edit, Memo, or RichEdit component.

To let Windows manage the cursor, leave the Cursor property set to crDefault. If you have specialized windows (components), you can specify one of the other cursors. When the mouse is moved over that component, Windows will change the cursor to the one you specified.

Frequently, you will need to change cursors at runtime. A long process, for example, should be indicated to the user by displaying the hourglass cursor. When you reset the cursor, you need to be sure to set the cursor back to whatever it was originally. The following code snippet illustrates this concept:

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

This ensures that the cursor that was originally set for the application is properly restored.

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

The Enabled Property

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


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

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


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

Although components can be disabled at design time, enabling and disabling components is something that is usually done at runtime. Menu items, for example, should be enabled or disabled according to whether they apply at a given time. The same is true of buttons. There are a variety of reasons why you might want to disable other types of controls as well.

To disable a component at runtime, just assign False to its Enabled property, and to enable a component assign True to Enabled. The following code snippet enables or disables a menu item based on some condition:

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

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


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

The Font Property

The Font property is a major property and therefore needs to be included here, but there is not a lot that needs to be said about it. The Font property is an instance of the TFont class and, as such, has its own properties. You can set the Font properties by double-clicking on the font name in the Object Inspector (which will expand the Font node and show the Font properties) or by invoking the Font dialog box. (The Font dialog box is discussed in more detail later in this chapter in the section "The Font Dialog Box.") Figure 7.2 shows the Object Inspector with the Font property node expanded to reveal the TFont properties.

FIGURE 7.2. The Object Inspector showing the Font property.

The Color property sets the color of the font, and the Name property enables you to choose the typeface for the font.

The Height and Size properties of TFont deserve special mention as well:

When you change one of these properties, the other will change automatically. The Height is often specified as a negative number. Refer to the online help for TFont for an explanation of why this is the case.

The Pitch property is not particularly useful. I'll explain it in just a moment, but first a quick tutorial on fonts. A font can be either proportionally spaced or fixed space:

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


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


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

The Hint Property

The Hint property is used to set hint text for a component. The hint text has two parts. The first part is sometimes called the short hint. This is the hint text that is displayed when the user places the cursor over the component and pauses. The pop-up window that displays the hint text is called a tooltip.

The second part of the hint text is sometimes called the long hint. The long hint is the optional hint text that shows in the status bar when the user moves the mouse cursor over the component. The short and long hint texts are separated by a pipe (|). For example, to specify both the short hint text and the long hint text for a File Open speed button, you would enter the following for the Hint property:

File Open|Open a file for editing

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


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


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

The ParentColor, ParentCtl3D, ParentFont, and ParentShowHint Properties

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

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

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

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

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

The Tag Property

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

Other Common Properties

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

TABLE 7.1. ADDITIONAL COMPONENT PROPERTIES.

Property Description
BorderStyle Can be bsSingle or bsNone. Use bsNone when you want the component to blend in with the background.
BoundsRect The rectangle of the entire component (not limited to only the client area).
Caption Sets the component's caption. Many components don't have captions, so for those components the Caption property is not exposed.
ClientHeight Contains the height of the client area of the component.
ClientRect Contains the rectangle for the client area of the component.
ClientWidth Contains the width of the client area of the component.
Constraints Sets the size constraints for the component (maximum width and height, minimum width and height). More important for forms than for other components.
Ctl3D Indicates whether the control should be drawn with a 3D border. If BorderStyle is set to bsNone, this property has no effect.
Height Sets the component's height.
HelpContext The HelpContext property is used to associate an index number in a help file with a particular component.
Left Sets the x-coordinate of the component.
Parent A pointer to the parent of the component.
PopupMenu Specifies the pop-up menu that will be displayed when the user clicks the secondary mouse button.
TabOrder For windowed components. Sets this component's position in the tab order.
TabStop For windowed components. Indicates that this component can be tabbed into. Setting this property to False removes the component from the tab order.
Top Sets the y-coordinate of the component.
Visible When read, indicates whether the component is currently visible. When written to, Visible either hides or shows the component.
Width Sets the width of the component.

Primary Methods of Components

There are more than 20 methods that most components have in common. Windowed components have more than 40 common methods from which to choose. Interestingly, not many of these are widely used. Much of the functionality of components is accomplished via properties. For example, to hide a component, you can call the Hide method or you can set the Visible property to False. In addition, components typically have methods specific to their purpose, and it will likely be those methods that you use most when dealing with a particular component.

There are a few methods worthy of note, however, so I'll list them here (see Table 7.2). Note that some of these methods are not available to all controls. These are not the most often used methods common to every component, but rather the most commonly used methods of components in general. Also, this list concentrates on components representing controls (components placed on forms) rather than components as forms. Methods particular to forms were discussed on Day 4, "The Delphi IDE Explored."


TABLE 7.2. COMMON METHODS OF COMPONENTS.

Method Description
Broadcast Used to send a message to all windowed child components.
ClientToScreen Converts client window coordinates into screen coordinates.
ContainsControl Returns True if the specified component is a child of the component or form.
HandleAllocated Returns True if the Handle property for the component has been created. Simply reading the Handle property automatically creates a handle if it hasn't already been created, so HandleAllocated can be used to check for the existence of the handle without creating it.
Hide Hides the component. The component is still available to be shown again later.
Invalidate Requests that the component be redrawn. The component will be redrawn at Windows's earliest convenience.
Perform Sends a message directly to a component rather than going through the Windows messaging system.
Refresh Requests that a component be redrawn immediately and erases the component prior to repainting.
Repaint Requests that a component be redrawn immediately. The component's background is not erased prior to repainting.
SetBounds Enables you to set the Top, Left, Width, and Height properties all at one time. This saves time having to set them individually.
SetFocus Sets the focus to a component and makes it the active component. Applies only to windowed components.
Update Forces an immediate repaint of the control. Typically, you should use Refresh or Repaint to repaint components.

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

Common Events

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

TABLE 7.3. COMMONLY HANDLED COMPONENT EVENTS.

Event Description
OnChange This event is triggered when a control changes in one way or another. Exact implementation depends on the component.
OnClick Sent when the component is clicked with either mouse button.
OnDblClick This event occurs when the user double-clicks the component.
OnEnter This event occurs when a windowed component receives focus (is activated).
OnExit This event occurs when a windowed component loses focus as the result of the user switching to a different control. It does not occur, however, when the user switches forms or switches to another application.
OnKeyDown This event is triggered when the user presses a key while the control has focus. Keys include all alphanumeric keys as well as keys such as the arrow keys, Home, End, Ctrl, and so on.
OnKeyPress This event is also triggered when the user presses a key, but only when alphanumeric keys or the Tab, backspace, Enter, or Esc keys are pressed.
OnKeyUp This event occurs whenever a key is released.
OnMouseDown This event is triggered when the mouse button is pressed while it's over the component. The parameters passed to the event handler give you information on which mouse button was clicked, special keys that were pressed (Alt, Shift, Ctrl), and the x,y coordinate of the mouse pointer when the event occurred.
OnMouseMove This event occurs any time the mouse is moved over the control.
OnMouseUp This event is triggered when the mouse button is released while over a control. The mouse button must first have been clicked while on the control.
OnPaint This event is sent any time a component needs repainting. You can respond to this event to do any custom painting a component requires.


DEALING WITH MOUSE EVENTS

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

OnMouseDown
OnClick
OnMouseUp



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

OnMouseDown
OnClick
OnDblClick
OnMouseUp



The point I am trying to make is that you need to take care when responding to both double-click and single-click events for a component. Be aware that you will get four events for a double-click event.

Multiple events will occur when a key is pressed, too. A keypress in an edit control, for example, will result in OnKeyDown, OnKeyPress, OnChange, and OnKeyUp events occurring.

The book's code (go to http://www.mcp.com/info and type 0-672-31286-7) contains a program called EventTst, which illustrates the fact that multiple events occur on mouse clicks and keypresses. Run this program and you will see how multiple events can be triggered based on certain user actions.


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

TStrings

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

Memo.Lines.SaveToFile(SaveDialog.FileName);

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

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

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

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

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

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


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

When the program runs, the list box will contain the contents of your WIN.INI file. Using this method, it's easy to load a list box from any ASCII text data file. The ComboBox component also has an Items property that works in exactly the same way.

You can add, delete, insert, and move items in a list box, combo box, or memo by calling the Add, Append, Delete, Insert, and Move methods of the TStrings class.


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

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

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


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

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



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


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


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

var
  Lines : TStrings;
{ ...later }
  Lines := TMemoStrings.Create;



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


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

Standard Windows Control Components

Back in the Jurassic age, there was something called Windows 3.0. Windows 3.0 gave you options such as edit controls (single line and multiline), list boxes, combo boxes, buttons, check boxes, radio buttons, and static controls. These controls must have been fairly well designed because they are very prevalent in Windows programs today--even considering all the new Win32 controls.

I'm not going to go over every Windows control and its corresponding VCL component. There are a few points, though, that you should know regarding the standard components, which are covered in the next sections.


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

Edit Controls

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

The Edit Component

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


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


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


The MaskEdit Component

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

03/21/98

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


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

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

FIGURE 7.3. The Input Mask Editor.

For more information on building your own masks, see the Delphi online help.

The Memo Component

The Memo component encapsulates a multiline edit control. The Lines property is the most significant property in a Memo component. As I mentioned earlier in the discussion on TStrings, the Lines property enables you to save the contents of the Memo component to disk, load the Memo with text from a file, or access the memo's lines individually.

The ScrollBars property is unique to the Memo component. This property enables you to specify whether your component has a horizontal scrollbar, a vertical scrollbar, or both. You used the ScrollBars property on Day 6 when you wrote the ScratchPad application. The Memo component is a very versatile component that you will probably find yourself using frequently.

The RichEdit Component

The RichEdit component is the biggest and the best of all the edit components; it is based on the Win32-rich edit control. The RichEdit component enables you to change fonts, use indentation, set text to bold, italic, or underlined, and much more. Basically, the RichEdit component is a mini word processor in one neat package. RichEdit has surprisingly few design-time properties over what the Memo component has.

Key runtime properties include SelAttributes and Paragraph. The RichEdit component is complex but easy to use, considering its complexities. See the Delphi online help for full details on the RichEdit component.

Common Edit Control Properties

Table 7.4 lists the properties specific to components based on edit controls.

TABLE 7.4. PROPERTIES FOR EDIT CONTROLS.

Item Applies To Description

Properties

AutoSelect Edit, MaskEdit When set to True, text in the edit control will automatically be selected when the user tabs to the control. Default: True
AutoSize Edit, MaskEdit When set to True, the edit control will automatically resize itself when the font of the edit control changes. Otherwise, the edit control does not change size when the font changes. Default: True
CharCase Edit, MaskEdit Determines whether the edit control displays uppercase (ecUpperCase), lowercase (ecLowerCase), or mixed text (ecNormal). Default: ecNormal.
HideScrollBars RichEdit When set to True, the scrollbars will be shown when needed but hidden otherwise. When set to False, the scrollbars are shown as determined by the value of the ScrollBars property.
HideSelection Edit, Memo, RichEdit When set to True, any text selected will not show as selected when the user tabs to another control. Default: False
Lines Memo, RichEdit The text contained in the component. Lines is an instance of the TStrings class.
MaxLength All Specifies the maximum number of characters that the component will hold. When set to 0, the amount of text that can be input is unlimited (limited only by system considerations). When set to any non-zero value, limits the number of characters to that value. Default: 0
OEMConvert Edit, Memo Set this property to True when the text input will consist of filenames. Default: False
PasswordChar Edit, MaskEdit When this property is set to a value other than ASCII #0, any text entered will be echoed with the character provided. The actual text in the edit control is unaffected. Most password edits use the asterisk (*) as the password character. Default: #0
PlainText RichEdit When set to True, RTF (rich text format) files will be shown as plain text without character and paragraph formatting. When set to False, RTF files are displayed with full formatting. Default: False
ReadOnly All When set to True, the component will display its text, but new text cannot be entered. The user can, however, highlight text and copy it to the Clipboard. Default: False
ScrollBars Memo, RichEdit Determines which scrollbars to display. Choices are ssNone, ssBoth, ssHorizontal, and ssVertical. Default: ssNone
Text Edit, MaskEdit Contains the text in the component.
WantReturns Memo, RichEdit When set to True, the component keeps the return character and a new line is inserted in the edit control when the user presses Enter. When set to False, return characters go to the form and are not placed in the edit control. If you have a form with a default button and WantReturns set to False, pressing Enter will cause the form to close. Default: True
WantTabs Memo, RichEdit When set to True, a tab character is placed in the edit control when the user presses the Tab key. When set to False, tab characters go to the form, which would enable tabbing out of the edit control. Default: False
WordWrap Memo, RichEdit When set to True, text entered will wrap to a new line when the right edge of the edit control is reached. When set to False, the edit control automatically scrolls as new text is entered. Default: True
Modified All Indicates whether the contents of the edit control have changed since the last time the Modified property was set. After saving the contents of a Memo or RichEdit component to a file, you should set Modified to False.
SelLength All Contains the length of the text currently selected in the edit control.
SelStart All Contains the starting point of the selected text in the edit control. The first character in the edit control is 0.
SelText All Contains the currently selected text in an edit control.

Edit controls have many common methods; they are too numerous to list here. The CutToClipboard, CopyToClipboard, PasteFromClipboard, and Clear methods deal with Clipboard operations and text manipulation. The GetSelTextBuff and GetTextBuff methods retrieve the selected text in the component and the entire text in the component, respectively. See the Delphi online help topics TEdit, TMaskEdit, TMemo, and TRichEdit for a complete list of methods associated with each edit component.

The edit component events that you are most likely to be interested in are dependent on the type of edit control you are using. In general, though, the OnEnter, OnExit, OnChange, OnKeyDown (or OnKeyPress), and OnKeyUp events will be the most widely used.

The ListBox and ComboBox Components

The ListBox and ComboBox components are also widely used. The ListBox component represents a standard Windows list box, which simply presents a list of choices that the user can choose from. If the list box contains more items than can be shown at one time, scrollbars provide access to the rest of the items in the list box.

New Term: Some list boxes are owner-drawn list boxes. In an owner-drawn list box, the programmer takes the responsibility for drawing the items in the list box.

You can do owner-drawn list boxes if needed. Owner-drawn list boxes are fairly common, although you might not realize it. On Day 4 I talked about customizing the Delphi toolbar. As part of that discussion, you looked at the Delphi Toolbar Editor dialog box. The Toolbar Editor dialog box contains two list boxes (see Figure 7.4).

FIGURE 7.4. The Toolbar Editor's Commands list box is owner-drawn.

The list box on the left is a regular list box; it lists the possible button groups you can choose from. The list box on the right is an owner-drawn list box. It shows the actual button as it will appear on the toolbar, as well as a textual description of what function the button performs.

Combo boxes are specialized list boxes. Actually, a combo box is a combination of a list box and an edit control. The user can choose from the list or type in a value in the edit portion. When the user chooses an item from the list, that item is placed in the edit control. There are three different types of combo box. The combo box type is determined by the Style property. Table 7.5 lists the types of combo boxes and a description of each.

TABLE 7.5. TYPES OF COMBO BOXES.

Item Description
Simple The simple style of the combo box is nothing more than an edit control placed on top of a list box. The user can choose from the list or type text in the edit portion.
Drop-down Similar to the simple style, except the list box portion is not initially displayed. A drop-down button is provided so that the user can view the list and choose an item. The user can also type text in the edit portion.
Drop-down list This is the most restrictive type of combo box. As with the drop-down style, the list is not initially exposed. The user can click the drop-down button to expose the list and choose an item from the list, but cannot enter text in the edit portion. Use this style when you want the user to select only from a predetermined set of choices.

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

FIGURE 7.5. The ComboTst program.

Table 7.6 lists the properties common to list boxes and combo boxes.

TABLE 7.6. PROPERTIES FOR EDIT CONTROLS.

Property Applies To Description
Properties
Columns ListBox Contains the number of columns in the list box. You can create multiple columns by making this property greater than 1. Default: 0
ExtendedSelection ListBox Determines whether extended selection is allowed. Extended selection enables the user to select items using Shift+click and Ctrl+click. Has no effect if MultiSelect is set to False. Default: True
IntegralHeight ListBox When True, the list box height will be resized to be sure that no partial lines are displayed. When False, the list box might show partial lines. Default: False
ItemHeight Both For use with owner-drawn list boxes and combo boxes. Sets the height of the items in the control. Default: 13
Items Both A TStrings instance that contains the list of items in the list box. (See the section on TStrings earlier in this chapter for a description of available properties and methods.)
MaxLength ComboBox The maximum number of characters the user can type in the edit portion of the combo box. Same as MaxLength in edit controls. Default: 0 (no limit)
MultiSelect ListBox When True, the list box enables multiple items to be selected. Default: False
Sorted Both When set to True, the list box items are sorted in ascending order. When set to False, the items are not sorted. Default: False
Style ComboBox The style of the combo box. Choices are csSimple, csDropDown, csDropDownList, lbOwnderDrawFixed, and csOwnerDrawVariable. (See Table 7.5 for a description of the three basic styles.) Default: csDropDown
ListBox Style choices for list boxes are lbStandard, lbOwnderDrawFixed, and csOwnerDrawVariable. Default: lbStandard
TabWidth ListBox List boxes can use tabs. This property sets the tab width in pixels. Default: 0
Text ComboBox Contains the text in the edit portion of the combo box.

Runtime Properties

ItemIndex

ListBox

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

SelCount

ListBox

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

Selected

ListBox

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

SelLength

ComboBox

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

SelStart

ComboBox

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

SelText

ComboBox

Contains the currently selected text in the edit control.

TopIndex

ListBox

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

As with the edit components you looked at earlier, there are very few ListBox and ComboBox methods. The Clear method clears the control of all data. The ItemAtPos methods return the list box item at the specified x and y coordinates. The SelectAll method selects the text in the edit control portion of a combo box.

Easily the most-used events when dealing with combo boxes and list boxes are the OnChange and OnClick events. Use these events to determine when a selection has been made in the list box.


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

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

VCL Button Types

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


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

Button Properties

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

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

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

Table 7.7 lists the ModalResult constants that VCL defines.

TABLE 7.7. VCL ModalResult CONSTANTS.

Constant Value
mrNone 0
mrOk 1
mrCancel 2
mrAbort 3
mrRetry 4
mrIgnore 5
mrYes 6
mrNo 7
mrAll 8
mrNoToAll 9
mrYesToAll 10


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

The book's code contains a program called ButtnTst that demonstrates the use of ModalResult. The program enables you to execute a form containing several buttons. When you click a button, the ModalResult will be reported on the main form.

The Default Property  The Default property is another key property of buttons. Windows has a standard mechanism for dealing with dialog boxes. One of the features of this mechanism is as follows: If a control other than a button has keyboard focus and the user presses the Enter key on the keyboard, the dialog box will behave as if the user had clicked the default button.

The default button is the button that has the BS_DEFPUSHBUTTON style set (usually the OK button). This feature has been the bane of programmers and the curse of data-entry personnel for years. The Default property is used to set a button as the default button for a form. The default value for this property is False. To make a button the default button, set its Default property to True. If you don't specifically set any button's Default property to True, the form will not close when the user presses the Enter key.


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

The Cancel Property

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


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


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

The Enabled Property  Earlier I discussed the Enabled property when I discussed components in general. This property is used often with buttons to enable or disable the button depending on the current state of the program or of a particular form. When a button is disabled (its Enabled property is set to False), its text is grayed out and the button does not function. In the case of buttons with bitmaps on them (BitBtn and SpeedButton), the bitmap will also be grayed out automatically.

Button components have only one method of interest: the Click method, which simulates a mouse click. When you call Click for a button, the OnClick event of the button is executed just as if the user had clicked the button. As for events, typically only the OnClick event is used.

Now let's take a look at the different button components Delphi provides.

The Button Component

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

The BitBtn Component

The BitBtn component is a perfect example of how a component can be extended to provide additional functionality. In this case, the standard Button component is extended to enable a bitmap to be displayed on the face of the button.

The BitBtn component has several properties in addition to what the Button component provides. All these properties work together to manage the bitmap on the button and the layout between the bitmap and the button's text. They are explained in the following sections.

The Glyph Property  The Glyph property represents the bitmap on the button. The value of the Glyph property is a picture, or glyph.

New Term: A glyph is a picture that is usually in the form of a Windows bitmap file (BMP).

The glyph itself consists of one or more bitmaps that represent the four possible states a button can be in: up, down, disabled, and stay down. If you are creating your own buttons, you can probably get by with supplying just one glyph, which the BitBtn component will then modify to represent the other three possible states. The bitmap will move down and to the right when the button is clicked and will be grayed out when disabled. The glyph in the stay-down state will be the same as in the up state, although the button face will change to give a pressed look.

If you provide more than one glyph, the glyphs must all be the same height and width and must be contained in a bitmap strip. The bitmaps that ship with Delphi provide two glyphs. Figure 7.6 shows the bitmap for the print button that comes with Delphi (print.bmp) in both its actual size and zoomed in to show detail. Note that the two glyphs each occupy the same width in the bitmap.

FIGURE 7.6. The PRINT.BMP bitmap.


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

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


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

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

For example, if you set the value of Kind to bkOK, the button will become an OK button. The glyph is set to a green check mark, the Cancel property is set to False, the Default property is set to True, the ModalResult property is set to mrOk, the Caption property is set to OK, and the results show up on the form. You can always override any of the properties modified by changing the Kind property, but it is not usually necessary to do so. Figure 7.7 shows the Button Test program from the code that comes with this book, with the BitBtn Test form displayed. The form contains each of the predefined button types available plus one custom button.

The Layout Property  The Layout property determines where the button is placed relative to the text. The default is blGlyphLeft. You can also choose to place the glyph on the face of the button to the right of the text, above the text, or below the text.

FIGURE 7.7. The predefined BitBtn types.

The Margin Property  The Margin property specifies the margin between the glyph and the edge of the button (which edge this property affects is determined by the value of the Layout property). The default is -1, which centers the glyph and the text in the button. Enter any positive value to set an absolute margin (in pixels).

The NumGlyphs Property  The NumGlyphs property specifies the number of glyphs you have in your bitmap strip for a particular button. You can supply from one to four glyphs, as I've mentioned. The glyphs must appear in the bitmap strip in this order: up, disabled, down, stay down.

The Spacing Property  The Spacing property controls the distance in pixels between the glyph and the button's text. The default value is four pixels.

The SpeedButton Component

The SpeedButton component was designed to be used with the Panel component to build toolbars. It is different from the Button and BitBtn components in that it is not a windowed component. This means that a speed button cannot receive input focus and cannot be tabbed to.

On the other hand, the SpeedButton component has several features in common with the BitBtn component. The way in which the Glyph property is handled by the SpeedButton component is exactly the same as with the BitBtn component, so I'm not going to go over that ground again. There are a couple of major differences, though, so let's look at those.

By default, speed buttons are square and are 25¥25 pixels. Your speed buttons can be any size you like and can contain text, although speed buttons don't usually contain text. There are some properties specific to speed buttons that you should be aware of, which I've broken down in the following sections.


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

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

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

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

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

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

When you run the program, click several of the buttons. You will notice that only one button can be in the down state at one time. As you can see when you assign a nonzero value to GroupIndex, the speed buttons change their behavior. A speed button with a GroupIndex of 0 pops back up when you click it, whereas a speed button that is part of a group stays down when clicked.

AllowAllUp  By default, one button in the group must be down at all times. You can change that behavior by setting the AllowAllUp property to True. Doing this for one button automatically changes the AllowAllUp property for all other buttons in the group to True as well. Now you can have any one button in the group selected or no buttons.


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

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

Radio Buttons and Check Boxes

Although radio buttons and check boxes are specialized buttons, they are, in the end, still buttons. I'm not going to spend a lot of time discussing these two components because implementing them is straightforward. Both the RadioButton and CheckBox components have a property called Checked that can be used to set the check state and can be read to retrieve the current check state.

The radio button is usually used in a group of buttons. A radio button typically signifies a group of options, only one of which can be selected at one time (like a group of speed buttons, which you just learned about). Although you can use a radio button by itself, it is not recommended because it is confusing to your users. When tempted to use a radio button by itself, use a check box instead--that's what a check box is for, after all.

Any radio buttons placed on a form will automatically be considered part of the same group. If you have more than one group of radio buttons, and those groups need to operate independent of one another, you need to use a RadioGroup component. This component enables you to quickly set up a group of radio buttons with a 3D frame around the buttons and a caption as well. To better understand this concept, try the following exercise:

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

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

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

Redtailed Hawk
Peregrine Falcon
Gyrfalcon
Northern Goshawk


4. Click OK to close the String list editor. The group box is populated with radio buttons containing the text you typed.
5. Change the Caption property of the radio group box to Apprentice Falconers Can Legally Possess:.

6. Click Run to compile and run the program.

When you click one of the radio buttons, the previously selected button pops up as expected. Using the RadioGroup component, you can put more than one group of radio buttons on a form. Like the list box and combo box components discussed earlier, the RadioGroup component has an ItemIndex property that you can read at runtime to determine which item in the group is selected. You can also set the ItemIndex to force a particular radio button to be selected. You might have noticed that none of the radio buttons were selected when the application ran. Change the ItemIndex to 0 in the Object Inspector and run the program again. This time the first radio button is selected.

Oh, by the way--if you live in the U.S., the answer to the quiz is Redtailed Hawk (American Kestrel would also have been an acceptable answer, but it was not presented in the list).


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

The CheckBox component is used to enable users to turn an option on or off or to indicate to a user that an option is currently on or off. A check box can have up to three states, depending on its style: on, off, or grayed. If the check box's AllowGrayed property is False, it can only be checked or unchecked. When the AllowGrayed property is True, the check box can be any one of the three states. The grayed, or indeterminate, state is handled programmatically.

In other words, it's up to you to decide what the grayed state means for your application. If the AllowGrayed property is False (the default), you can use the Checked property to determine whether the check box is checked or unchecked. If the AllowGrayed property is True, you must use the State property to determine (or set) the check box state. State will return cbChecked, cbUnchecked, or cbGrayed.


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

The Label Component

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

TABLE 7.8. PROPERTIES FOR THE Label COMPONENT.

Property Description
AutoSize When set to True, the label sizes itself according to the text contained in the Caption property. When set to False, text is clipped at the right edge of the label. Default: True
FocusControl A label is a non-windowed component, so it cannot receive input focus and it cannot be tabbed to. Sometimes, however, a label serves as the text for a control such as an edit control. In those cases you could assign an accelerator key to the label (using the ampersand) and then change the FocusControl property to the name of the control you want to receive focus when the label's accelerator key is pressed.
ShowAccelChar Set this property to True if you want an actual ampersand to show up in the label rather than the ampersand serving as the accelerator key. Default: True
Transparent When this property is set to True, the Color property is ignored and anything beneath the label shows through. This is useful for placing labels on bitmap backgrounds, for example. Default: False
WordWrap When set to True, text in the label will wrap around to a new line when it reaches the right edge of the label. Default: False


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

The ScrollBar Component

The ScrollBar component represents a stand-alone scrollbar. It's standalone in the sense that it is not connected to an edit control, list box, form, or anything else. I have not found that the scrollbar is a control I use very frequently. Certain types of applications use scrollbars heavily, of course, but for day-in, day-out applications its use is fairly uncommon.

The scrollbar's performance is set by setting the Min, Max, LargeChange, and SmallChange properties. The scrollbar's position can be set or obtained via the Position property. The Kind property enables you to specify a horizontal or vertical scrollbar.

The Panel Component

The Panel component is sort of a workhorse in Delphi. There is almost no limit to what you can use panels for. Panels can be used to hold toolbar buttons, to display text labels such as a title for a form, to display graphics, and to hold regular buttons as well. One of the advantages of a panel is that components placed on the panel become children of the panel. As such, they go with the panel wherever the panel goes. This can be a great aid at runtime and at design time.

Much of the power of the Panel component lies in its Align property. For example, let's say you want a title to be displayed on the top of a form. Let's further assume that you want it centered no matter how the user sizes the window. By setting the Align property to alTop and the Alignment property to taCenter, your title will always be centered. It's as simple as that.

A panel can have many appearances. The panel's appearance can be altered by changing the BevelInner, BevelOuter, BorderStyle, and BorderWidth properties, as displayed in Figure 7.8.

FIGURE 7.8. The Panel Styles Example showing different styles.

The Panel component is so versatile that it will take you a while to discover all its possible uses.

And That's Not All...

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

There is one other group of components that I need to discuss before you move on: the Dialog group.

The Common Dialog Boxes

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

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

The Execute Method

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

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

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


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

if OpenDialog.Execute then begin



is equivalent to

if OpenDialog.Execute = True then begin



Use either method, but the first is preferred.


The File Open and File Save Dialog Boxes

The File Open and File Save dialog boxes have several properties in common. File Open is used when you want to allow the user to open a file in your application (see Figure 7.9). It is encapsulated in the OpenDialog component. The File Save dialog box is used when getting a filename from the user in order to save a file. It is also used as the Save As dialog box. The File Save dialog box is encapsulated by the SaveDialog component.

FIGURE 7.9. A typical File Open dialog box.

The file dialog boxes are fairly easy to use in their most basic form. They do have a few features, however, that need to be explained in order for you to get the full benefit of using them. The following sections examine the properties that are specific to the file dialog boxes.


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

The DefaultExt Property

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

The FileName Property

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

The Files Property

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

The Filter Property

The Filter property contains a list of the file types from which the user can choose. The file types are displayed in the File of type: combo box in the file dialog box. You can set Filter to reflect types of files specific to your application. For example, a simple text-editing program could have the filter set to show files of type .TXT, .INI, and .LOG, to name just a few.

The filter can easily be set at design time through the Filter Editor dialog box. To invoke the Filter Editor, double-click the Value column next to the Filter property in the Object Inspector. Figure 7.10 shows the Filter Editor for a File Open dialog box, as described previously.

FIGURE 7.10. The Filter Editor dialog box.

The Filter Name column contains a textual description of the file type. The Filter column is the actual file mask that will be used to display files of that type.

Although you can enter the filter string directly in the Value column of the Object Inspector, it is easiest to use the Filter Editor. If you are using only a single filter, you can type it directly into the Value column for the Filter property. Separate the description and filter with a pipe. For example, to have a single filter for all file types, you would enter the following:

All Files (*.*)|*.*

The FilterIndex Property

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

The InitialDir Property

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


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

The Options Property

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

The Title Property

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


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

The file dialog boxes have no events associated with them.


TIP: You can implement a File Open dialog box (or any of the common dialog boxes) at runtime without ever placing an OpenDialog component on your form. To accomplish this, create an instance of the TOpenDialog class and then call its Execute method, as follows:

procedure TForm1.Button1Click(Sender: TObject);
var
  OpenDlg : TOpenDialog;
begin
  OpenDlg := TOpenDialog.Create(Self);
  if OpenDlg.Execute then begin
    { do something here }
  end;
  OpenDlg.Free;
end;



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


The File Open Picture and File Save Picture Dialog Boxes

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

The Color Dialog Box

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

FIGURE 7.11. The File Open Picture dialog box.

The Font Dialog Box

The Font dialog box enables the user to choose a font from the list of fonts available on his or her system. Through the Device property, you can choose whether you want screen fonts, printer fonts, or both types of fonts to be displayed. You can limit the maximum and minimum font sizes that the user can select by modifying the MaxFontSize and MinFontSize properties. As with the file dialog boxes, the Options property contains a wide variety of options you can use to control how the Font dialog box functions.

If the user clicks OK, the Font property will contain all the information you need to implement the new font. Figure 7.12 shows the Font dialog box in the default configuration.

FIGURE 7.12. The Font dialog box.

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

The Find and Replace Dialog Boxes

The Find and Replace dialog boxes provide users the capability to enter text to search for and text to replace the found text with, and a variety of search and replace options. The Find dialog box is encapsulated in the VCL component FindDialog, and the Replace dialog box is represented by the ReplaceDialog component. The Replace dialog box, which contains everything found on the Find dialog box plus the extra replace features, is shown in Figure 7.13.

FIGURE 7.13. The Replace dialog box.

Major properties of the FindDialog and ReplaceDialog components include FindText (the text to find), ReplaceText (the text with which to replace the found text), and Options. Obviously, the FindDialog does not have a ReplaceText property. The Options property contains a wide variety of information about the various options that the user set at the time the Find Next, Replace, or Replace All button was clicked.

The Execute method for the FindDialog and ReplaceDialog components is a little different than it is with the other common Dialog components. First of all, the Find and Replace dialog boxes are modeless dialog boxes. As soon as the dialog box is displayed, the Execute method returns.

Because the dialog box is modeless, the return value from Execute is meaningless (it's always True). Instead, the Find and Replace dialog boxes use the OnFind and OnReplace events along with the Options property to determine what is happening with the dialog box. The OnFind event occurs when the Find Next button is clicked. The ReplaceDialog has an OnFind event, but it also has an OnReplace event that is fired when the Replace or Replace All button is clicked. Use these events to determine when the user has requested a find or replace action. Your programs should read the Options property to determine how the user intended the find or replace operation to be carried out.

Summary

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

Workshop

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

Q&A

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Quiz

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

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

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

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

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

6. How many types of combo boxes are there?

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

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

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

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

Exercises

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

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

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

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

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

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

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

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

In Review

You covered a lot of ground this week. In some ways this was the toughest week. Object Pascal, while not as daunting as some other programming languages, still takes some time to learn. But there is no doubt that you can learn to program in Delphi if you stay with it. Don't forget to take a break now and then. This book is titled Sams Teach Yourself Delphi 4 in 21 Days, but that doesn't mean that they have to be consecutive days! Sometimes it's good to take a few days off to let it all soak in.

If you are confused by some of the Object Pascal syntax, don't feel you are alone. It all can be confusing at first. Don't worry, though, because you will start to get the hang of it before long. As you work with Delphi, little by little it begins to make sense. What you probably lack at this point is real-world experience. That is where you really learn. Knowledge gained by experience is the kind that sticks. My advice is to take an idea and turn it into a working program. You can't necessarily do that at this point, but you can get a good start. The program doesn't have to be as advanced as Microsoft Word, Netscape Navigator, or a game like DOOM, mind you. It just needs to be a little something to help you tie your education in with some experience.

The first part of this week, you worked on Object Pascal language keywords and syntax. Items such as loops and if statements are fairly easy to comprehend. Don't be concerned, though, if you have to go back and look up the syntax once in a while. There is a lot to learn, and you aren't expected to memorize every keyword and its syntax. Later on you will be, but at this stage of the game, it isn't expected.

On Day 3, you were introduced to Object Pascal classes. Classes are a big part of Object Pascal and programming in Delphi. Sometimes it takes a while to perceive where classes can be used in your programs. For a long time, you might deal only with the classes that the Visual Component Library (VCL) provides and not write any classes of your own. Later on you will probably find situations where a class will fit perfectly with a particular task you have to accomplish. When that time comes, you will be ready to tackle writing your own class. After you've written one or two, you will be off and running.

On Day 4, you learned about the IDE: how to customize it to your liking, how the Component Palette works, what the Object Inspector is for, and so on. This part of the week you experienced the fun stuff. It's okay to use the word fun. I find all kinds of programming a great deal of fun. That's why I do it. I hope that you find it fun, too.

On Day 5, I gave you an introduction to class libraries, also known as frameworks. VCL is a framework. A framework makes your life easier by encapsulating difficult Windows programming tasks into classes that you can deal with on a more rational level. Believe me, sometimes the raw Windows API seems to be anything but rational. VCL takes care of dealing with those issues for you and provides you with a higher level of programming objects that you can easily incorporate in your applications. No, VCL is not easy, but it is much easier than dealing with the API. As part of the discussion on frameworks, you were introduced to the component model. You learned about properties, methods, and events and how you use them to build Windows programs in Delphi.

On Day 6, you learned about the Form Designer. The Form Designer is where the bulk of your Delphi applications will be designed--the graphical part of the application, anyway. Working with the Form Designer can be fun, too. Using the Form Designer, you can create great-looking forms. Remember, a form represents a window in your applications. Most applications have a main window and several dialog boxes that are displayed, based on user interaction with the program. On Day 6, you also created a simple but useful program. This program, ScratchPad, gave you a start in building an application with Delphi. ScratchPad will be used throughout the book. As you build your programming knowledge, you will add new features to ScratchPad to give you practice with the techniques presented. If you are developing an application of your own, I encourage you to add new features to your program as you learn about them.

On Day 7, you learned about some of the VCL components available to you. I didn't cover all the VCL components, but I covered the components that are most commonly used in Windows programming with Delphi. You will be introduced to other VCL components as you work through the rest of the book.

I hope this week hasn't left you too worn out. If it has, take a short break and then jump right back into the game. If you found this week exhilarating and energizing, just keep on turnin' those pages. I'm ready if you are.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.