Chapter 5 of Delphi Unleashed 1.0: 16 Bit

By: Charles Calvert

Abstract: In this chapter you will be introduced to graphics and the basic way it is handled inside Delphi.

Chapter 5 of Delphi Unleashed 1.0: 16 Bit

Copyright (c) 1995 by Charlie Calvert

What is a Graphical Environment?

Overview

In this chapter you will be introduced to graphics and the basic way it is handled inside Delphi. Everyone new to Delphi should be sure to go through most of the material in this chapter, as it covers many fundamental points about the way Delphi handles components. In short, the discussion of graphics programming will also demonstrate some basic facts about components and the relationship between components and ObjectPascal.

The goals of this chapter are threefold: [lb] To get you up to speed on Graphical User Interfaces (GUIs) and the basic logic of drawing graphics. [lb] To show you the TShape and TCanvas components, and to explain how Delphi uses them to draw images. [lb] To give you additional experience working with the basic elements of procedures, event handlers, and components.

This chapter starts with several pages of exposition on the topic of Graphical User Environments and graphics mode programming. This is fundamental material that you have to understand if you are going to get serious about Windows programming. If you are an experienced Windows or graphics programmer, you may find that you can skip or skim the first two sections of this chapter.

Graphics and graphics components are part of the basic syntax of the Delphi language. As you will see in the next few chapters, no programmer is equipped to do any serious work without a knowledge of types, branching, and looping. Basic graphics programming is now just as fundamental, and all programmers should understand how it works.

What is a Graphical User Interface?

Windows runs in graphics mode, not text mode. Some people who usually work at the DOS or UNIX prompts might not even be fully aware of the difference between graphics mode and text mode. Furthermore, many people are confused about graphics modes, graphics drivers, palettes, 8-bit versus 16-bit versus 24-bit graphics, and the elemental units of graphics modes, which are pixels.

As a result, it's perhaps best to start at the beginning and explain why people have moved from text mode to graphics mode, even though graphics mode is slower and demands more expensive hardware. The key point to understand is that a traditional text mode screen is divided up into 2,000 individual sections or characters, while a traditional graphics screen is divided up into at least 307,200 sections, or pixels. The figures alone help to explain why graphics mode is more powerful than text mode.

In particular, a text mode screen is usually 80 characters wide and 25 characters deep. Graphics screens, on the other hand, are usually at least 640 pixels wide by 480 pixels deep. See Figures 5.1 and 5.2 for illustrations of the two types of screens.

Figure 5.1A Graphics screens are usually 640 X 480.

Figure 5.1B. Text screens are usually 80 X 25.

Graphics screens have a finer resolution than text screens. This makes it possible to create much more interesting and realistic effects than any that can be produced in text mode. For instance, Figure 5.3 and Figure 5.4 illustrate the differences between a sentence written in text mode, and the same sentence written in graphics mode. Overall, it seems hard to deny that graphics mode offers some obvious advantages for which users would quite naturally be willing to make some sacrifices.

Figure 5.2. A portion of a poem by Samuel Taylor Coleridge, shown in text mode using the standard built in character set.

Figure 5.3. Another excerpt from the same Coleridge poem, but shown in graphics mode using a truetype font.

Figure 5.5 shows a portion of the speedbar from Delphi. Graphics mode makes it possible to produce detailed visual symbols, which can act as clues or guides for users who want to find easy ways to navigate through a multifaceted and diverse interface. In other words, graphics mode can help to simplify the user's approach to an elaborate tool, and can indeed help to make complex interfaces more readily comprehensible. The key point here is that it is the increased resolution, the larger number of individual facets, that makes all this possible.

Figure 5.4. The speedbar from Delphi makes the program's resources more accessible.

Figure 5.6 shows another type of art, the kind that mathematics produces via fractal manipulations. This image was produced by the FRACDLL program, which appears on the disk accompanying this book. That program will be discussed in Chapter 33, "DLL Basics and VCL DLLs."

Figure 5.5. A fractal design created with the FRACDLL program that comes with this book.

What all this adds up to is that graphics modes make it easier for programs to convey information. Nearly everyone in contemporary society desperately needs to be able to work with large blocks of information in the simplest way possible. This is what GUIs are all about. They make information palatable.

When personal computers first came out, it was enough that they were powerful. Users, impressed with the new technology, were willing to learn arcane interfaces, each of which tended to differ from one program to the next. Today's users and today's marketplace demand that programs be easier to use.

For now, the best solution to this need is a well-designed GUI such as Windows or OS/2. Well-designed GUI's shorten learning curves. Furthermore, if it's helpful to draw a graph or picture of a concept, GUIs can easily handle this task. Finally, modern operating environments such as Windows or OS/2 enable programmers to integrate sound and motion pictures into their programs. The latter technology is still in its infancy, but in this book you will write programs that show how easy it is to communicate large amounts of data via a short movie shown on a computer.

Graphics mode offers a very clear demonstration of why users are willing to work with larger and slower codebases if the advantages are obvious and significant. Everything about graphics mode is much slower than text mode, by several orders of magnitude. The hardware itself is slower, and the code that is written to manipulate the graphics is much larger, slower, and more complex. But most users prefer graphics mode to text mode, except when working on certain isolated problems. Furthermore, modern computers run graphics applications more quickly than the old technology could run text-based applications, and this is a situation that will continue to improve exponentially over the years.

Technical Aspects of Graphics Mode

There are a number of points about graphics modes that some users find confusing. The first is the idea of a pixel. A pixel is a single point of light on the screen. On most computers, a pixel is so small that users are not even aware that they exist. They are simply too tiny for the naked eye to perceive, unless they are seen as isolated colored points on a black background. If you want to see individual pixels being turned on one at a time, you can run the PIXELS program that is supplied on the disk. The code for this program will be explained in Chapter 9, "While Statements and Repeat."

If you have a color system, the PIXELS program demonstrates that not only can each individual pixel on the screen be turned on and off individually, it can also be assigned its own color. All of the displays you see in Windows are the result of manipulating these individual points of light.

    Those who remember the Bush administration need to be assured that these are not political points of light, but physical points of light!

The letters VGA stand for Video Graphics Array. The 640[ts]480 VGA mode used on many PCs was a standard developed by IBM back in 1987.

The VGA standard took the computer world by storm because it introduced relatively high resolution graphics shown in 16 easily accessible colors. However, many computer users now want higher resolutions. As a result SuperVGA modes with resolutions of 800[ts]600 and 1024[ts]768 were made popular by a wide variety of companies, not one of which was powerful enough to create a standard.

Furthermore, it has turned out that many computer users want to work with more than the 16 colors produced by 4-bit graphics cards. At the time of this writing, many programs use 256 color 8-bit graphics, and even 24-bit graphics cards that can produce millions of colors.

If you take a moment to do a little math, you can easily see the kind of calculations involved in defining a 1024[ts]768, 8-bit color screen. For instance, 1024 times 768 equals some three quarters of a million pixels, or more precisely 786,432 pixels. Each one of those pixels can be assigned to any one of 256 different colors. Furthermore, 786,432 times 256 equals... Well, never mind[md]you get the idea!

Painting a single screen that operates at this resolution is a task of fantastic complexity. In fact, some PCs can't be run effectively at that level of detail, though many can. Because the 18 month rule states that processors should double in speed every year and a half, it might not be long until everyone has graphics systems at least that powerful.

Graphics mode obviously introduces numerous complexities that make the programmer's job difficult. The coup de grace is delivered when one considers the mess created by the complete lack of standards in this field. For instance, one video card might implement a 1024[ts]768 screen in a particular fashion, while the next card might have a totally different way of implementing the same resolution. The end result is that it can be extremely complicated for programmers to start working with the SuperVGA modes, which are, unfortunately, expected by many sophisticated users.

I'm explaining all of this primarily because it is important that users understand the graphics-related issues encountered when working in Windows. Any single Windows program can end up being run on a very wide variety of graphical systems. In other words, if you write a Windows program and unleash it upon a perhaps unsuspecting public, you have to understand that it could be run one day on a 640[ts]480 black-and-white system, and the next day on a 1024[ts]768 256-color system.

Such a prospect would be entirely too daunting for the vast majority of programmers were it not for the fact that Windows provides methods for taming all this diversity. Specifically, Windows has worked out a system whereby most programmers never write directly to a video card. Instead, they program something called a device context, which is in turn linked into a series of drivers written by video card manufacturers. These drivers always present a uniform interface to Windows, while at the same time implementing device-specific code to manipulate the hardware.

As a Windows programmer, you don't ever directly manipulate the video hardware. Instead, you deal with a stable API that calls functions, which manipulate an abstract entity called a device context. The device context, in turn, talks to a driver written by a hardware manufacturer, and the driver talks to the hardware. This arrangement makes it possible for you to write one set of code that will work on a very large variety of systems.

Once again, the main theme here is that people are willing to put up with bulky code bases and time delays in order to find some way to tame complexity. In this case, it's not the user, but the programmer who is willing to trade the speed of direct hardware access for the benefits to be found in a single stable API.

Of course some programmers, particularly game writers, simply can't stand to not have direct access to the hardware. By optimizing their code for a specific purpose, they feel that they can make their games more lively. However, they can't accomplish this goal unless they are proficient at writing or manipulating a large body of assembly language code, which has to be tuned, retuned, written, and rewritten for each of the 10 or 15 popular video cards that are found on the market.

Furthermore, these game programmers might find that a new video card appears on the scene, which is powerful and inexpensive enough to gain a significant market share. Once again, they have to dig down to the assembler level and start figuring out how to write for this particular card. Windows programmers, on the other hand, simply assume that every version of this card will be distributed with a free Windows video driver. This means that they don't have to change a line of their code. It's ready to run, because the video card's driver fully supports the Windows API.

    A few further points can be made about this issue. One is that many DOS gamers write code that is aimed at a very easy to use, very low resolution video mode, which allows only 320[ts]200 pixels per screen and 256 colors. Their skills are indeed formidable, but their problem is less complex than that presented by Windows, where many different resolutions are expected to be supported by all software.

    On the other side of the coin, not all video drivers written for Windows perform as well as they should. As a result, some of the very best Windows software manufacturers end up writing their own drivers, or at least portions of their own drivers. These extremely sophisticated programs are usually aimed at a narrow market, such as CAD programs or presentation software. Most Windows programmers, however, don't need to worry about the problems faced by these very sophisticated programs that typically make extensive use of very esoteric graphics calls.

    Finally, Microsoft has made some recent attempts to improve the speed of graphics performance under Windows. In particular, they have released the Wing library, Pascal-based translations that are readily available from Compuserve; just type GO DELPHI.

In the contemporary programming world, it seems that one is always trading speed for convenience. What makes it possible is the 18 month rule discussed in Chapter One, "Introduction to Visual Programming." The end result is the need for ever larger and more powerful systems.

In this particular case, you have seen that Windows provides a single API that enables you to tame an array of video cards with a single stroke. Yes, your code would run faster if you wrote directly to video. On the other hand, the work involved in single-handedly writing even a simple non-Windows graphics program that could run on all systems would be prohibitively expensive for most developers. To overcome this problem, Windows programmers can use an API that handles all the graphics-oriented details. Fortunately, Windows provides one of the best available solutions to this need. It's not perfect, but it's very good.

Coordinates and the TPoint and TRect Structure

Delphi uses several different structures to encapsulate basic graphics-oriented programming ideas. If you are going to program in Windows, you have to get a feeling for what these structures are all about.

The TRect type is used to describe the four corners of a rectangle. It does so by assigning values to a point in the upper left corner of the rectangle, and a second point in the bottom right corner of the rectangle, as shown in Figure 5.7. (As you will see in a moment, this is a slight oversimplification, but is nonetheless a valid way to think about a TRect structure.)

Figure 5.6. The top-left and bottom-right corners of a rectangle that is 800 X 600.

The coordinates of the forms used in a Delphi program can be described in terms of the four coordinates of a TRect structure. For now, it's simplest to think of the TopLeft corner of a form as residing at point (0, 0). Given that assumption, the bottom right corner of the rectangle always contains the values associated with the width and height of the form.

This whole subject might be easiest to describe via an analogy. Suppose you own a plot of land 800 yards wide and 600 yards deep. Furthermore, suppose you want to be able to describe various locations on the land as if they were part of a grid, for which each point in the grid can be assigned an x and y value.

To get started, you might assign the upper left (northwest) corner of the land the values Left := 0 and Top := 0. The bottom right corner of the land would then be assigned the values Right = 799 and Bottom = 599. You could then use a TRect structure to describe the entire plot of land thusly: (0, 0, 799, 599).

Suppose you walked 20 yards to the west along the northern boundary of the land. Now, you would be at location (20, 0), with 20 being the x or horizontal coordinate, and 0 being the y or vertical coordinate. Having started on your venture, you bravely move south an additional 10 yards. You are now at location (20, 10).

Having arrived at this spot, you decide that you like the quality of the soil near you, so you plant a flag there to mark the coordinates. This coordinate, at (20, 10), is equivalent to an Object Pascal TPoint record, with the x coordinate set to 20 and the y coordinate set to 10.

The next thing you do is walk an additional 15 yards east and 25 yards south, and then plant a flag at your current position, which is (35, 35). The two flags you have planted now describe a rectangle, which resides inside of the larger 800[ts]600 rectangle that you own (see Figure 5.8). If you wanted, you could now set this area aside as a nice place to build a garden. When referring to the area, you could use a TRect structure (20, 10, 15, 25), in which the last two numbers represent the rectangle's width and height, rather than an actual location on the screen.

Figure 5.7. Using a coordinate system to define a smaller rectangle inside a larger rectangle.

There are two different ways to look at a TRect structure. From one point of view, a TRect is four separate coordinates on a grid: (Left, Top, Right, Bottom). From another point of view, a TRect is two points, one describing the TopLeft corner and the other describing the BottomRight corner: (TopLeft: TPoint, BottomRight: TPoint). I haven't yet covered complex data types such as records, but you will have a chance to read about them later. For those who know about them from prior experience, here's what the declaration for a TPoint looks like:

TPoint = record
  x: Integer;
  y: Integer;
end;

And here's what a TRect looks like:

TRect = record
  case Integer of
    0: (Left, Top, Right, Bottom: Integer);
    1: (TopLeft, BottomRight: TPoint);
end;

    NOTE: The TRect structure is a called a variant record. Variant records can be seen from two different perspectives, depending on your needs. For instance, the previous record can be seen as either four discrete points, or as two TPoint structures. That is, you can see it as (20, 10, 15, 25), or as ((20, 10), (15, 25)). All of this will be explained in more detail later on, but the key point to grasp is that both syntaxes describe the same rectangle, each having its own advantages under certain circumstances.

If you take a moment to consider the matter, you can see that the TRect structure and the coordinate system described earlier can play a crucial role in Windows programming. For instance, if you place a button in a form, it can be viewed as a small rectangle inside of a larger rectangle. In other words, important coordinates in screen real estate are measured just like in the previous farming example.

At this stage, you should once again run the PIXELS.DPR program, which came on the CD accompanying this book. That program paints a series of individual pixels on the screen. That is, it turns on and off the smallest possible units of light that can be painted on a graphics screen. When I say that a typical graphics screen is 640[ts]480 pixels in size, the units I'm referring to are the tiny dots of light you see in the PIXELS program.

    NOTE: If you are not already an experienced programmer, don't bother trying to understand the PIXELS program. Instead, just run it and see what it does. The technical side of the program is explained in Chapter 9, which discusses loops.

Throughout this book, I'm going to make references to screen coordinate systems, and to the pixels that are used to paint a computer screen. These are basic units out of which the visual portion of the Windows environment is constructed. They are small and clumsy to use at times, and they force us all to invest in expensive hardware. Nevertheless, they are part of what Windows is all about.

Simple Graphics Code with TShape

The simplest graphic shape to draw to the screen is a rectangle. Delphi provides three ways to draw Rectangles to the screen; I am going to show two of them in this chapter, and the third method involves writing Windows API code to manipulate the current window's device context. The first method for drawing rectangles utilizes the TShape component, and the second uses the TCanvas object.

    NOTE: I sometimes use the words object and component interchangeably. This is technically correct because all Delphi components are objects by definition. Some objects, however, are not components. That is, they cannot be automatically placed on the Component Palette. TCanvas is such an object. You will always use code to manipulate TCanvas and any other object that is not instantiated as a component. If you want to start manipulating it as a visual object, you have to make at least a few minor changes to the way it operates so that it can become a component. As it happens, the TShape component really is little more than a thin wrapping around one facet of the TCanvas object.

The TShape component resides on the Additional page of the Component Palette. If you click on its icon and drop it onto a form, you have created a rectangle. Use the mouse to resize it so that it looks like the graphic shown in Figure 5.9. Notice that to reshape the component, you must first click on it, and then select one of the black tabs on the edges of the component. Remember to hold the left mouse button down while you are reshaping the object.

Figure 5.8. The RECT1 program shows how to use the TShape component.

If you look at the Object Inspector, you see that a TShape object has 15 visible properties. Most of these properties are discussed in the next few pages.

The size of the TShape object is defined by designating coordinates in the Left, Top, Height, and Width properties. The coordinates are stated in terms of the form in which they are located, not in terms of the entire screen. This is like the earlier farming example. You don't describe a particular place on a farm by referring to its distance from the edges of the country in which it resides. Instead, you say that such and such a plot is X number of yards from the edges of the farm's property. In the same way, you don't normally refer to a rectangle on a form in terms of its location on the entire screen, but only in terms of its location relative to the form on which it resides.

    NOTE: The distinction between viewing coordinates in terms of another window, or in terms of the entire screen, is technically referred to as the difference between Client coordinates and Screen coordinates. There are Windows functions such as ScreenToClient and ClientToScreen, as well as GetWindowRect and GetClientRect. These functions enable you to move back and forth between the two different coordinate systems. However, for now, we are working only with fundamental Delphi properties, so you should think primarily in terms of Client coordinates, which are calculated relative to the window in which the object resides. The only Delphi object that regularly uses Screen coordinates is the Form itself, which calculates its Left and Top coordinates in terms of the entire screen. This may sound more complicated at first, but the concepts involved are really very simple. The form's Left and Top coordinates describe the distance from the upper left corner of the form to the upper left corner of the screen. The Top and Left coordinates of objects dropped onto a form are described in terms of their distance from the upper left corner of the form.

Here are the four coordinates used to define the location of the TShape object:

  • Left: Locates the left side of the rectangle, relative to the left side of the form on which it resides.
  • Top: Shows how far the top of the shape is from the top of the form.
  • Width: Shows how wide the form is, that is, how far the right side of the rectangle is from the left side of the rectangle.
  • Height: Defines the height of the rectangle. Again, this is not the distance from the top of the screen, nor from the top of the form, but the distance from the top of the rectangle!

    NOTE: When making advanced calculations about the height and width of a form, you sometimes need to take the border and caption of the form into account. You will see an example of this in just a few more pages. However, the ClientWidth and ClientHeight properties also provide information that takes things such as borders and captions into account.

While in design mode, try dragging the shape with the mouse and watching the Object Inspector at the same time. You see that the Left, Top, Width, and Height properties change dynamically when you reshape or move the component.

If you want, you can directly type in the coordinates of the TShape component in the Object Inspector. If you want the component to be exactly 100 pixels in width, simply type the number 100 in the Width column.

    NOTE: Advanced Windows programmers know that it is possible to change the size of the units used to measure the location of objects. That is, it's possible to change the mapping mode used to calculate screen coordinates. This topic is not germane to the current discussion, and for now these advanced programmers should always assume that the current mapping mode is mm_Text.

This section has given you a very basic overview of how to draw rectangles using the TShape component. In the next few sections, you will see more details about the best way to handle this process. At the same time, you will be learning many rules that apply to the basic manipulation of components.

Changing the Shape of a Component at Runtime

If you want to change the shape of a component at runtime, you can do so by manipulating the relevant properties. For instance, there is no practical difference between entering 100 in the Width property of the Object Inspector at design time, and executing the following code at runtime:

Shape1.Width := 100; 

The RECT2 example program on your disk shows how to enter the coordinates of a shape object into four edit controls, and then assign those values to a shape object at runtime. To build the RECT2 project, place a TPanel and a TShape object on a form. Place four edit controls on the panel, as shown in Figure 5.10. There are a number of tricks that you can use to help arrange these controls, and I will explain them to you soon.

Figure 5.9. The RECT2 program enables you to change the shape of an object at runtime.

After you have placed the controls on the form, select all four of the edit components by holding down the shift key and clicking once on each of the components. As you click on each control, you should see little boxes appear at its corners, thereby signifying that it is selected. When all of the controls are selected, you can let up on the shift key. Select the Text property from the Object Inspector, and type in the number 100. The number should simultaneously appear in all four edit controls. Now select the Events page from the Object Inspector, and create a method for the OnExit event of the controls. Fill in the method so that it looks like this:

procedure TForm1.Edit1Exit(Sender: TObject);
begin
  Shape1.Left := StrToInt(Edit1.Text);
  Shape1.Top := StrToInt(Edit2.Text);
  Shape1.Width := StrToInt(Edit3.Text);
  Shape1.Height := StrToInt(Edit4.Text);
end;

Some of the steps you just completed may need some explanation. The key point to grasp is that you have found a way to highlight a group of controls and perform the same action on each one. For instance, you typed in the number 100 in the Object Inspector, and all the edit controls changed at once because they were all selected. Furthermore, you found that you could easily associate one method with all four edit controls. Whenever an OnExit event is generated by any of these controls, you can now be sure that the method you have created will be called.

    NOTE: This technique only works if all the controls you selected share the same property. For instance, if you select both a label control and an edit control, you cannot change the text associated with either one, because one uses the Caption property to control its text, and the other uses the Text property.

    You've seen that you can select multiple controls by holding down the shift key and clicking on each control. You can perform the same action by clicking on the form next to a control, and then dragging a box around a set of controls. To test this, drop two or three label controls on the main part of this form and select them all using the mouse. To perform the same actions for a control placed on a panel, hold down the Ctrl key while you are clicking and dragging.

OnExit events are generated when the user leaves a particular control. It's not enough just to switch between one application and another application; instead, you must leave the edit control for another control on the same form. Typically this is done with a mouse click or a key stroke. For instance, if you run this program, you can generate an OnExit event by pressing the Tab key, or by holding down the Shift key and pressing the Tab key. The first method moves you to the next edit component, and the second method moves you to the previous edit component.

    NOTE: Remember that you can change the Tab Order of items on a form by selecting the Edit | Tab Order menu choice. Note that if you want to change the tab order of items on a panel, you must first select the panel, and then open the Tab Order dialog.

The code in the method shown previously goes out to the edit controls and grabs the information stored in them, and then assigns these values to the coordinates of the TShape control. For instance, you could use this technique to assign the controls the following coordinates: (20, 50, 380, 200). Every time you move on to a new edit control, an OnExit event is generated, and the changes you have made take effect dynamically.

In this section, you have seen how to control the size of a component at runtime. In the process, you have also learned a great deal about the way components act, and how you can configure them at design time. One of the points you should be grasping by now is that visual controls are easy to use, but nonetheless, there are quite a few subtle tricks that can be mastered in order to optimize their use. In particular, you have seen that you can select multiple components, perform one action, and have it affect multiple parts of your program.

    To see why properties function as they do, you need to understand something about what the developers were trying to accomplish when they built the product. As you already know, every component that you manipulate inside Delphi is really just an object. When the developers of Delphi were designing the product they decided that there should be little or no difference between objects and the things they represent. In other words, if you are working with a button object, the developers wanted you to feel that the code encapsulating and representing the button was the same thing as the button itself. Hence, they created properties.

    If you change the Width and Height properties of a button, the button changes its width and height immediately. There is no need for you to change the Width and then tell the object to redraw itself, although this is exactly what you have to do if you are dealing with raw data. Properties are more than a simple representation of data. Instead, they make the data come alive. Change the Width field of an object, and the object changes shape right before your eyes. Properties give you the illusion that you are dealing with a real object, and not with a code-based representation of an object.

    When you understand why properties exist, it becomes obvious that they are not directly connected to the Object Inspector. Rather, the Object Inspector is merely a convenient way to help you use properties at design time. There is, however, no reason at all that you cannot change properties at runtime; in fact, this is a very common thing to do, especially if you have a complex interface that can respond to the user's needs.

The Align, Size, and Alignment Palette Dialogs

In the RECT2 program, you had to place four edit controls on a panel, then choose their size and alignment. A simple way to expedite this matter is to select all four edit controls, and then choose the Size or Align option from the Edit menu. Note that the ellipses after the Size and Align options mean that a dialog is associated with that menu choice. The Size dialog is shown in Figure 5.11, and the Align dialog is shown in Figure 5.12.

Figure 5.10. The Size dialog enables you to easily coordinate the sizes of multiple controls.

Figure 5.11. The Align dialog enables you to arrange multiple components on the form in an orderly manner.

When you are working with the Size Dialog, the best way to proceed is to first size one of the edit controls individually. For instance, you might give the first control a height of 30 and a width of 40. Then, select all the controls, open the size dialog, and choose Grow to Largest for both the width and height. When you click the OK button, you find that all the controls are now the same size.

Obviously, there are a number of different permutations you can run on this sequence. I leave it to you to experiment with the controls to find the method that best suits your needs. Remember that if you have further questions about the Size Dialog, you can turn to the on-line help.

The Align Dialog works in a manner very similar to the Size Dialog. To use it, first select all four edit controls, then choose Space Equal from the Horizontal group box. Now open the dialog a second time, and choose Center in Window from both the Horizontal and Vertical groups. Once again, there are a number of variations you can run on this system, but I leave it up to you to experiment with the dialog, and to look up certain specific issues in the on-line help.

    Delphi also lets you align components using the Alignment Palette from the View menu. Unlike the Alignment Dialog, the Alignment Palette stays open on your desktop while you are working with a form. The Alignment Palette however, has a number of less than purely intuitive icons on it. Most readers will probably need to spend a little time with the on-line help before they know how to use each of its options.

Colors, Scrollbars, and Shapes

The TShape object can do more than simply draw a white rectangle on the screen. For instance, if you pull down the list associated with the Shape property in the Object Inspector, you see that you can easily work with ellipses, circles, squares, and other assorted shapes. Furthermore, if you expand the Brush property, you can change the shape's color. The pen property enables you to change the width and color of the outline of a TShape object.

    NOTE: Don't forget that you can expand properties that have a plus sign (+) next to them by double-clicking on the property's name. A Color property always has a dialog associated with it. To bring up the dialog, double-click the area to the right of the Color property. Select a color from the dialog, click the Ok button, and the color you chose automatically takes effect.

As just described, it's trivial to change the major characteristics of a TShape object at design time. However, to make the same changes at runtime takes a little more work. The SHAPEDEM and SHAPEDEM2 programs on your disk show you how to proceed.

At its core, the SHAPEDEM program consists of nothing more than a TShape object placed on a form, along with two scroll bars and a few buttons. What's interesting about the program is the ease with which you can change the size, color, and shape of the TShape object at runtime.

Next, you find the code for the program in Listings 5.1 and 5.2. Remember that if you want to view the source for the DPR file in your projects, you can select the View | Project Source menu item.

Listing 5.1. The code for SHAPEDEM.DPR.

program Shapedem;

uses
  Forms,
  Main in 'MAIN.PAS' {Form1};

begin
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Listing 5.2. The code for the main unit in SHAPEDEM.DPR.
unit Main;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: SHAPEDEM }

interface

uses
  WinTypes, WinProcs, Classes,
  Graphics, Forms, Controls,
  StdCtrls, Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Shape1: TShape;
    ComboBox1: TComboBox;
    ShapeColor: TButton;
    ColorDialog1: TColorDialog;
    FormColor: TButton;
    ScrollBar1: TScrollBar;
    ScrollBar2: TScrollBar;
    procedure ComboBox1Click(Sender: TObject);
    procedure ShapeColorClick(Sender: TObject);
    procedure FormColorClick(Sender: TObject);
    procedure ScrollBar2Change(Sender: TObject);
    procedure ScrollBar1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ComboBox1Click(Sender: TObject);
begin
  Shape1.Shape := TShapeType(ComboBox1.ItemIndex);
end;

procedure TForm1.ShapeColorClick(Sender: TObject);
begin
  if ColorDialog1.Execute then
    Shape1.Brush.Color := ColorDialog1.Color;
end;

procedure TForm1.FormColorClick(Sender: TObject);
begin
  if ColorDialog1.Execute then
    Form1.Color := ColorDialog1.Color;
end;

procedure TForm1.ScrollBar2Change(Sender: TObject);
begin
  Shape1.Height := ScrollBar2.Position * 2;
end;

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  Shape1.Width := ScrollBar1.Position * 3;
end;

end.

In the next few paragraphs, you'll learn how to change the color of the form, the shape shown on the form, and the size and shape of the object itself.

When you run the SHAPEDEM program, it looks like the graphic shown in Figure 5.13. Use the program's scrollbars to change the size of the figure in the middle of the screen. Use the combobox to select a new shape for the object, and use the buttons to bring up a dialog that enables you to change the color of either the form or the shape.

Figure 5.12. You can use the scrollbars and buttons to change the appearance of the SHAPEDEM program's form.

To create the program yourself, start by dropping down a button and a TColorDialog. The TColorDialog is found on the Dialogs page of the Component Palette. Now change the name on the button so that it reads FormColor. Double-click the button to create a method in the editor that looks like this:

procedure TForm1.FormColorClick(Sender: TObject);
begin
  if ColorDialog1.Execute then
    Form1.Color := ColorDialog1.Color;
end;

When you run the program, the code shown here pops up the ColorDialog, as shown in Figure 5.14.

Figure 5.13. The ColorDialog gives the user an easy way to select a valid color at runtime.

If the user clicks the OK button in the form, the following line of code is executed:

Form1.Color := ColorDialog1.Color; 

This line of code sets the Color property for Form1 to the color that was selected by the user inside of ColorDialog1.

The technique just shown can be used to change the color of the TShape object. All you need to do is drop down a TShape object from the additional page, then drop down a button and change its name to ShapeColor. Double-click on the button and create a method that looks like this:

procedure TForm1.ShapeColorClick(Sender: TObject);
begin
  if ColorDialog1.Execute then
    Shape1.Brush.Color := ColorDialog1.Color;
end;

What could be simpler?

Notice that the code written here is all but self-documenting. Anyone with even the slightest acquaintance with programming can just glance at this procedure and determine what it does.

You should now run the SHAPEDEM program so that you can see how easy it is to change the color of the elements on the form. Of course, you don't have to give the user the exclusive right to control all the elements of your program. Sometimes you can take the initiative. For instance, you could change the color of your form, or of an element on your form, in order to focus the user's attention on a particular part of the screen.

It makes sense that it should not be very difficult to change the color of an object found on one of the forms you create. But, using scrollbars to change its shape at least appears to be a more difficult task. In fact, experienced Windows programmers know that using scrollbars in a Windows program can be a fairly difficult task, requiring you to trap a number of messages in a complex case statement. Delphi, however, reduces the entire task of responding to a scrollbar to a single line of code.

To get started, first drop two scrollbars on the screen and set the Kind property of one of them to sbHorizontal, and the Kind property of the other to sbVertical. Now, turn to the events page of the Object Inspector, and create a method for the OnChange property of each scrollbar. Fill in the methods with two lines of code so that they look like this:

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  Shape1.Width := ScrollBar1.Position * 3;
end;

procedure TForm1.ScrollBar2Change(Sender: TObject);
begin
  Shape1.Height := ScrollBar2.Position * 2;
end;

The code shown here sets the Width and Height of the TShape object to the current position of the thumb on the scrollbar. For aesthetics reasons, I've opted to multiply the Position property by either 2 or 3, so that the shape can range over a fairly large portion of the form. The key point, however, is that it is extremely easy to write Delphi code that performs a task, which would be relatively complex to execute if you had to work directly with the Windows API. Delphi always makes you feel as though you are working directly with an object, and tries to hide the complexities that are introduced by the Windows API.

    The need to multiply the scrollbar position by either 2 or 3 is not entirely satisfying. As a result, I've implemented some more-complex code in the second version of this program that shows a better way to handle this problem.

The final part of the SHAPEDEM program is a bit more challenging than the first two parts. It is also considerably more illuminating, and shows a good deal about the power of Delphi's language.

If you create the SHAPEDEM program from scratch, you need to type in the names of the shapes that appear in the combobox. For instance, you need to manually type in the words stCircle and stSquare. These are the same names you see listed in the Object Inspector for the Shape1 object under the property called Shape. In other words, if you highlight Shape1 on the form and look at the shape property in the Object Inspector, you find a list of the possible shapes that can be associated with this object. These are the same shapes you should list in the combobox.

As I said earlier, it's not possible to get access to the Object Inspector at runtime. As a result, you need to first drop down a combo box and then manually type these names into the Items property for the combobox that is located on your form. To get started, first highlight the combobox on the form by clicking on it. Then, double-click on the right side of the Items property in the Object Inspector. This pops up a String List Editor, as shown in Figure 5.15.

Figure 5.14. The String List Editor enables you to type in a set of default names that appear in a combo box.

The actual items that you type into the string list editor are shown next. Be sure to type them in exactly as shown, and in the identical order.

  • stRectangle
  • stSquare
  • stRoundRect
  • stRoundSquare
  • stEllipse
  • stCircle

You can find these words listed in the on-line help, under the listing for TShapeType. Or, if you want to go back to the original source code, you find the following enumerated type:

TShapeType = (stRectangle, stSquare, stRoundRect,
              stRoundSquare, stEllipse, stCircle);

    You can also access the names of the members of enumerated type by using the GetEnumName and GetEnumValue functions from the TypeInfo unit.

There is always code that stands behind the object you see in a Delphi program. Because you are a Delphi programmer, you have complete access to this code. Nothing is hidden from you. This is why you can change nearly every aspect of your program at runtime.

In this particular case, you need to write only one line of code, which should be executed in response to a click on the combo box by the user. To create the code, first highlight the combo box on the form, and then select the Events Page in the Object Inspector. Now find the OnClick method in the Object Inspector and double-click the area to its right. Using this technique, create a method that looks like this:

procedure TForm1.ComboBox1Click(Sender: TObject);
begin
  Shape1.Shape := TShapeType(ComboBox1.ItemIndex);
end;

This line of code sets the Shape1.Shape property to the shape that the user has selected in the combo box. The code works because of the correspondence between the ordinal members of an enumerated type and the numerical value of the various items in a combobox. In other words, the first element in an enumerated type has the value zero, as does the first item shown in a combo box. For now, that's all you need to know. This topic will be taken up again, however, at the end of Chapter 8, "Branching and Integers," where you will get a chance to examine enumerated types in detail.

When you revisit this program in Chapter 8, you will see that there are several ways that it can be improved. To see these improvements immediately, you can run the SHAPDEM2 program, which is stored with the other programs from Chapter 5, "Pascal Building Blocks," and Chapter 8, "Branching and Integers." However, you should probably ignore the code for this program right now, as it involves a number of concepts most readers have not yet seen.

Using the RGB Function

Whenever a TShape component is painted, its interior and border are drawn in particular, predefined colors. By default, these colors are white and black, respectively. More specifically, the interior of the ellipse is filled with the color of the currently selected Brush. You can change this color by making an assignment of the following type:

Shape1.Brush.Color := MyNewColor. 

The RGBSHAPE program on your disk shows how you can get very specific control over the colors of an object that you paint to the screen. The letters RGB stand for red, green, and blue; each of the these colors makes up one of the colors passed to the RGB function itself:

function RGB(R: Byte; G: Byte; B: Byte): LongInt; 

The parameters passed to this function describe an intensity to be assigned to one of these three colors. These numbers always exist within a range between 0 and 255.

If you pass the RGB function the following parameters, it will return a long integer representing the color red:

var
  Red: LongInt;
begin
  Red := RGB(255, 0, 0);
  Shape1.Brush.Color := Red;
end;

Here's how you get the colors green and blue:

Green := RGB(0, 255, 0);
Blue := RGB(0, 0, 255);

If you combine these three colors in various ways, you can produce particular shades. For instance, if you drop a button into a form and respond to a click the button with the following code, you draw a bright yellow ellipse on the screen:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Shape1.Brush.Color := RGB(255, 255, 0);
  Shape1.Shape := stEllipse;
end;

To achieve the color gray, pass in the following parameters:

Gray := RGB(127, 127, 127); 

To get a fleshlike color, enter:

Skin := RGB(255, 127, 127); 

You see how it works. Remember that the first parameter controls the amount of red in the final color, the second the amount of green, and the third the amount of blue. RGB: red, green, blue!

The RGBSHAPE program has a TShape component, three labels, three scrollbars, and three edit controls. Figure 5.16 shows what the program looks like in design mode.

***05MBP16***

Figure 5.16. The RGBSHAPE program enables you to get a fine-tuned adjustment to the colors in a component.

The RGBSHAPE program has only one method:

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  Shape1.Brush.Color := RGB(Scrollbar1.Position,
                      Scrollbar2.Position,
                      Scrollbar3.Position);
  Edit1.Text := IntToStr(Scrollbar1.Position);
  Edit2.Text := IntToStr(Scrollbar2.Position);
  Edit3.Text := IntToStr(Scrollbar3.Position);
end;

This method first uses the current positions of the scrollbars to assign a color the TShape objects brush. To make this work correctly, I set the Max property for each scrollbar to 255. When the color has been drawn on the screen, I show the actual numbers passed to the scrollbar in the edit components.

The point of the RGB program is to give you a graphical representation of the way the RGB function works. You might also find that this program helps you choose colors that you want to use in your own programs.

NOTE: When working with the RGBSHAPE program, some users may find that Windows cannot create pure tones for some colors, but instead creates a kind of patchwork that approximates the shade described by the parameters passed to the RGB function. However, you can generally get pure tones if you set each of the parameters to 0, 128, or 255. Numbers halfway between 0 and 128 also usually produce pure tones. Of course, the actual results you see depend on whether you are using a 16-color card, 256-color card, or some video card that offers many thousands of colors.

Listing 5.3. The code for the main unit in the RGBSHAPE program.

unit Main;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: RGBSHAPE }

interface

uses
  WinTypes, WinProcs, Classes,
  Graphics, Forms, Controls,
  StdCtrls, SysUtils, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    ScrollBar1: TScrollBar;
    ScrollBar2: TScrollBar;
    ScrollBar3: TScrollBar;
    Shape1: TShape;
    Red: TLabel;
    Green: TLabel;
    Blue: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure ScrollBar1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  Shape1.Brush.Color := RGB(Scrollbar1.Position,
                      Scrollbar2.Position,
                      Scrollbar3.Position);
  Edit1.Text := IntToStr(Scrollbar1.Position);
  Edit2.Text := IntToStr(Scrollbar2.Position);
  Edit3.Text := IntToStr(Scrollbar3.Position);
end;

end.

Using TCanvas to Draw Shapes

As I said earlier, the TShape object is not the only technique for drawing shapes on the screen. At times it is more convenient to simply draw them with the TCanvas object.

TCanvas provides many different ways to draw shapes, but perhaps the most important are the Ellipse and Rectangle procedures:

procedure Ellipse(X1, Y1, X2, Y2: LongInt);
procedure Rectangle(X1, Y1, X2, Y2: LongInt);

Both of these methods enable you to draw a shape to the screen by simply writing code like this:

Canvas.Rectangle(10, 10, 100,  100); 

This code draws a rectangle to the screen with the Left and Top points set to 10 and 10. The next two numbers, 100 and 100, do not designate that the rectangle has a width and height of 100. Instead, these numbers state that the rectangle's right and bottom points are 100 pixels distant from the left and top parts of the form. As a result, it is often simplest to write code that looks like this:

var
  X,Y: Integer;
begin
  X := 10;
  Y := 10;
  Canvas.Rectangle(X, Y, X + 20, Y + 10);
end;

The preceding code designates that the rectangle should have a width of 20 and a height of 10.

You can draw ellipses the same way you draw rectangles:

var
  X,Y: Integer;
begin
  X := 10;
  Y := 10;
  Canvas.Ellipse(X, Y, X + 20, Y + 10);
end;

The code shown here draws an ellipse with a bounding rectangle exactly the same size as the rectangle shown in the previous example. The graphic shown in Figure 5.17 demonstrates how you can use a rectangle to define the shape of an ellipse.

Figure 5.16. The Ellipse procedure draws a rounded figure that fits exactly inside a bounding rectangle.

If you want to change the color of a shape that you draw to the screen, you can do so with the Brush property of a TCanvas object:

Canvas.Brush.Color := clRed; 

TCanvas.Brush is of type TColor. The constant clRed is one of many colors that can be taken on by the TColor type. Here are a few other available types:

clAqua clBlack clBlue clDkGray clFuchsia clGray
clLime clLtGray clMaroon clNavy clOlive clPurple
clRed clSilver clTeal clWhite clYellow. clGreen

All of these colors represent particular results that are returned from the RGB function. For instance, clRed is the same color you get if you pass RGB the parameters 255, 0, and 0.

The CANSHAPE program found on your disk demonstrates a few simple ideas about using the TCanvas object to draw shapes to the screen. The basic idea of the program is to enable you to click on a portion of the screen and see a small circle appear to mark the location of the click.

The main function for the program occurs in response to an OnMouseDown event. To create the event, first select the form, then choose the events page from the Object Inspector. Now double-click on the area to the right of the OnMouseDown event and create the following function:

procedure TForm1.FormMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Canvas.Brush.Color := ShapeColor;
  Canvas.Ellipse(X, Y, X + 10, Y + 10);
end;

The FormMouseDown function draws a small ellipse at the coordinates logged each time a mouse click occurs. The actual coordinates that you use are automatically passed to you in the X and Y parameters of the FormMouseDown method. As you can see, the actual width and height of the ellipse is set to 10, which is a relatively small value on screens that are usually somewhere between 600 and 1100 pixels in width.

The CANSHAPE program uses a simple menu to enable you to choose the color of the circles you place on a screen. Delphi makes it very easy for you to create menus. To get started, select a menu from the Standard page of the Component Palette, drop it on a form, and then double click on it to bring up the Menu Designer, which is shown in Figure 5.18.

Figure 5.17. The Menu Designer enables you to create menus with just a few short moments of work.

To create a menu item, simply begin typing and you see that your words appear in the menu itself, and also in the Caption property in the Object Inspector. By using either the mouse or the arrow keys, you can move down into a popup menu or further to the right. The menu for this program has the word ShapeColor on the menubar itself, and a popup menu beneath, with the words Red, Blue, and Green on it (as shown in Figure 5.19). When typing in the words, press Enter after each.

Figure 5.18. The menu for the CANSHAPE program.

If at design time you double-click on any of the menu items listed above, an associated method is created, just as a method is created when you double-click on a button. Furthermore, if you turn to the Events page in the Object Inspector, you can see that the previous technique creates a method for the OnClick event, which is exactly the same thing that happens when you double-click on the button.

The actual methods you create should look like this:

procedure TForm1.Red1Click(Sender: TObject);
begin
  FShapeColor := clRed;
end;

procedure TForm1.Blue1Click(Sender: TObject);
begin
  FShapeColor := clBlue;
end;

procedure TForm1.Green1Click(Sender: TObject);
begin
  FShapeColor := clGreen;
end;

FShapeColor is a global variable that you must declare yourself, which you should place in the declaration for the form object:

TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    Shape1: TMenuItem;
    Red1: TMenuItem;
    Blue1: TMenuItem;
    Green1: TMenuItem;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Red1Click(Sender: TObject);
    procedure Blue1Click(Sender: TObject);
    procedure Green1Click(Sender: TObject);
  private
    FShapeColor: TColor;
  public
    { Public declarations }
  end;

For now, you should not worry about the declaration made here, as the whole subject of variables will be explained in depth over the next few chapters.

The key point to grasp about the CANSHAPE program is that it enables you to choose colors from a menu, and to then draw a small circle with that color at a particular location on the screen. The program serves as a brief introduction to the Canvas property, and to the basic steps involved in creating and using a menu.

Listing 5.4. The code for the main unit in the CANSHAPE program.

unit Main;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: CANSHAPE }

interface

uses
  WinTypes, WinProcs, Classes,
  Graphics, Forms, Controls,
  Menus;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    Shape1: TMenuItem;
    Red1: TMenuItem;
    Blue1: TMenuItem;
    Green1: TMenuItem;
    About1: TMenuItem;
    About2: TMenuItem;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Red1Click(Sender: TObject);
    procedure Blue1Click(Sender: TObject);
    procedure Green1Click(Sender: TObject);
    procedure About2Click(Sender: TObject);
  private
    FShapeColor: TColor;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  About;

{$R *.DFM}

procedure TForm1.FormMouseDown(Sender: TObject;
                               Button: TMouseButton;
                               Shift: TShiftState;
                               X, Y: Integer);
begin
  Canvas.Brush.Color := FShapeColor;
  Canvas.Ellipse(X, Y, X + 10, Y + 10);
end;


procedure TForm1.Red1Click(Sender: TObject);
begin
  FShapeColor := clRed;
end;

procedure TForm1.Blue1Click(Sender: TObject);
begin
  FShapeColor := clBlue;
end;

procedure TForm1.Green1Click(Sender: TObject);
begin
  FShapeColor := clGreen;
end;

procedure TForm1.About2Click(Sender: TObject);
begin
  AboutBox.ShowModal;
end;

end.

Listing 5.5. The code for the About box in the CANSHAPE program.

unit About;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: CANSHAPE }

interface

uses 
  WinTypes, WinProcs, Classes, 
  Graphics, Forms, Controls, 
  StdCtrls,  Buttons, ExtCtrls;

type
  TAboutBox = class(TForm)
    Panel1: TPanel;
    OKButton: TBitBtn;
    ProgramIcon: TImage;
    ProductName: TLabel;
    Version: TLabel;
    Copyright: TLabel;
    Comments: TLabel;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  AboutBox: TAboutBox;

implementation

{$R *.DFM}

end.

As a final touch to round out this chapter, I have added an about box to the CANSHAPE program. The actual dialog used in shown in Figure 5.20.

Figure 5.19. The About box for the CANSHAPE program is a variation on the dialog stored in the Gallery.

To create the about box, first select the Options | Environment | Preferences dialog from the menu, and then make sure that Gallery: Use on New Form has an X in its checkbox. Close the Environment dialog and select New Form from the Speedbar. Select About box from the Gallery dialog, as shown in Figure 5.21.

Figure 5.20. The Gallery dialog contains some default forms, and enables you to add your own forms if you wish.

    NOTE: You can add new dialogs to the Gallery by right-clicking on any form and selecting Save as template. You can also save entire project to the Gallery by right-clicking on the Project Manager. The Gallery will be discussed in more depth in Chapter 19, "Creating DLLs." However, I introduce it here briefly so that you will be aware of its existence.

After you have selected the About box dialog from the Gallery, you can modify it as you would any other dialog. In particular, you might want to add the name of the dialog, a copyright notice, and a brief comment on what the program does.

Save the dialog into the CANSHAPE directory, under the name ABOUT.PAS. Go to the top of the MAIN.PAS unit and add a uses clause that references the unit:

...
var
  Form1: TForm1;

implementation

uses
  About;
...

In this particular case, it doesn't matter whether you add the reference to unit About in the long uses clause at the top of the program, or if you add it in the implementation, as shown previously.

    The location of an item in a uses clause becomes important only when you are working with complicated programs, in which one unit might reference another unit, which might in turn reference the first unit. This is called a circular unit reference, and can usually be resolved by moving the relevant portions of the uses clause in one unit from the interface to the implementation.

After creating the uses clause, you should add a Help | About reference to the menu, and then double-click on the menu item so you can add the following line of code:

procedure TForm1.About2Click(Sender: TObject);
begin
  AboutBox.ShowModal;
end;

The subject of modal dialogs will be introduced again later in the book, but for now you should simply note that the code shown here pops up the about dialog, and terminates only after the user closes the dialog. This means the user cannot use any other portion of the program while the dialog is visible. Dialogs that act this way are called modal dialogs, and will be discussed at the beginning of Section III.

Opening Forms as Text Objects

Delphi supports Two Way tools, which means that you can open a form as either a visible object, or as a text object. You might want to open a form as a text object if you need to edit it for internationalization purposes, or if you want to save it into an archive.

If you want to view the main form for the CANSHAPE program as a text object, go to the File | Open File menu choice and pop up the Open File dialog. At the bottom left corner of the dialog, there is a combobox that enables you to select Form Files, as shown in figure 5.22. Choose this option, and then open MAIN.DFM. If Delphi asks you whether you want to save and close the Form Designer, choose Yes. You then see the text shown in Listing 5.6. This text describes the main form as code. You can edit this text if you wish; when you close the text view of the form, the changes you have made will be visible.

Figure 5.21. The Open dialog enables you to select either files with a Pas extension, or files with a DFM extension.

Listing 5.6. The main form for the CANSHAPE program, when viewed as text.

object Form1: TForm1
  Left = 154
  Top = 130
  Width = 435
  Height = 281
  Caption = 'Form1'
  Menu = MainMenu1
  PixelsPerInch = 96
  OnMouseDown = FormMouseDown
  object MainMenu1: TMainMenu
    object Shape1: TMenuItem
      Caption = 'ShapeColor'
      object Red1: TMenuItem
        Caption = 'Red'
        OnClick = Red1Click
      end
      object Blue1: TMenuItem
        Caption = 'Blue'
        OnClick = Blue1Click
      end
      object Green1: TMenuItem
        Caption = 'Green'
        OnClick = Green1Click
      end
    end
    object About1: TMenuItem
      Caption = 'Help'
      object About2: TMenuItem
        Caption = 'About'
        OnClick = About2Click
      end
    end
  end
end

If you want to get a sense of how to edit a form when it's in text mode, you can change one of the caption properties shown earlier, and then close the DFM file. When you bring up the dialog once again, you see that the caption now has the value you assigned to it when the form was being viewed in text mode.

You can always edit a component as text, even if you don't have the entire form open in text mode. For instance, you can drop a button on a form, highlight it, choose Cut from the Edit menu, and then paste the object into a text editor:

object Button1: TButton
  Left = 112
  Top = 16
  Width = 89
  Height = 33
  TabOrder = 0
  Caption = 'Button1'
end

It doesn't matter what text editor you use. For instance, you could use the editor built into Delphi, or you could paste the text into the NotePad program. Now edit the text by changing the Caption, and by changing the type from TButton to TRadioButton:

object Button1: TRadioButton
  Left = 112
  Top = 16
  Width = 89
  Height = 33
  TabOrder = 0
  Caption = 'Charlie'
end

Now, copy the new code into clipboard and paste the button back into the Forms Editor, and you see that the caption has been changed, and its type has been changed from TButton to TRadioButton. Be sure to try this technique on your own computer, because it is both fun to watch and instructive regarding the way Delphi handles visual objects.

If you are interested in an advanced use of this capability, see the FRM2COMP program that ships on the CD in the PROGRAMS subdirectory. It shows how you can convert a form into a component.

Summary

In this chapter you learned how to use the TCanvas and TShape objects to draw simple geometric forms to the screen. In particular, the chapter taught you a lot about using colors, calculating coordinates, and working with rectangles and ellipses.

As a subtext running throughout most of the chapter, you learned a number of important points about how to control components both at design time and runtime. In particular, you saw how the coordinates of a component are mirrored in that component's properties, and how you can controls those coordinates both at design time and runtime. You also got a brief peek at the Gallery, and at editing Forms as text.

In general, you should now know enough about components and graphics to enable you to manipulate these two features in the simple ways required during the next 10 chapters. These chapters will discuss the basics of programming with Object Pascal, and introduce a few more simple graphics issues, particularly in Chapter 9. Graphics programming will be visited again in the section on objects and in the last section of the book, which is on advanced Windows programming.

Delphi Unleashed a SAMS book by Charles Calvert (Do not distribute)

Delphi Unleashed by Charlie Calvert 05


Server Response from: ETNASC04