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
|