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

' + pPage + ''; zhtm += '
'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Teach Yourself Borland Delphi 4 in 21 Days -- Ch 5 -- The Visual Component Model


Teach Yourself Borland Delphi 4 in 21 Days

Previous chapterNext chapterContents


- 5 -

The Visual Component Model


Today I am going to talk about the visual component model and Borland's Visual Component Library (VCL). Before getting into that, though, I'll talk a little about class frameworks. In this chapter you will find

Frameworks Fundamentals

"In the beginning there was C. . . ." Well, not quite. As far as Windows programming is concerned, though, that statement is accurate. In the beginning, the vast majority of Windows programs were written in C. In fact, the Windows Application Programming Interface (API) is just a huge collection of C functions--hundreds of them. There are still undoubtedly thousands of programmers out there writing Windows programs in C.

Somewhere along the line, the folks at Borland decided, "There has got to be an easier way." (Actually, the framework revolution might have started on several different fronts, but Borland was certainly a leader.) It was apparent that Windows programming was very well suited to object-oriented programming. By creating classes that encapsulate common Windows programming tasks, a programmer could be much more productive. After a class was created to encapsulate a window's various duties, for instance, that class could be used over and over again. The framework revolution began.

But I haven't yet told you what a framework is.

New Term: A framework is a collection of classes that simplifies programming in Windows by encapsulating often-used programming techniques. Frameworks are also called class libraries. Encapsulation means taking a complex programming task and making it easier by providing a simplified interface.

Popular frameworks have classes that encapsulate windows, edit controls, list boxes, graphics operations, bitmaps, scrollbars, dialog boxes, and so on.

So Why Should I Care About Frameworks?

That's a good question. The bottom line is that frameworks make Windows programming much easier than it would be in straight C, in assembler, or in the original Pascal language (the Pascal that came prior to Object Pascal). Let me give you an example. Listing 5.1 contains a portion of a Windows program written in C++. This section of code loads a bitmap file from disk and displays the bitmap in the center of the screen. None of this will make sense to you right now, but be patient.

LISTING 5.1. C++ CODE TO LOAD AND DISPLAY A BITMAP.

HPALETTE hPal;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
LPBITMAPINFO lpbi = 0;
HFILE hFile;
DWORD nClrUsed, nSize;
HDC hDC;
HBITMAP hBitmap;
void *bits;
do {
  if ((hFile = _lopen(data.FileName, OF_READ)) == HFILE_ERROR) break;
  if (_hread(hFile, &bfh, sizeof(bfh)) != sizeof(bfh)) break;
  if (bfh.bfType != `BM') break;
  if (_hread(hFile, &bih, sizeof(bih)) != sizeof(bih)) break;
  nClrUsed =
    (bih.biClrUsed) ? bih.biClrUsed : 1 << bih.biBitCount;
  nSize =
    sizeof(BITMAPINFOHEADER) + nClrUsed * sizeof(RGBQUAD);
  lpbi = (LPBITMAPINFO) GlobalAllocPtr(GHND, nSize);
  if (!lpbi) break;
  MoveMemory(lpbi, &bih, sizeof(bih));
  nSize = nClrUsed * sizeof(RGBQUAD);
  if (_hread(hFile, &lpbi->bmiColors, nSize) != nSize) break;
  if (_llseek(hFile, bfh.bfOffBits, 0) == HFILE_ERROR) break;
  nSize = bfh.bfSize-bfh.bfOffBits;
  if ((bits = GlobalAllocPtr(GHND, nSize)) == NULL) break;
  if (_hread(hFile, bits, nSize) != nSize) break;
  hDC = GetDC(hWnd);
  hBitmap = CreateDIBitmap(hDC, &(lpbi->bmiHeader), CBM_INIT,
                          bits, lpbi, DIB_RGB_COLORS);
  if (hBitmap) {
    LPLOGPALETTE lppal;
    DWORD nsize = sizeof(LOGPALETTE)
      + (nClrUsed-1) * sizeof(PALETTEENTRY);
    lppal = (LPLOGPALETTE)  GlobalAllocPtr(GHND, nSize);
    if (lppal) {
      lppal->palVersion = 0x0300;
      lppal->palNumEntries = (WORD) nClrUsed;
      MoveMemory(lppal->palPalEntry, lpbi->bmiColors,
      nClrUsed * sizeof(PALETTEENTRY));
      hPal = CreatePalette(lppal);
      (void) GlobalFreePtr(lppal);
    } 
  } 
}  while(FALSE);
if (hFile != HFILE_ERROR) _lclose(hFile);
HPALETTE oldPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP oldBitmap =(HBITMAP)SelectObject(hMemDC, hBitmap);
BitBlt(hDC, 0, 0, (WORD)bih.biWidth, (WORD)bih.biHeight,
  hMemDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, oldBitmap);
DeleteDC(hMemDC);
SelectPalette(hDC, oldPal, FALSE);
ReleaseDC(hWnd, hDC);
if (bits) (void) GlobalFreePtr(bits);
if (lpbi) (void) GlobalFreePtr(lpbi);

That looks just a little intimidating, doesn't it? Now look at the equivalent using Borland's VCL:

Image.LoadFromFile(`winnt.bmp');

So which would you rather use? You don't even have to know what these code snippets do to make that decision. It's easy to see that the VCL version is shorter (just a bit!) and more readable.

These examples sum up what frameworks are all about. Frameworks hide details from you that you don't need to know. Everything that is contained in Listing 5.1 is performed behind the scenes in the VCL code (albeit in Pascal rather than in C++). You don't need to know every detail about what goes on behind the scenes when VCL does its job, and you probably don't want to know. All you want is to take the objects that make up a framework and put them to use in your programs.

A good framework takes full advantage of OOP, and some do that better than others. Borland's Object Windows Library (which came in both C++ and Pascal versions) and Visual Component Library are excellent examples of object-oriented programming. They provide the proper abstraction needed for you to rise above the clutter and get down to the serious business of programming.

So What's the Catch?

A little skeptical, are you? Good. You're bright enough to figure out that if you have all that ease of use, you must be giving up something. Truth is, you are right. You might think that a program written with a framework would be larger and slower than its counterpart written in a low-level language. That's partially correct. Applications written with frameworks don't necessarily have to be slower than those other programs, though. There is some additional overhead inherent in an object-oriented language, certainly, but for the most part, it is not noticeable in a typical Windows program.

The primary trade-off is that Windows programs written in Delphi tend to be larger than programs written in languages such as C. For example, let's say you had a simple Windows program written in C that was 75KB. The equivalent program written with Delphi might be 250KB. That might seem like a significant difference, but this example demonstrates the worst-case scenario. The difference in final program size between a C application and a Delphi application written with a framework is most noticeable in very small programs. As your programs increase in size and sophistication, the size difference is much less noticeable.

One reason for the size difference is simply the difference between a procedural language and an object-oriented language. Object-oriented languages (C++ and Object Pascal, for example) carry additional overhead for features such as exception handling, runtime type information (RTTI), and other OOP goodies. In my opinion, the difference in code size is an acceptable trade-off for the features that Object Pascal provides.

Now, before you label me as a code-bloat proponent, let me say that I am as conscientious as the next person when it comes to code bloat. I believe that we should all write the tightest code we can given the tools we use. I am also a realist, and I understand that time-to-market is a driving force in the software industry today. I am willing to trade some code size for the power that Object Pascal and VCL give me. Let me put it another way. I'm not interested in spending a month to write a Windows program that compiles to a 100KB executable when I can accomplish the same thing in Delphi in two days and end up with a 400KB executable. The size of the resulting executables is insignificant when compared to the development time saved.


FRAMEWORKS TEACH OBJECT-ORIENTED PROGRAMMING AND DESIGN

If you end up getting serious about this crazy game called Windows programming, you will eventually end up peeking into the source code of your favorite framework. Sooner or later you'll want to know how the pros do things. The VCL source code is a great place to go for that kind of information.




Some weekend when the leaves are raked, the house trim is painted, the laundry is done, the kids are at Grandma's, and you think you have a good handle on Delphi, spend some time browsing the VCL source code. (Delphi Professional and Client/Server ship with the VCL source code.) It can be intimidating at first, but after a while you'll see what the designers were doing. Don't strain yourself. Attempt to understand the things that bump up against the limits of your knowledge regarding Object Pascal. Leave the complicated stuff for later.
But notice how the VCL designers use private, protected, and public access in classes. Notice how things that should be kept hidden from the user aren't in public view. Studying the VCL source can teach you a great deal about Object Pascal and object-oriented design.


The Visual Component Library

You have probably noticed that this book contains "Delphi 4" in its title. Obviously Delphi has been around a while. When Delphi 1 was introduced in 1995, it was an instant hit. Delphi offered rapid application development (RAD) using something called components. Components are objects that can be dropped on a form and manipulated via properties, methods, and events. It's visual programming, if you will.

The concept of form-based programming was first popularized by Microsoft's Visual Basic. Unlike Visual Basic, though, Delphi used a derivative of Pascal as its programming language. This new language, called Object Pascal, introduced OOP to the Pascal language. Delphi and Object Pascal represented the marriage of object-oriented programming and form-based programming. In addition, Delphi could produce standalone executables. Real programs. Programs that did not require a runtime DLL to run; programs that were compiled, not interpreted; programs that ran tens of times faster than Visual Basic programs. The programming world was impressed.

Delphi didn't just throw Object Pascal at you and let you flounder. It also introduced the Visual Component Library. As I have said, VCL is an application framework for Windows programming in Object Pascal. The most noticeable feature of VCL is that it was designed around the concept of properties, methods, and events--the visual component model. Let's look at the visual component model in more detail.

Components

As I talked about on Day 1, "Getting Started with Delphi," VCL components are objects that perform a specific programming task. VCL components are wrapped up in Object Pascal classes. From now on in this book, you will be encountering components on a daily basis. I won't spend a lot of time explaining every detail of components right now because you will see by example how they work throughout the rest of the book. I'll explain components in more detail on Day 7, "VCL Components."

Properties, Methods, and Events

On Day 1, I gave you a brief introduction to the properties, methods, and events model. These three ingredients make up the public interface of components in VCL (the part of the component the user will see). Let's take a look at these elements one at a time.

Properties

Properties are elements of a component that control how the component operates. Many components have common properties. All visual components, for example, have a Top and a Left property. These two properties control where the component will be positioned on a form both at design time and at runtime. All components have an Owner property, which VCL uses to keep track of the child components a particular parent form or component owns.

Properties and the Object Inspector  A picture is always worth a thousand words, so let's start up Delphi again and see properties in action. When you start Delphi, you are greeted with a blank form and the Object Inspector.


NOTE: If you have the Delphi options configured to save the desktop when you close Delphi, you might see the last project you were working on when you start Delphi. If that's the case, choose File|New Application from the main menu to get a blank form.

The Object Inspector will look something like Figure 5.1. (When Delphi starts, it sizes the Object Inspector based on your current screen resolution, so your Object Inspector might be taller or shorter than the one shown in Figure 5.1.) If necessary, click on the Properties tab of the Object Inspector window so that the form's properties are displayed. The component's properties are arranged in alphabetical order.

FIGURE 5.1. The Object Inspector.

If more properties exist than can be displayed at one time, the Object Inspector displays a scrollbar so that you can view additional properties. The Object Inspector window can be moved and sized. I like my Object Inspector as tall as my screen permits so that I can see the maximum number of properties at one time. Scroll through the properties until you locate the Left property and then click on it. Change the value for the Left property (any number between 0 and 600 will do) and press Enter on the keyboard. Notice how the form moves as you change the value.

This illustrates an important aspect of properties: they are more than simple fields of a class. Each property has an underlying data field associated with it, but the property itself is not a class data field. Changing a property often leads to code executed behind the scenes.


New Term: Properties are often tied to access methods that execute when the property is modified.


Changing a Property's Value  Properties can be changed at design time (when you are designing your form) and at runtime (when the program is running through code you write). In either case, if the property has an access method, that access method will be called and executed when the property is modified. You already saw an example of changing a property at design time when you changed the Left property and watched the form move on the screen.

That is one of the strengths of VCL and how it is used in Delphi: You can instantly see on the screen the result of your design change. Not all properties show a visible change on the form at design time, however, so this doesn't happen in every case. Still, when possible, the results of the new property value are immediately displayed on the form.

To change a property at runtime, you simply make an assignment to the property. When you make an assignment, VCL works behind the scenes to call the access method for that property. To change the Left property at runtime, you use code like this:

Left := 200;

In the case of the Left property (as well as the Top property), VCL moves and repaints the form. (For you Windows API programmers, you can figure out that this eventually translates into calls to the Windows API functions SetWindowPos and InvalidateRect.)

New Term: Property Access Specifiers  Properties have two access specifiers, which are used when properties are read or modified. There is a read specifier and a write specifier.

Suffice it to say that access specifiers associate read and write methods with the property. When the property is read or written to, the methods associated with the property are automatically called. When you make an assignment as in the previous example, you are accessing the write specifier. In effect, VCL checks to see whether an access method exists for the write specifier. If it does, the access method is called. If no access method exists, VCL assigns the new value to the data field associated with the property.

When you reference a property (use the property as the right side of an equation), you are accessing the read specifier:

X := Left;

In this case, VCL calls the read specifier to read the value of the Left property. In many cases the read specifier does very little more than return a property's current value.

Property Attributes  The properties of the property (sorry, I couldn't resist) are determined by the writer of the component. A property can be read-only. A read-only property can be read--its value can be retrieved--but not 
written to. In other words, you can fetch the property's value, but you can't change it. In rare cases, a property can be made write-only (a property that can be written to but not read isn't very useful in most cases). This is obviously the opposite 
of a read-only property. 

Finally, some properties can be specified runtime-only. A runtime-only property can be accessed only at runtime, not design time. Because a runtime-only property doesn't apply at design time, it is not displayed in the Object Inspector. A runtime-only property can be declared as read-only, too, which means that it can be accessed only at runtime and can only be read (not written to).

Property Types  Some properties use an instance of a VCL class as the underlying data field. To illustrate, let's put a memo component on our blank form:

1. Go to the Delphi Component palette, choose the Standard tab, and click the Memo button. (The tooltip will tell when you are over the Memo button.)

2. Now move to the form and click on it where you want the memo's top-left corner to appear. As soon as you place the memo component on the form, the Object Inspector switches to show you the properties of the component just placed on the form, in this case a TMemo.

3. Locate the Lines property and click on it. Notice that the property value contains the text (TStrings) and that there is a little button with an ellipsis (. . .) to the right of the property value.


The ellipsis button tells you that this property can be edited by using a property editor. For an array of strings, for instance, a dialog box will be displayed in which you can type the strings. In the case of the Font property, clicking the ellipsis button will invoke the Choose Font dialog box. The exact type of the property editor is property-specific, although certain properties can share a common editor. You can bring up the property editor by clicking the ellipsis button or by double-clicking the property value.

The Lines property for a memo component is an instance of the TStrings class. When you double-click the Value column, the string editor is displayed and you can then type the strings you want displayed in the memo component when the application runs. If you don't want any strings displayed in the memo component, you need to clear the property editor of any strings.


The Font property is another example of a property that is an instance of a VCL class. A font includes things like the typeface, the color, the font size, and so on. Locate the Font property in the Object Inspector. (It doesn't matter whether you have selected the memo component or the form.)

Notice that there is a plus sign before the word Font. This tells you that there are individual properties within this property that can be set. If you double-click on the property name, you will see that the Object Inspector expands the property to reveal its individual elements. You can now individually edit the Font property's elements. In the case of the Font property, these same settings can be edited by invoking the property editor. You can use either method with the same results.

Some properties are sets (I talked about sets on Day 3, "Classes and Object-Oriented Programming"). The Style property within the Font object is a good example of a set. Notice that Style has a plus sign in front of it. If you double-click on the Style property, you will see that the Style node expands to reveal the set's contents. In this case, the set consists of the various styles available for fonts: bold, italic, underline, and strikeout. By double-clicking a style, you can turn that style on or off. A set can be empty or can contain one or more of the allowed values.

New Term: Some properties can be enumerations, a list of possible choices.

An enumeration is a list of possible choices for a property. When you click on an enumeration property, a drop-down arrow button appears to the right of the value. To see the choices in the enumeration, click the drop-down button to display the list of choices. Alternatively, you can double-click the Value column for the property. As you double-click on the property's value, the Object Inspector will cycle through (or enumerate) the choices. The Cursor property gives a good example of an enumerated property. Locate the Cursor property and click the arrow button to expose the list of possible cursors to choose from.

Enumerations and sets differ in that with an enumeration property, only one of the presented choices can be selected (only one cursor can be in effect at any time). The set can contain none or any number of the choices (a font style can contain bold, underline, italic, or none of these).

As long as you have Delphi running and a blank form displayed, you might as well spend some time examining the various components and their properties. Go ahead, I'll wait.


HOUSE RULES: PROPERTIES




Methods

I discussed methods on Day 3, "Classes and Object-Oriented Programming," so I don't need to cover them again here in any great detail. Methods in VCL components are functions and procedures that can be called to make the 
component perform certain actions. For example, all visual components have a method called Show, which displays the component, and a method called Hide, which hides the component. For example:
MyWindow.Show;
{ do some stuff, then later... } 
MyWindow.Hide;

Methods in VCL can be declared as public, protected, or private. Public methods can be accessed by the component's users. In this example, both the Show and Hide methods are public. Protected methods cannot be accessed by the component users but can be accessed by classes (components) derived from a component. Of course, private methods can be accessed only within a class itself.

Some methods take parameters and return values, others don't. It depends entirely on how the method was written by the component writer. For example, the GetTextBuf method retrieves the text of a TEdit component. This method can be used to get the text from an edit control as follows:

var
  Buff     : array [0..255] of Char;
  NumChars : Integer;
begin
  NumChars := EditControl.GetTextBuf(Buff, SizeOf(Buff));
end;

As you can see, this particular method takes two parameters and returns an integer. When this method is called, the edit control contents are placed in the variable Buff and the return value is the number of characters retrieved from the edit control.

For now, that's all you need to know in order to use methods. I'll discuss them in more detail on Day 20, "Creating Components."

House Rules: Methods

Events

New Term: Windows is said to be an event-driven environment. Event-driven means that a program is driven by events that occur within the Windows environment. Events include mouse movements, mouse clicks, and key presses.

Programmers moving from DOS or mainframe programming environments might have some difficulty with the concept of something being event-driven. A Windows program continually polls Windows for events. Events in Windows include a menu being activated, a button being clicked, a window being moved, a window needing repainting, a window being activated, and so forth.

Windows notifies a program of an event by sending a Windows message. There are over 200 possible messages that Windows can send to an application. That's a lot of messages. Fortunately, you don't have to know about each and every one of them to program in Delphi; there are only a couple dozen that are used frequently.

VCL Events  In VCL, an event is anything that occurs in the component that the user might need to know about. Each component is designed to respond to certain events. Usually this means a Windows event, but it can mean other things as well. For example, a button component is designed to respond to a mouse click, as you would expect. But a nonvisual control, such as a database component, can respond to non-Windows events, such as the user reaching the end of the table.

New Term: Handling Events  When you respond to a component event, you are said to handle the event.

Events are handled through methods called event handlers. You used event handlers extensively as you worked through the first three days of the book.

A typical Windows program spends most of its time idle, waiting for some event to occur. VCL makes it incredibly easy to handle events. The events that a component has been designed to handle are listed under the Events tab in the Object Inspector window. Event names are descriptive of the event to which they respond. For instance, the event to handle a mouse click is called OnClick.


NOTE: You don't have to handle every event that a component defines. In fact, you rarely do. If you don't respond to a particular event, the event message is either discarded or handled in a default manner as described by either VCL or the component itself. You can handle any events you have an interest in and ignore the rest.

These concepts will make more sense if you put them into practice. To begin, let's start a new application. Choose File|New Application from the main menu. If you are prompted to save the current project, click No. Now you will again have a blank form. First, set up the main form:

1. Change the Name property to PMEForm (PME for properties, methods, and events).

2. Change the Caption property to PME Test Program.

Next, you need to add a memo component to the form:


1. Choose the Standard tab on the Component palette and click the Memo button.

2. Click on the form to place a memo component on the form.

3. Change the Name property to Memo. Be sure the memo component is selected so that you don't accidentally change the form's name instead of the memo component.

4. Double-click on the Lines property in the Value column. The String list editor will be displayed.

5. Delete the word Memo and type A test program using properties, methods, and events. Click OK to close the String list editor.

6. Resize the memo component so that it occupies most of the form. Leave room for a button at the bottom.

Your form will now look like the form shown in Figure 5.2.


Now you can place a button on the form:

2. Click on the form below the memo component to place the button on the form.

3. Change the Name property for the button to Button.

4. Change the Caption property to Show/Hide.

FIGURE 5.2. The form with a memo component added.

5. Center the button horizontally on the form.


TIP: You can center components visually, but for a more exact method use the Alignment palette. Choose View|Alignment Palette from the main menu and then click the Center horizontally in window button on the Alignment palette to center a component horizontally on the form.

You will use this button to alternately show and hide the memo component. Now you need to write some code so that the button does something when it is clicked. Be sure that the button component is selected, and then click on the Events tab in the Object Inspector.


A list of the events that a button component is designed to handle is presented. The top event should be the OnClick event. Double-click on the Value column of the OnClick event. What happens next is one of the great things about visual programming. The Code Editor comes to the top and displays the OnClick procedure ready for you to type code. Figure 5.3 shows the Code Editor with the OnClick handler displayed.

FIGURE 5.3. The Delphi Code Editor with the OnClick handler displayed.


NOTE: Your Code Editor might not look exactly like Figure 5.3. I removed the Module Explorer from the Code Editor window to show more of the code. One of the great things about the Delphi IDE is that it is fully customizable. If you don't like the default layout, you can always change it.

Adding Event Handler Code  Notice that the event handler is already set up for you, all you have to do is type the code. If you take a good look at the event handler, you will see that it is a procedure, that it is called ButtonClick, that it is a member of the TPMEForm class, and that it takes a pointer to a TObject called Sender as a parameter. (I'll talk about the Sender parameter in just a bit.) All that is left to do now is type code that shows and hides the button each time the button is clicked. We'll borrow a little code from our earlier discussion of methods. Edit the ButtonClick method until it looks like this:

procedure TPMEForm.ButtonClick(Sender: TObject);
const
  IsVisible : Boolean = False;
begin
  IsVisible := not IsVisible;
  if IsVisible then
    Memo.Hide
  else
    Memo.Show;
end;
This code first declares a typed constant named IsVisible.

New Term: A typed constant, when used in this way, is a variable that retains its value between calls to the method.

The first line of code in this method flips the Boolean variable between True and False by applying a logical NOT to the variable's present value. It works like so:

1. Initially the static variable is set to False. The first time the event handler is called, the variable is assigned NOT False, which is, of course, True.

2. The next time the event handler is called, the variable is assigned NOT True, and so on.

3. Each time the method is called, IsVisible contains the opposite value it had on the previous call. After that, the if/else pair calls either Show or Hide depending on the value of IsVisible.

That's all there is to it. But does it work? Let's find out. Click the Run button on the toolbar. After being compiled, the program runs and is displayed. It's the moment of truth. Click the button, and the memo component is hidden. Click the button again, and the memo component is again displayed. It works!

After playing with that for a minute, close the program (use the Close Program button in the upper-right corner of the title bar) and you are back to the Code Editor.

Before you go on, save the project. Choose File|Save All from the main menu. The first thing you are prompted for is the name of the unit (source file). Type PMEMain and click OK. Next, you are prompted for a filename for the project. Type PMETest and press Enter or click OK.

Using the Visible Property  All that work with a Boolean variable is a bit cumbersome. Think back to the discussion about properties. Wouldn't it be nice if the memo component had a property that could tell us whether the component was currently visible? Is there such a beast? Of course there is. It's called, predictably, Visible. Let's make use of it. Again, edit the event handler until it looks like this:

procedure TPMEForm.ButtonClick(Sender: TObject);

begin

  if Memo.Visible then
    Memo.Hide
  else 
    Memo.Show;
end;

Again click the Run button. The program is displayed and, lo and behold, the button does what it's supposed to. How about that? You managed to use properties, methods, and events in the same example.

Are you getting the fever yet? Hold on, because there's much more to come. Oh, and wipe that silly grin off your face. . .your boss thinks you're working!

The Sender Parameter  As you can see, the ButtonClick method takes a pointer to a TObject called Sender (whether you know it or not, all class variables in Delphi are pointers). Every event handler will have at least a Sender parameter. Depending on the event being handled, the event handler might have one or more additional parameters. For instance, the OnMouseDown event handler looks like this:

procedure TPMEForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
end;

Here you are getting information on the button that was pressed, which keyboard keys were pressed at the time the mouse was clicked, and the x, y coordinate of the cursor when the mouse button was clicked. The event handler contains all the information you need to deal with the particular event the event handler is designed to handle.

So what exactly is Sender? Sender is a pointer to the component that is sending the message to the message handler. In this example, the Sender parameter is extra baggage because you know that the Show/Hide button is the sender. Sender exists to enable you to have more than one component use the same event handler.

To illustrate, let's create a new button and make one of our buttons the Show button and the other the Hide button:

1. If the Code Editor is on top, press F12 to switch back to the Form Editor.
2. Click on the Show/Hide button to select it. Change both the Name and Caption properties to Show.

3. Add a new button to the form to the right of the Show button. Arrange the buttons if you want to give an even look to the form.

4. Change the Name property for the new button to Hide. The Caption property will also change to Hide (you'll have to press Enter before the Caption property will change).

5. Click the Show button and then click on the Events tab in the Object Inspector. Notice that the OnClick event now says ShowClick. Edit it to say ButtonClick again. (The initial event-handler name is a default name. You can change it to any name you like.)

6. Click the Hide button and find the OnClick event in the Object Inspector (it will be selected already). Next to the value is a drop-down arrow button. Click the arrow button and choose ButtonClick from the list that drops down (there will be only one name in the list at this point).

7. Double-click on the value ButtonClick. You are presented with the Code Editor with the cursor in the ButtonClick method. Modify the code so that it reads like this:

procedure TPMEForm.ButtonClick(Sender: TObject)
begin
  if Sender = Hide
    then Memo.Hide
  else 
    Memo.Show;
end;
8. Bake at 425 degrees for one hour or until golden brown. (Just checking.)

Your form will look similar to Figure 5.4. Compile and run the program. Click each button to be sure that it functions as advertised. 

FIGURE 5.4. The form with all components added.

What you have done here is create a single event-handling method that handles the OnClick event of both buttons. You use the Sender parameter to determine which button sent the OnClick event and then either hide or show the memo component as needed. You could have created a separate OnClick handler for each button, but with this method the code is more compact. Besides, it's a good illustration of how Sender can be used.

Note that I am comparing the Sender variable to a component's Name property. Because both are pointers, a comparison is being made to see whether both variables contain the same address.

Step 6 in the previous exercise illustrates an important point: After you create an OnClick event handler for a particular component, you can attach that same handler to the OnClick event of any component on the form. This enables you to use the same event handler for multiple components. I'll discuss events in more detail as you progress through the book.


HOUSE RULES: EVENTS

VCL Explored

The Visual Component Library is a well-designed framework. As with most good frameworks, VCL makes maximum use of inheritance. The bulk of the VCL framework is composed of classes that represent components. Other VCL classes are not related to components. These classes perform housekeeping chores, act as helper classes, and provide some utility services.

The VCL class hierarchy dealing with components is complex. Fortunately, you don't have to know every detail of VCL to begin programming in Delphi. At the top of the VCL chain, you will find TObject. Figure 5.5 shows some of the main base classes and the classes derived from them.


FIGURE 5.5. The VCL class hierarchy.

TObject is the granddaddy of all VCL component classes. (Remember that all classes in Object Pascal are derived from TObject.) Below TObject you see TPersistent. This class deals with a component's capability to save itself to files and to memory as well as other messy details you don't need to know about. I'm thankful (and you should be, too) that you don't need to know much about TPersistent to program most applications in Delphi.

The TComponent class serves as a more direct base class for components. This class provides all the functionality that a basic component requires. Nonvisual components are derived from TComponent itself. Visual components are derived from TControl, which, as you can see in Figure 5.5, is derived from TComponent. TControl provides additional functionality that visual components require. The individual components, then, are derived from either TGraphicControl or TWinControl.

When you drop a component on a form, Delphi creates a pointer to that component in the form's class declaration so that you can access the component in your code. Delphi uses the component's Name property for the pointer variable's name. When you created the sample application earlier, you placed a memo component on the form. At that point Delphi created a TMemo variable and gave it the name Memo.

Similarly, when you created a button on the form, Delphi created a TButton variable to represent the button. Before any of that took place, Delphi had already derived a new class from TForm and, of course, created an instance of that class to represent the form.

Some understanding of the VCL classes and components is obviously necessary before working with VCL. Although I cannot review each and every VCL class, I can hit the high points. Let's take a look at some of the classes that you will use most frequently.

Form and Application Classes

Form and application classes represent forms and the Application object in VCL. These classes are derived from TComponent and indeed are components themselves. They are listed separately to distinguish them from the controls you drop on a form.

The TApplication Class

The TApplication class encapsulates the basic operations of a Windows program. TApplication takes care of responsibilities such as managing the application's icon, providing context help, and doing basic message handling. Every Delphi application has a pointer to the TApplication object called Application. You use the TApplication class primarily to execute message boxes, manage context help, and set hint text for buttons and status bars. TApplication is a bit of an oddity in VCL in that some of its properties (Icon, HelpFile, and Title) can be set via the Application page of the Project Options dialog box.

The TForm Class

The TForm class encapsulates forms in VCL. Forms are used for main windows, dialog boxes, secondary windows, and just about any other window type you can imagine. TForm is a workhorse class in VCL. It has nearly 60 properties, 45 methods, and 20 events. I discussed forms in detail in Day 4.


Component Classes

This group of classes encompasses a wide range of classes and can be further divided into separate categories, which I've done in the following sections.

Standard Component Classes

The standard components are those that encapsulate the most common Windows controls. The standard component classes include TButton, TEdit, TListBox, TMemo, TMainMenu, TScrollBar, TPopupMenu, TCheckBox, TRadioButton, TRadioGroup, TGroupBox, TPanel, and TActionList.

Most of these classes encapsulate a Windows control, so I won't discuss all of them right now. The TMainMenu class encapsulates an application's main menu. At design time, double-clicking the MainMenu component's icon brings up the Menu Designer. TMainMenu has properties that control whether the menu item is grayed out, whether it is checked, the help context ID, the item's hint text, and others. Each menu item has a single event, OnClick, so that you can attach an event handler to a menu item being selected. I'll discuss menus and the Menu Designer in more detail tomorrow.

The TPanel Component  Another standard component of interest is TPanel.

New Term: A panel represents a rectangular region on a form, usually with its own components, that can be treated as a single unit.

The Panel component is a container component. As such it can contain other components. Panels have properties that control what type of edge the panel should have; whether the panel is raised, sunken, or flat; and the width of the border. Combinations of these properties can be used to create a variety of 3D panels.

The TActionList Component  The TActionList component is new to Delphi 4. This component can be used to easily add command enabling to a component or a group of components. For example, an application that uses the Clipboard might have cut, copy, and paste items on a menu, on a toolbar, and on a context menu. If there is data in the Clipboard, the paste button and menu items should be enabled. If there is no data in the Clipboard, the button and menu items should be disabled. All of the controls (the toolbar button and the menu items) can be enabled or disabled at one time using the TActionList component.

Components on the Additional Tab  Delphi has another group of components that I'll throw in with the standard controls. These controls can be found under the Additional tab on the Component palette. The classes representing these components include TBitBtn, TSpeedButton, TMaskEdit, TStringGrid, TDrawGrid, TImage, TShape, TBevel, TScrollBox, TCheckListBox, TSplitter, TStaticText, and TChart. The TBitBtn class represents a button that has an image on it.

TSpeedButton is also a button with an image, but this component is not a true button. Instead, it's a graphical depiction of a button. This enables you to have a large number of speed buttons and not consume Windows resources for each button.


The TImage component enables you to place an image on a form that can then be selected from a file on disk. You can use the TBevel component to create boxes and lines that are raised (bumps) or lowered (dips). Bevels can be used to divide a form into visual regions and to provide an aesthetically pleasing form. The TStringGrid and TDrawGrid classes give you a means to present information in a grid format.

Win32 Custom Control Classes

VCL has component classes that encapsulate many of the Windows 32-bit custom controls. These classes include TListView, TTreeView, TTrackBar, TProgressBar, TTabControl, TPageControl, TRichEdit, TImageList, TStatusBar, TAnimate, TDateTimePicker, TToolBar, TCoolBar, and a few others. Some of these controls are, by nature, complicated, and the VCL classes that represent them are complicated as well. Trust me when I say that VCL does much to ease the burden of working with these common controls. You have to spend some time with these classes before you fully understand them. I cover the TToolBar, TCoolBar, TImageList, and TStatusBar components on Day 13, "Beyond the Basics."

Database Component Classes

VCL has a host of database components that include both visual and nonvisual classes. Nonvisual database components include TDataSource, TDatabase, TTable, and TQuery. These classes encapsulate behind-the-scenes database operations.

Visual database component classes are the part of the VCL database operations that users see and interact with. For instance, a TDBGrid component is used to display a database table in grid format. In this way, the TDBGrid acts as the interface between the user and the database. Through the TDBGrid, the user can view and edit the database table on disk.

The TDBNavigator component provides buttons that enable the user to move through a database table. This class includes buttons for next record, previous record, first record, last record, cancel edit, accept edit, and undo edit.

Other data-aware component classes hook standard Windows controls to database fields. These classes include TDBText, TDBEdit, TDBListBox, and TDBImage, among others.

A group of components usually associated with database programming is the QuickReport components. This group of components makes report writing easy, especially if your data source is a database. The database QuickReport components are discussed on Days 16, 17, and 18.

Common Dialog Classes

As you are no doubt aware, Windows has common dialog boxes for things like opening files, saving files, choosing fonts, and choosing colors. VCL encapsulates these common dialog boxes in classes representing each type. The classes are TOpenDialog, TSaveDialog, TOpenPictureDialog, TSavePictureDialog, TFontDialog, TColorDialog, TPrintDialog, and TPrinterSetupDialog. VCL also adds the TFindDialog and TReplaceDialog classes to this component group. All the components in this group are nonvisual in that they don't have a design-time visual interface. The dialog boxes are visible when displayed at runtime, of course.

System Component Classes

The System tab on the Component palette contains a mixture of visual and nonvisual components. The TTimer class is used to represent a Windows system timer. Its single event is OnTimer, which is called each time the timer fires. The timer 
interval is set through the Interval property. TTimer is a nonvisual component.

Tucked into this group of classes is the TMediaPlayer class. This class enables you to play media files like wave audio, AVI video, and MIDI audio. The media can be played, stopped, paused, or positioned at a particular point in the file, as well as many other operations. This class has many properties and events that greatly simplify the complex world of the Windows Media Control Interface (MCI).

The TPaintBox component gives you an empty canvas on which you can draw anything you like. This component has many potential uses. The System group includes OLE and dynamic data exchange (DDE) classes as well.

The Win 3.1 Group of Components

Don't make the mistake of automatically discarding this component group just because of the name of the tab on which it resides. This group contains some great components. (The Win 3.1 tab has its roots in Delphi 1.) In particular, I like the TTabSet and TNotebook components. This group also includes several component classes that enable you to build your own custom File Open or File Save dialog box. The classes are TFileListBox, TDirectoryListBox, TDriveComboBox, and TFilterComboBox.

Internet Components

Depending on which version of Delphi you have (Standard, Professional, or Client/Server), you might have an Internet tab. This tab contains components used in Internet programming. The components include HTML, FTP, SMTP, POP3, and HTTP components. It also contains components that are for general network programming via the Winsock API. Most of these components are native VCL components, although at least one, the THTML component, is an ActiveX control.

Sample Components

The Samples tab contains components that can be used to gain an understanding of how to write components. The source for these components is provided so that you can see how they work. The sample components include TGauge, TColorButton, TSpinButton, TSpinEdit, TDirectoryOutline, and TCalendar.

ActiveX Controls

The ActiveX tab contains ActiveX controls that you can use in your applications. These controls include Chart FX by Software FX, Inc., Visual Speller by Visual Components, Inc., Formula One Spreadsheet, Formula One VtChart, and a Graph control by 
Bits Per Second, Ltd.

GDI Classes

The GDI (graphics device interface) classes get a lot of work in Windows GUI applications. These classes encapsulate the use of bitmaps, fonts, device contexts (DCs), brushes, and pens. It is through these GDI objects that graphics and text are displayed on a window. The GDI classes are not associated with a specific component, but many components have instances of these classes as properties. For example, an edit control has a property called Font that is an instance of the TFont class.

The term device context is well known by traditional Windows programmers. In VCL, though, the term is not widely used. This is because VCL encapsulates Windows DCs in the TCanvas class. VCL uses the term canvas to refer to a Windows device context. A canvas provides a surface that you can draw on using methods like MoveTo, LineTo, and TextOut. Bitmaps can be displayed on the canvas using the Draw or StretchDraw methods. The concept of a canvas that you draw on makes more sense than the archaic term device context, don't you think? Graphics operations are discussed on Day 12, "Graphics and Multimedia Programming." The following is a list of the more common GDI classes used:

In addition to the GDI classes listed here, there are others that either work as helper classes or extend a base class to provide extra functionality. As you work with Delphi, you will learn more about these classes and how to use them. Figure 5.6 shows the hierarchy of the VCL classes that encapsulate GDI operations.

FIGURE 5.6. VCL GDI class hierarchy.

Utility Classes

VCL contains many utility classes that you can use in your applications. A utility class simplifies some task in Windows programming. For instance, the TIniFile class eases the use of writing and reading Windows configuration files (.INI files). Conventional wisdom has it that the use of .INI files is out and the Registry is in. To aid in Registry operations, VCL has the TRegistry and TRegkeyInfo classes.

The TStringList class enables arrays of strings. TStringList is used by many of the component classes to store strings. For instance, the TMemo class uses a TStringList object for its Lines property. TStringList has the capability to save its list of strings to file or load strings from a file using the LoadFromFile and SaveToFile methods. TStringList can also be used to read and write text files.

Another useful VCL utility class is the TList class. This class enables you to create arrays of any type of object you want. The TList class simply stores a list of pointers. The main advantage of the TList class is that it provides you with an array that will dynamically grow or shrink as new objects are added or removed.

VCL also includes a set of classes to enable reading and writing of streams (a stream is really just a block of data). The TStream, TFileStream, TMemoryStream, and TResourceStream classes all enable you to read or write data to streams. TStream is the base class for all stream classes. The TFileStream class is used when dealing with files on disk, TMemoryStream is used to manipulate data in memory, and TResourceStream is used to load binary resources from EXEs and DLLs. These classes are for more advanced uses, but they are invaluable when you need the particular functionality they provide. For more information on these classes, see the Delphi VCL Help.

And That's Not All . . .

By no means did I cover all the VCL classes here. I did, however, touch on those classes that you are most likely to use in your applications.

Flip back a few pages and take another look at Listing 5.1. I showed you how easy it was to load and display a bitmap with Delphi. Let's do an exercise that proves the point. First, begin a new application. You will be looking at a blank form. Perform the following steps:

1. Change the Caption property of the form to Bitmap Test Program.
2. Click on the Additional tab on the Component palette, choose the Image component, and place the component on the form.

3. Locate the Align property and change it to alClient. The picture component fills the client area of the form.

4. Locate the Stretch property and change it to True.

5. Locate the Picture property and double-click the Value column.

6. The Picture Editor dialog box is displayed. Click the Load button. The File Open dialog box is displayed.

7. Navigate to the \Program Files\Common Files\Borland Shared Files\Images\Splash\256Color directory and choose an image from those presented (I like HANDSHAKE.BMP). Click OK.

8. You are now back to the Image Editor dialog box, and the bitmap you chose is displayed in the preview window. Click OK. (If you want to choose a different bitmap, click the Load button again.) The bitmap now fills the client area of the form.

9. Click the Run button. When the application runs, you can size the window and the bitmap will always fill the client area of the window.

See how easy it is? It would have been even easier if you hadn't bothered to make the image fill the form's client area. Figure 5.7 shows the bitmap test program running.

FIGURE 5.7. The bitmap test program running.

Summary

Today you learned about frameworks and how VCL fits into the framework scene. I discussed properties, methods, and events and gave you some hands-on experience in the process. You finished today with an overview of the VCL classes that you are likely to encounter when programming in Delphi. I didn't cover them all, but I gave you a brief look at the most commonly used classes.

So where is this industry going? The wave of the future appears to be components. The great thing about VCL is that if you decide (or are forced) to switch to C++, you can still use VCL. You might be surprised to learn that the very same VCL that is used in Delphi is also used in Borland C++Builder. Any time you spend learning VCL can be immediately applied to C++ if the time ever comes. It's the same VCL, so there's nothing to relearn.

Workshop

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

Q&A

Q What is a framework?

A A framework, also called a class library, is a set of classes that simplifies Windows programming. A good framework implements object-oriented design and object-oriented programming to apply an object-oriented approach to writing Windows applications.

Q Is VCL a framework?

A Yes, VCL is a framework. VCL is written in Object Pascal and works with both Delphi and C++Builder.
Q It seems as though the component way of doing things is the best approach. Is that true?

A For the most part, yes. VCL is a great choice for applications that use a lot of dialog boxes and for database applications. There's almost nothing that you can't do with Delphi but certainly Delphi shines in the area of RAD.

Q Are properties just class data fields?

A No. Properties are special creatures. Some properties simply set a data field in the class. Other properties, when modified, invoke a method that performs special operations with that property. In these cases, a property does more than just set a class field.

Q Do I have to respond to each and every event a component defines?

A No. You can respond to as many events as appropriate for your application or not respond to any events at all.

Q Are the components on the Win 3.1 obsolete?

A Not at all! These components were especially important in Delphi 1 because Delphi 1 was a 16-bit programming environment (hence the Win 3.1 tab name). That doesn't mean these components are obsolete in Delphi 4, though.

Q There sure are a lot of VCL classes. I thought programming with Delphi was going to be easy.

A Programming with Delphi is much easier than programming Windows with traditional tools. Windows programming, no matter how good the programming tool, requires a lot of experience and knowledge to master. You will master it if you keep at it.

Quiz

1. Are all components visible at design time?
2. Is the OpenDialog component a visual component or a nonvisual component?

3. What is the name of the VCL class that represents a Delphi form?

4. Do all versions of Delphi ship with the same set of components?

5. Are all VCL classes ultimately derived from TObject?

6. Name one nonvisual VCL component.

7. Do all components share certain common properties?

8. Name two common properties that all visual components share.

9. Can two or more components share the same event handler?

10. What is the VCL terminology for a Windows device context? What is the name of the VCL class that encapsulates device contexts?

Exercises

1. Write a paragraph describing how properties and class fields differ.

2. Create a Delphi application that displays a bitmap on the main form when a button is clicked.
3. Create a Delphi application that displays a message box saying Hello, Bubba! when the main form is clicked.

4. Place several components of your choice on a form. Click on a component and use the Object Inspector to study that component's properties. Repeat for each component on the form.

5. Switch to the Events tab and view the events of each component you placed on the form in Step 4.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.