Delphi Informant 95 2001
Delphi Informant 95 2001
Delphi Informant 95 2001
Application Development
Implementing MDI, On-Line Help,
and .INI Files
ON THE COVER
9 Creating MDI Apps — Jonathan Matcho
In the Microsoft Windows development environment, there
are two ways you can go with the basic interface of any
application you create: MDI or SDI. This month, Mr
Matcho gets us started with the basics of the Multiple
Document Interface.
he obscure Metallic KO is a live recording of the apocalyptic final concert of Iggy Pop and the Stooges.
T It captures an odd moment in music history and is remarkable in several respects, not the least of
which is that it was ever released. It records the Stooges playing abominably in front of an overtly
hostile audience. You can even hear bottles breaking as they hit the stage. At one point the band is so out
of sync that Iggy Pop shouts over the cacophony and orders them to “take it down to the drums.” Slowly,
agonizingly, in front of a hooting crowd, the Stooges comply. Apocalyptic — yes. Good music — hardly.
In this business, the readers set the Love the mag ner, but can already follow (and near-
tempo and I have no intention of letting us Hi Jerry: The last three issues of ly understand) most of your articles.
get out of sync, so I decided to take it Delphi Informant have been fabulous. This does not mean that the level is
“down to the drums” from the get-go. In I read a ton of magazines and this one not right — it is. A good mix of intro-
June I asked you to let us know how we’re is tops. It is just right in length and ductory and more advanced in fact.
doing and what types of articles you’d like wonderful in layout. ... (3) I like the little tips in “At Your
to see. And you did. How about an article that carefully Fingertips”. Please do keep little
I was greatly relieved to see from your dissects a simple (not too simple) snippets like the “incremental search
letters that we’re on track so far. In fact object? An article that hits on the con- field”. It reminds me to not waste
the response has been overwhelmingly structor, destructor, and all the parts money on some third-party product
positive — and for that I thank you. You of a custom component would be very that offers such simple stuff!
also did a fine job of burying us in article nice. ... I prefer reading about the <smile> I know that lots of powerful
requests, and they are also very much basics of objects. stuff is just a matter of reading the
appreciated. All have been duly noted and This is virgin territory for a lot of us manuals and practicing and the tips
a list has been made available to all cur- out here. And keep hitting the data- help with this. ... (4) Third-party
rent and prospective DI writers. base stuff a lot too. product and book reviews are very
Taking your messages as a whole, sever- Congrats on a fine publication. Well useful. Yes — I’ll buy good-value
al points came through loud and clear. worth the money. — Chip Mayer third-party components. I’ve also got
For one, you do want an editorial section the Dummies book and it is very good
with letters to the editor, so here you are. Thank you Chip! Although we want to hear as you said (lots of tips again). Still
I’ve attempted to capture most of the all comments — positive and negative — waiting to buy the Sams Delphi
other major points with representative this is the kind of message that keeps us Developer’s Guide and Waite Groups’
letters, so I’m going to let you do the rest going. Feel free to send them any time. <g> Borland Delphi How-To. ... Maybe
of the talking. And don’t worry — I won’t run a couple of you have some reviews coming on
I’d like to avoid the sort of humiliating, pages of “you guys are great” messages. But these before I buy? (5) Don’t waste
public course correction that the Stooges keep in mind that I can if I need to. <g> too much space on VB! Give those
suffered in the early 70’s (oh yeah — long fellas a short while to convert, but
before punk), so you can bet I’ll be listen- Suggestions, suggestions, suggestions don’t waste valuable space when it’s
ing. Let’s keep in sync. Jerry. A few comments on your new Delphi stuff we want. (6) Object
mag as requested. ... (1) Yes, I do like Pascal basic-to-intermediate level
the editorial page. A useful communi- articles would be helpful for many. ...
cation that makes the magazine seem Thanks for an interesting and useful
— Jerry Coffey, Editor-in-Chief more complete. Nice to hear from the magazine. Looking forward to my next
CompuServe ID: 70304,3633 busy Editor-in-Chief. (2) I’m a begin- one. — Richard Entwistle, Hong Kong
This pithy message from Mr Entwistle is enough of it. Could you please get a little behind (one of them follows). We fully intend
fairly typical. Most of you have responded more between the covers? That’s the to keep a good balance by offering articles
with such thoughtful critiques. Thank you! only negative thing I can say about DI. for programmers at all levels of expertise.
Thank you! Thank you! Regarding the book DI seems to have a pretty good balance
reviews — we plan to review every Delphi of material. Anyway, thanks for a great Too technical
book as soon as it is available. The two you magazine. I’ve already cleared a spot on Dear Sir,
mention aren’t out yet, but three that are the bookshelf for future volumes. ... I am frustrated with some of the
available are reviewed in this issue. By the way, have you noticed how articles provided in the first two issues
many WWW sites are forming of DI. I am brand new to Delphi, Pascal,
Not too much database please around Delphi? Amazing! Do you and OOP. My background is DOS and
First, I like the editorial ... How you think DI will talk about the offerings BASIC, so Delphi is quite a big under-
are doing? IMHO, wonderfully! It is of these sites? — bt taking for me. ... My subscription to DI
an information-packed magazine with is part of this process.
something for everyone, and don’t One thing virtually everyone agreed on was In particular, I find that there are
change that! ... Topic spread: Pretty that DI could be a bit bigger. All of you some assumptions made on the part of
good, considering you have only pub- should be happy to note that this month’s some writers that their readers know
lished two issues as of yet. Keep it up! issue is 16 pages longer (that’s one “signa- certain terms or lingo. For example, in
Object Pascal: Like many coming from ture” in the biz). It certainly makes me issue one, you had an article by Dan
a strong Visual Basic background ... I happier. From an editorial stand-point it Ehrmann called “Data-Aware Delphi”.
don’t know everything in the Object allows us to offer broader coverage. Our It dealt with modifying a query compo-
Pascal language itself yet. Thus, the WWW coverage begins on page 40. nent via a tabbed interface. ... even
more of it, the better! though this article is identified for
Databases: Eeeiiiii! Everybody seems Not technical enough “Beginner/Intermediate”, nowhere could
to assume that VB programmers pro- I just read the first issue of the DI. I I find information on what a table is! ...
gram databases! As a game/edutain- was very excited to read some articles Questions arose in my mind: 1) What is
ment author, I take umbrage at that. ... with deep insight into Delphi and the a table? 2) How are tables created? Do
There are a lot of database program- VCL ... I think, DI has a good chance they float down out of the sky? 3) How
mers, so I’m not suggesting that you to become a major Delphi developers does one add data to a table? 4) Is a
remove those articles from the maga- information magazine, but I would .DBF file a table? ... then the author
zine, but keep it under control if you recommend that you offer articles that throws the term query around as if
don’t want to lose the general program- are more technical. ... One could write everyone knew what a query was. ...
ming contingent. ... More graphics-ori- about the architecture and implemen- Thank you for your time.
ented articles would be nice. tation of the VCL database compo- — Philip Kapusta
... I noticed something missing from nents ... OLE programming with
your magazine; a letters department ... Delphi ... a dockable toolbar ... Mail- Too technical, not technical enough —
it would be nice to see what other peo- access components ... There are lots of these are the kinds of things that keep edi-
ple think of Delphi Informant. complicated things to write about. tors awake nights. I’m afraid there’s little I
In general, I really like the maga- This criticism should not be taken as a can do except try to steer down the middle.
zine, and will most likely subscribe to very strong one. I can imagine it is a lot As long as these letters are in balance, we’re
it soon if this quality continues! Keep of work to start a new magazine. It on course — and your feedback is critical.
up the great work! — Michael should be taken as a friendly hint ... Let me close with a final question. Philip
what a Delphi developer with some refers to a practice we used only in the pre-
Thanks Michael. And don’t worry — we practice would like to read. miere issue of DI (which is sold out BTW —
won’t let the magazine be dominated by Thanks for your work on Delphi thank you very much), that of labeling arti-
any one programming area. There’s a cool Informant — Christian Abeln cles as Beginner, Intermediate, etc. In prac-
graphics article beginning on page 34. tice, I found this distinction a highly subjec-
BTW, have you subscribed yet? <g> Are there ever lots of technical things to write tive one and simply quit doing it. A number
about! And thank you Christian for the of you have asked for its return however, so
Give us more! “friendly hint” to supply more advanced please let me know how you feel. I can
Mr. Coffey, you asked for feedback. articles. (There were a couple of similar let- always bring it back if you find it
Well, here is mine. I just received my ters that weren’t as kind.) However, there were useful.
first issue (V1N2) of Delphi Informant. also many letters exhorting us not to become Thanks again for all your
In short, I loved it. In fact, I couldn’t get too technical and leave those learning Delphi messages. — J.C.
By Jonathan Matcho
Each style presents an application to the user in a different way. MDI applications have been
thought of as “proper” Windows style. However, SDI applications are becoming more popular —
Delphi is an excellent example.
In this first part of a two-part series, we’ll focus on MDI application design. Specifically, we’ll discuss:
learning the basics of setting up an MDI application, designing code to control MDI applications,
learning about MDI-related properties and methods, and developing an example MDI application.
Start with a new project by selecting File | New Project from the
menu. If you have the Gallery option on for new projects, you’re
prompted with the Browse Gallery dialog box shown in Figure 1.
If the Browse Gallery dialog box appears, select the Blank project
template and press the OK button.
Suppose you have a user request from Bob, the head of the In this procedure, the var statement declares a form object
human resource department at a company called XYZ. Bob has named frmChildTemp that is of the TfrmMDIChild class (the
just requested that a function be added to XYZ’s Bonus class created when the MDI child form was added to our pro-
Calculation system to enable creation of additional bonus calcu- ject). This is also the same syntax by which new variables are
lation windows within the application. Bob explains that a New defined. The object type TfrmMDIChild is available from the
Now Bob can create new child windows at will (see Figure 5). Changing a child window’s FormStyle property from
This example highlights how child windows (which are essential- fsMDIChild to fsNormal enables that window to be brought
ly objects) are created by using a handle of the same object type outside the parent MDI window, as it’s no longer a child win-
as that of the actual window’s class. Additional properties and dow. This can also be done at design- or run-time.
methods can be used to control MDI applications. We’ll discuss
these properties as well as creating new MDI child windows. The Visible property determines whether a visual object is
shown or hidden at run-time. Typically, MDI parent forms
Useful Properties for MDI Window Management are not hidden. It’s also somewhat odd to consider hiding
The highest window class, TForm, includes a number of proper- child forms, and probably the reason this is simply not
ties that support MDI parent and child windows. You can use allowed in a Delphi MDI application.
these properties to affect the behavior of all child forms in your
MDI application. Since forms are used as the basis of the user Incorporating MDI Window Methods
interface, changing overall form-handling properties often has a Now we’ll turn our attention to the additional methods of the
major effect on interface functionality. TForm class that work with MDI windows. Along with the
Create method used in our previous example, there are a
A number of MDI parent window properties that are read- and number of other methods that are useful when assembling
run-time only are not available in the Object Inspector at design MDI applications. MDI methods are recognized only by
Arranging Icons
The ArrangeIcons method organizes the icons of minimized MDI
child windows so they’re evenly spaced along the bottom of the
parent MDI window. The ArrangeIcons method must be sent
only to MDI parent windows (again, those having a FormStyle
property value of fsMDIForm).
procedure TfrmMDIParent.menuFileCloseClick(Sender:
TObject); Conclusion
begin Of course, now that you have all this information, you have all the
if ActiveMDIChild <> nil then
license needed to create interesting and unique Delphi applications
ActiveMDIChild.Close;
end; — right? Not entirely. Keep in mind that your Windows users will
expect consistency between applications. And with the impending
This code checks the ActiveMDIChild property to make sure release of Windows 95, there will be a greater call to provide the
that there is a child window to close. If there isn’t, issuing the user with uniform application interfaces. This does not mean that
Close method will cause a General Protection Fault. an MDI-compliant application will always be best. Perhaps an SDI
implementation would be better suited to your user’s needs.
Next and Previous
The Next method makes the next MDI child window in the Fortunately, Delphi makes it simple to create and maintain
MDI parent window sequence the active MDI child form. The either interface. The freedom you have to use built-in templates
Next method treats the MDI child windows as a circular list. or create custom templates is provided by Delphi. All you have
For example, if you send the Next method to the MDI parent to do is concentrate on the user and the application-specific
window and the currently selected MDI child window is the logic. (In fact, it’s important to note that an excellent model for
last MDI child window out of at least two, the Next method a robust MDI application is available in the form of the MDI
causes the first MDI child window in the list to be made active. Application project template provided with Delphi. You should
definitely study it before creating your own MDI applications.)
The following illustrates use of the Next method:
Next month we’ll discuss an interface style that is growing in
procedure TfrmMDIParent.menuNextClick(Sender: TObject); popularity, the Single Document Interface (SDI). We'll compare
begin
SDI and MDI and build a simple SDI application.
Next;
end;
This article was adapted from material for Using Delphi: Special
The Previous method behaves similarly to the Next method. Edition (Que, 1995) by Jon Matcho, David Faulkner, et al. ∆
However, Previous selects child windows in the opposite direc-
tion. Like the Next method, the Previous method also treats the The demonstration project referenced in this article is available
list of MDI child windows as a circular list. If the Previous on the 1995 Delphi Informant Works CD located in
method is issued when the first MDI child is active, the last INFORM\95\AUG\JM9508.
MDI child becomes active.
Jon Matcho has been building business systems since 1987. Since
Tiling MDI Children then, Jon founded Brickhouse Data Systems, Inc., an East Coast con-
The Tile method sizes the MDI child windows so they don’t over- sulting firm specializing in software development. In 1993 he joined
lap each other. The client area of the MDI parent window is Professional Computer Solutions, Inc. to assist in the delivery of mis-
divided into different regions, each with an MDI child window sion-critical database solutions. You can reach Jon at (908) 704-
7300, or on CompuServe at 71760,2720.
contained (see Figure 8). This code example uses the Tile method:
By Robert Palomo
A Topical Search
Integrating On-Line Help in Delphi Applications
or many people, their first encounter with the term hypertext conjures up
Software companies are now supplying more of their product’s documentation in an electronic for-
mat. They do this because the cost of printing and shipping books takes a bite out of their bottom
line. On the other hand, software users are often unwilling to relinquish the comforting security of
a hefty volume or two (or six). Indeed, sometimes a printed manual is vital. For example, if your
system has just crashed, an on-line system administrator’s guide would be pretty useless.
The popularity of CD-ROM based multimedia titles — such as Microsoft’s Encarta encyclopedia
— is doing much to encourage acceptance of the computer screen as a preferred medium for read-
ing and gleaning information. CD-ROM enables software companies to distribute the entire con-
tents of their printed manual sets on disk, as Borland does with Delphi.
On-line documentation solutions range from entire printed manuals “dumped” into a format for
“viewer” software (that displays text in a linear fashion similar to a book), to custom-designed hyper-
text help systems. This article focuses on the latter, since Delphi provides devel-
opers with some nicely encapsulated means of integrating this type of help in
their applications.
Authoring on-line help essentially involves writing text, dividing it into discrete
topics, formatting it to create hypertext links, creating graphics, and configuring
display elements such as secondary windows and menus. A help file is a self-con-
tained Windows application that users can run from Windows independently of
any other application. (For example, you can double-click on a .HLP file in the
Windows File Manager to run it.) However, that’s not always enough to meet
the needs of end-users, as we’ll see later.
Depending on the scope of your application, you might TApplication has three methods that pertain to the display of on-
define different ranges of values and the parts of the applica- line help: HelpCommand, HelpContext, and HelpJump. If you
tion. For example, one range might be reserved for an entire have experience in other environments working with WinHelp,
module, and sub-ranges defined for component windows of then you may prefer the HelpCommand method for invoking and
those modules. Or, in a small application, you might have controlling help from a Delphi application. This method provides
one range for each form or dialog box, or one reserved only a quick hook-up from the Delphi environment to any of the
for menus, and so forth. WinHelp command macros.
As you plan the ranges, make them large enough so that during Besides accessing topics in context, you can control properties of
initial development each component’s HelpContext ID incre- the help window — such as size, color, or position — and dis-
ments by five. For example, for three button components on a play topics in secondary windows. HelpCommand is well docu-
form, you would set the HelpContext properties as 100, 105, and mented in the Delphi on-line help system, and you should be
110 respectively (instead of 100, 101, and 102). This leaves able to learn and use it quickly.
room in the range for additional components without having to
make major modifications to the [MAP] section of the .HPJ file. If you want to access a specific help topic with code, then you’ll
appreciate the simplicity of the HelpContext and HelpJump meth-
Properties and Methods for Accessing Windows Help ods. These provide the same result: the display of a specific topic
Delphi provides properties and methods that enable you to in the help file. The difference is in how you specify the Context
access specific topics in a help file, or in different help files. In ID for the help topic you want to access.
this section, we’ll summarize these and take a look at some cre-
ative ways you might use them. An application developer will tend to approach context-sensitivity
from the standpoint of the application user-interface. You know
Delphi’s TApplication component encapsulates the functionality what components are in a specific dialog box or window, and you
needed to access WinHelp via the Windows API. The WinProcs know (or can easily look up) the numeric Context ID in the Object
unit contains methods for accessing specific help topics in a Inspector. Therefore, you would know the Widget component in
given help file. the Foo dialog box has a Context ID of 110 in its HelpContext
property. You may not know (nor care) that the help file topic asso-
Before your application can display help, you must specify a help ciated with it is “Using the Widget Component” with a context
file for it. The HelpFile property of TApplication specifies the string of UsingWidget. You simply want to display whatever topic is
name of the help file that the various help access methods will in the help file for the Widget component when it has the focus and
look at. You normally specify the name of this file by selecting the user presses 1.
Options | Project and choosing the Application page of the
Project Options dialog box (see Figure 4). If you want to display this topic in response to another event,
simply call the HelpContext method in your event handler:
You can also set or change the designated help file at run-time
with an assignment: Application.HelpContext(110);
I can also recommend Developing On-Line Help for At design time, we’ll specify HRINFO.HLP as the application
Windows by Scott Boggan, David Farkas, and Joe Welinske help file. We’ll create a UI that will switch the help file as need-
(SAMS). This book will take you from neophyte to consul- ed. Then, we’ll create a main form for the application called
tant-level expertise in Windows help development. It’s well- MAINFRM.PAS, as shown in Figure 5.
organized and indexed, very readable, and covers all the
issues. It includes a disk containing Word templates and
macros optimized for creating Windows help source files,
example help projects, help project file templates, and
bitmap graphics you can use in your own projects. It also
presents a comprehensive review and comparison of several
third-party help authoring tools.
Third-Party Tools
Windows Help Authoring Utility
(on the Microsoft Developer Network CD)
Microsoft Corp.
1 Microsoft Way
Redmond, WA 98052
(206) 882-8080 Figure 5: The Human Resources sample application.
RoboHelp
Blue Sky Software Corp. This form has five BitBtn components labeled Insurance,
7486 La Jolla Boulevard, Suite 3 Benefits, Policies, Help, and Close. The event code behind the
La Jolla, CA 92037
(619) 459-6365
first four buttons hides the main window, displays a button bar,
and executes the OnClick event of the appropriate Speedbutton
Doc-To-Help component on the button bar:
WexTech Systems, Inc.
310 Madison Avenue, Suite 905 procedure TMainWin.BBtnInsuranceClick(Sender: TObject);
New York, NY 10017 begin
(212) 949-9595 MainWin.Hide;
BtnBarWin.Show;
Help Magician BtnBarWin.SBInsuranceClick(Sender);
Software Interphase, Inc. end;
82 Cucumber Hill Road
Foster, RI 02825-1212
(800) 542-2742
Each of the BitBtn components has a similar OnClick event handler.
(401) 397-2340
On the button bar, each Speedbutton sets the current help file
and displays its Contents screen:
procedure TBtnBarWin.SBInsuranceClick(Sender: TObject);
provides information and answers to employee questions on-line. { User has selected Insurance button in main screen }
The information is contained in several Windows help files. The begin
{ Display Speedbutton in down state }
text of these files are maintained by different people in the HR SBInsurance.Down := True;
department. There is also a help file for the module itself. { Set current Help file }
Application.HelpFile := 'INSURE.HLP';
{ Access Help file Contents }
The help files are as follows: Application.HelpCommand(HELP_CONTENTS, 0);
• INSURE.HLP — Information on medical, dental, and life PnlInsurance.visible := True;{ Display button panel }
insurance plans. end;
The Windows Help button on the main form switches the active
help file back to the design time default, HRINFO.HLP:
The Insurance, Benefits, and Policy buttons on the button bar have Robert Palomo has been a technical writer in the software industry in Seattle, WA and
their GroupIndex property set to a value of 1, and the AllowAllUp Silicon Valley, CA for the past five years. His most recent job was as a member of the
property set to False. This eliminates the need to test for the value of Delphi documentation group at Borland International. The extensive staffing cutbacks
the button’s Down property. You simply set the Down property of at Borland gave him enough free time after Delphi shipped to set up shop part-time as
the selected button to True, and the others with the same a consultant specializing in integrating context-sensitive help in Delphi and other
Windows applications. Robert also does Delphi application development work. You can
GroupIndex property display in the “up” state. (You could achieve contact him on CompuServe at 76201,3177 or on the Internet at
the same effect with a RadioButton group, but this technique [email protected].
enables you to use glyphs on the buttons for a more elegant UI.)
By Douglas Horn
Initialization Rites
Using Windows .INI Files in Delphi
N a glance at the \Windows directory of most PC’s will show how widely
they’re used — and some programs use several.
Unfortunately, .INI files are underutilized by most application developers. This is despite the fact
that they’re extremely useful for storing information that must be saved between program sessions.
Delphi makes it particularly easy to implement an .INI file with an application. This article will
introduce .INI files and explain their use with Delphi.
Two of the best-known .INI files are WIN.INI and SYSTEM.INI. Both of these files are used to
store important parameters for the Windows operating system. While most Windows applications
use an .INI file named for the executable (e.g. DELPHI.INI), many use the WIN.INI file instead
of, or in addition to, their own file.
.INI files are simple text files (see Figure 1). Each .INI file is divided into
sections, easily recognizable by a section title enclosed in brackets (e.g.
[Boot]). Within each section are a number of statements. Each statement
begins with a variable or identifier, followed by its value (e.g.
spooler=True). .INI files are not case-sensitive.
uses
IniFiles, SysUtils, WinTypes, WinProcs, Messages,
Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,
StdCtrls, Menus, Buttons;
When it’s no longer necessary to access the .INI file, the Free
method is used to release the .INI file, destroy the TIniFiles
object, and free the resources reserved for the object:
...
AppIni.Free;
end;
Conclusion
I hope this article has helped to demystify Windows .INI files.
These files can be easily implemented, and add an extra level of
user-friendliness and sophistication to any Delphi application.
Although for clarity’s sake, the sample application stores only
simple coordinates and settings, .INI files can be used to store
almost any type of program information.
Next month, we’ll extend the .INI file concept further by com-
bining it with MDI (multiple-document interface) applications
and menus to create a list of most-recently used files (or MRU).
This is an advanced feature that enables users to quickly and eas-
ily pick up where they left off. ∆
Douglas Horn is a freelance writer and computer consultant in Seattle, WA. He special-
izes in multilingual applications, particularly those using Japanese and other Asian
languages. He can be reached via CompuServe at 71242,2371.
By Charles Calvert
Strings: Part I
An Introduction to Object Pascal Strings
trings. You work with them in virtually every application you develop, yet
S there are probably a number of features associated with strings that you
haven’t had a chance to explore.
This first installment of a three-part series on strings will demonstrate that strings are really a
form of array. However, a number of interesting rules specific to strings do not apply to arrays,
and we’ll investigate most of these in detail. In particular, you’ll learn how to search for a sub-
string in a string, and how to parse a lengthy string.
type
TNearString = array[0..255] of Char;
var
NearString: TNearString;
MyString: string;
Based on this code fragment, MyString has all the traits of TNearString, plus a few special
qualities. In other words, a Delphi string is a superset of an array of Char that is 256 charac-
ters long. Let’s examine the differences.
All characters in an array of Char are equal; no one character has any special properties. In a
string however, the first character is called a length byte. It designates how many characters
exist in the string. Because the first character has this special task, the first letter in the string is
always at offset one.
It’s important to remember that most Chars can be represented in two ways. For instance, the letter
“A” can be printed verbatim, or it can be represented by the 65th member in certain character sets.
If you want to refer to the letter “A” by its place in a character set, you can enter #65. The # in this
example designates the item in question is a Char and not a simple numerical value. Therefore, the
number five is represented as 5, but the fifth character in a character set is represented by #5.
For example, consider a string containing the word “Hello”. This string is five characters
long, so the first byte in a string containing this word would be set to the fifth character
in a table of characters:
NearString[0] := #5;
procedure TForm1.Button1Click(Sender: TObject);
var
The next character in the array would be an “H”: S: string;
begin
NearString[1] := 'H'; { H = #72 } S[0] := #5;
S[1] := 'H';
S[2] := 'e';
The rest of the letters would immediately follow: S[3] := 'l';
S[4] := 'l';
NearString[2] := 'e'; S[5] := 'o';
NearString[3] := 'l'; Edit1.Text := S;
NearString[4] := 'l'; end;
NearString[5] := 'o';
Figure 1: Assuming an Edit component (named Edit1) and Button
The result is an array of six characters that looks like: component (named Button1) exist on a form, this Object Pascal code
compiles and displays Hello in the Edit box.
#5,'H','e','l','l','o'
Clearly, this fragment is easier to write. However, the fact
If you took the whole process to an imaginary “memory that you can write code like this is a special feature of the
theater”, you would see six seats aligned one behind the compiler. What the compiler actually does is shown in
other. The person in the first seat would be told to remem- Figure 1. However, it’s laborious to write all that code each
ber the total number of letters in the word(s) that are part time you want to assign a value to a string. Therefore, the
of the string. The person in the second seat would remem- compiler allows you to write code like the above example.
ber the first letter of the string (in this case it’s an “H”).
The person in the next seat would remember the letter A brief sample program, named EASYSTR.DPR, demon-
“E”, and so on. strates these ideas. Specifically, EASYSTR shows what hap-
pens if you don’t treat a string’s length byte carefully. The
So far so good. But what about the remaining 250 characters form for the EASYSTR program includes two buttons and an
in the string? It doesn’t matter what information is stored in Edit component (see Figure 2). The code for the EASYSTR
those bytes. They can be zeroed out, or hold nothing but program is shown in Listing Two on page 28.
garbage. It doesn’t matter what those bytes hold provided the
first byte is correctly set to the total number of valid charac- The EASYSTR program enables you to display a valid or invalid
ters in the string. string inside an Edit component. The invalid string is flawed
because it has an incorrect length byte. Specifically, it sets the
Let’s say the length byte in the above example was accidental- length byte to 150, although the string you want to print is only
ly set to #6 instead of #5. five characters long.
Then, the letter the person in seat seven is supposed to This simple mistake would cause trouble in your program.
remember would become part of the string — usually with However, so you can clearly see what is going wrong, anoth-
disastrous results. For instance, the string Hello may suddenly er ScrambleString procedure was added to the program.
become any of the following:
• Hello1
• Hellob
• Hello#
• Hello+
var
S: string;
begin
S := ‘Hello’;
Edit1.Text := S;
end; Figure 2: To create the form for the EASYSTR program, simply drop
two buttons, a panel, and a label onto a form.
implementation
The demonstration program referenced in this article is avail-
able on the 1995 Delphi Informant Works CD located in
{$R *.DFM} INFORM\95\AUG\CC9508.
procedure ScrambleString(var S: String);
var
i: Integer;
begin
for i := 0 to 255 do Charlie Calvert works at Borland International as a Developer Relations Manager for
S[i] := Chr(Random(255));
end; Languages. He is the author of Delphi Programming Unleashed, Teach Yourself
Windows Programming in 21 Days, and Turbo Pascal Programming 101. He lives
procedure TEasyString.BValidClick(Sender: TObject); with his wife, Marjorie Calvert, in Santa Cruz, California.
var
S: string;
begin
ScrambleString(S);
S[0] := #5;
S[1] := 'H';
S[2] := 'e';
S[3] := 'l';
S[4] := 'l';
S[5] := 'o';
Label1.Caption := S;
end;
end.
very database application needs the ability to validate data. This can be
E as simple as ensuring that the user enters data in all upper-case letters,
or as complex as verifying that an entered value is consistent with data
stored in another table.
This month’s DBNavigator is the first of a two-part series on data validation. Part I introduces
the basic concepts of data validation, and describes how to apply field-level validation to stan-
dard components (i.e. components from the Standard page of the Component Palette not
associated with database tables). In Part II, we’ll look at applying both record-level and field-
level validation to data-aware components (i.e. components from the Data Controls page of
the Component Palette).
Field-level validation refers to the validation that takes place when a user enters data into a sin-
gle field. (The term “field” is used here in its database sense. In Delphi a “field” usually takes
the form of an Edit or DBEdit component.) In most cases, the validation process occurs when
the user has completed editing the field, although it can also be applied after each keystroke. If
the data in the field is determined to be unacceptable, the user is informed and won’t be per-
mitted to leave the field.
From the user’s standpoint, record-level validation is more convenient than field-level. Specifically,
when record-level validation is employed, the user can move freely among the records, leaving some
fields only partially complete to move onto other fields. As long as the user completes these partial
fields before attempting to post the data (and has also completed the record correctly), record-level
validation code will accept the record. This process mimics the way a user interacts with a paper form.
On the other hand, field-level validation can be intrusive. A user attempting to leave a field — or
perform another task that will result in the field being posted (such as inserting a new record) —
when the field is not complete is interrupted by the validation code. This interruption may be as
minor as the display of an error message in a status bar, or as significant as the display of a message
in a modal dialog box requiring acknowledgment.
Field Validation with Standard Controls ComboBox component is the one that requires the most adjust-
In Delphi, you will quickly learn that sometimes less program- ment, this example will make use of that component.
ming is better. Specifically, if you can achieve a particular result
using either properties or code, use properties. Sometimes this Begin by creating a new project. (It’s always a good idea to
also means selecting the right component for the job. These rules first create a directory to save the project in.) On your new
certainly apply when it comes to field-level validation. form place a Label and a ComboBox component from the
Standard page of the Component Palette (see Figure 1).
In addition, how you perform field-level validation depends in Change the Label’s Caption property to &Day of Week:, and
part on the type of component you are validating. The compo- its FocusControl property to ComboBox1. (A Label component
nents (or controls) that you place on a form are available in cannot have focus, so the FocusControl property determines
two basic “flavors”: data-aware (i.e. associated with a field in a which component focus will shift to when you press the
table), or not. A data-aware edit component (i.e. a DBEdit Label’s short-cut key, “D” in this case.)
component) has different events than one that is not (i.e. an
Edit component), and consequently, requires distinct tech-
niques for field-level validation.
The Text property permits you to define the default value that
will appear in the component. At a minimum, this property
should be set to a blank string. It looks strange to the user if the
component name (e.g. Edit1) appears by default.
There are a number of events to which you can attach your Edit
component validation code. Some of these require more work
Figure 2 (Top): The String list than others. For instance, if you add your validation code to
editor dialog box. Figure 3
either the OnKeyDown or OnKeyUp event handlers, you must
(Left): The demonstration form
at run-time. The user can only evaluate the entered data after each keystroke. In many cases, this
enter values from the is a lot of work. For example, if the Edit component is used to
ComboBox’s dropdown list. get a date from a user, your code must account for the fact that
the value will not conform to a date value until the user enters
the last character.
the Text property is blank. If you need to define a default value
for your DropDownList ComboBox, add the following line to the I have found it easier to validate data when the user attempts to
form’s OnCreate event: leave the field, or before the value in the Edit component is used
by another part of the program. For example, if a button contains
ComboBox1.ItemIndex := 0; code in its OnClick event handler that will generate a query based
on a value in an Edit component, the OnClick code should first
This Object Pascal statement sets the ComboBox’s default to the validate the Edit component’s data. Likewise, if the validation
first string stored in the List property (i.e. Sunday). needs to be performed before a form is closed, the validation code
can be called from the form’s OnCloseQuery event handler (and its
Validating Standard Controls Using Code CanClose parameter can be set to False if invalid code is found).
Edit components are more complex to validate. On one hand,
they possess properties you can use to ensure the user enters The specific technique you employ to validate the Edit compo-
appropriate data. On the other hand, there are many situations nent depends on the type of validation required. Since most of
where you must add code to ensure validity. the time the validation relates to the data type of the entered
text, a try...except statement is the most useful. In the try block
Important properties that you should consider using with you attempt to cast the Text property of the field to the particu-
Edit components include CharCase, MaxLength, ReadOnly, lar data type.
and Text. You can use CharCase if you want to control the
case of data entered into an Edit component. For example, If the value cannot be cast to the specified value, Delphi will
setting CharCase to ecUpperCase will convert all letters generate an exception. You trap this exception in the except
entered into that field to upper-case, while ecLowerCase will block, and respond accordingly by displaying a message in a sta-
convert them to lower-case. tus bar (usually a Panel component), or by raising a custom
exception (displaying an error message you’ve defined).
The MaxLength property enables you to define the maximum
number of characters the user can enter into a field. If the Even if the Text property of the Edit object can be successfully cast
user attempts to enter one character more than the defined to a date type, it is still possible that the value is not valid based on
MaxLength value, the form beeps and the character is rejected. business rules. For example, you may not want to permit the user
When MaxLength is set to zero, no limit is enforced. to enter a date later than today’s date. When an unacceptable date
is detected by your code, you can explicitly raise an exception.
You use the ReadOnly property to prevent the user from changing This can permit the rule violation to be handled by the same
a value in an Edit component. Obviously, if an Edit component is exception handler that processes the illegal assignment exception.
read-only, the user cannot change the value. Often, you modify
the ReadOnly property at run-time, changing the Edit component The following example demonstrates how to validate a field.
from editable to non-editable, based on events on the form. Because this type of validation is often called from more than
one event, this code will be nent to validate. If the date in that Edit component is found
placed in a function. This to be invalid, an error message is displayed and ValidateDate
allows it to be called from returns the value False. Otherwise, the function returns the
any event handler that must value True.
validate the field. (The
specifics of creating a new As an additional demonstration, this code also sets the value
function are not discussed of the Day of Week ComboBox after a valid date has been
here. Likewise, exception entered. This is done by modifying the ComboBox’s
handling and exception cre- ItemIndex property. However, notice it’s necessary to subtract
ation is demonstrated without Figure 4: Adding an Edit compo- one from the DayOfWeek function. This is because List com-
going into detail. If you need nent to the example form. ponents have a zero-based index, while the DayOfWeek func-
additional information about tion returns a value from one to seven. If you want to use the
these topics, please refer to the Delphi User’s Guide.) ValidateDate function as a generic date validation function,
Begin by adding a Label component and an Edit component to you would remove the following statement:
your form. Change the Caption property of the Label component
ComboBox1.ItemIndex := DayOfWeek(newValue)-1;
to D&ate:, set the FocusControl property to Edit1, and delete Edit1
from the Text property (see Figure 4).
We’re not finished implementing the ValidateDate function. It’s
still necessary to declare the function in the interface part of the
Next, select the Edit component and set its MaxLength property
unit. Add the header of the function (without the TForm1 com-
to 10 (10 characters is sufficient to permit the entry of a date).
ponent name) to the type declaration.
Next, press @ to display the unit. Move your cursor to the
line just above the final statement in the unit (end.), and enter
It is also necessary to declare the exception EEarlyDate. This
the ValidateDate function shown in Figure 5.
must be done before the form declaration. When you are
through the type declaration should look like this:
The ValidateDate function takes a single parameter — an Edit
component. This permits you to call this validation code from type
various routines, passing the name of a specific Edit compo- EEarlyDate = class(Exception);
TForm1 = class(TForm)
Label1: TLabel;
ComboBox1: TComboBox;
function TForm1.ValidateDate(TheField: TEdit): Boolean; Edit1: TEdit;
var Label2: TLabel;
NewValue: TDateTime; function ValidateDate(TheField: TEdit): Boolean;
begin private
{ Initialize the return value. } { Private declarations }
Result := True ; public
{ Do not evaluate if the field is blank. } { Public declarations }
if TheField.Text = '' then end;
Exit;
try All you need to do now is call this function from the OnExit
{ Cast the field’s text to a date. } event handler for the Edit component. Return to the form,
NewValue := StrToDate(TheField.Text);
{ The value is a date. Perform any additional tests. } select the Edit component, and double-click OnExit on the
if NewValue < StrToDate('1/1/95') then Events page of the Object Inspector. Modify the Edit1Exit
raise EEarlyDate.Create('Date cannot be before 1995') event handler to look like this:
else
{ Valid date. Modify the value of ComboBox1.}
ComboBox1.ItemIndex := DayOfWeek(newValue)-1; procedure TForm1.Edit1Exit(Sender: TObject);
except begin
on EEarlyDate do ValidateDate(Edit1);
begin end;
Result := False;
raise; Compile the project, save it as VALIDATE.DPR, and exit Delphi.
end;
else Then select File | Run from Windows Program Manager and
begin enter the name of the executable project (include the path and the
Result := False; name VALIDATE). Once the form is running, move to the Date
raise Exception.Create('Invalid date');
end; field and enter an invalid date. Then press F to exit the field.
end; This will produce an exception resembling that in Figure 6. Move
back to the Date field and enter a valid date. This time, the value
end;
displayed in the Day of Week field will be updated.
Figure 5: This custom ValidateDate function uses a try...except block Note: To see how the exception appears to the user, it is best
to validate a date value.
to run this program from the Program Manager, rather than
Conclusion
It is often necessary to ensure that data entered by users con-
forms to certain parameters. Here we have considered how to
perform this task with fields not associated with table data.
While some of these components required no code to ensure
valid data, Edit components do.
By David Faulkner
The program in this article demonstrates device independence by creating the color triangle shown
in Figure 1. Try this program with different video drivers, resolutions, and number of colors. By
varying some of the calculations, you can create some spectacular screens.
Form1.Canvas.TextOut(10,10,'Hi Mom');
Now run the form and click the button. Assuming your button isn’t in the wrong place, “Hi
Mom” should appear in the screen’s upper-left corner.
Each form you create has a Canvas property of type TCanvas. With
the form’s canvas you can easily draw lines, circles, boxes, and text.
The Canvas property encapsulates the Windows device context. If you
are not already convinced that Delphi is the “cat’s meow”, the Canvas
property should convince you. If you had to write the “Hi Mom”
code above using just the Windows API, you would need to do quite
a bit of work creating and destroying pens and brushes.
The form’s canvas also gives you access to the individual pixels on a
form. This is done through the Pixels property:
For a quick example of the Pixels property, create a form and By substituting this equation into the above Pythagorean equa-
enter the following code into the form’s OnClick method: tion, and doing some algebra we have:
procedure TForm1.FormClick(Sender: TObject);
var h 2 = ( 1 2 h) 2 + y 2
x,y : integer;
begin
for x:=0 to Form1.ClientWidth do 1
for y:=0 to Form1.ClientHeight do h2 = h2 + y2
Form1.Canvas.Pixels[x,y] := 4
$02000000 + Trunc($ffffff * Random);
end; 3 2
h = y2
4
Run the form and click on it. The form should fill from left to
right with random colors (not exactly exciting, but it gets better). 4 2
h2 = y
3
The TColor value of the Pixels property determines the color
of a pixel. Its type is Longint so it is four bytes long. The first 2
h= y
byte determines how the color is rendered on a palette. The 3
next three bytes represent blue, green, and red, respectively. A
value of zero for any of these colors means that color is not
In Object Pascal code this becomes:
displayed at all, while a value of $FF (that’s Delphi’s hex
notation for 255) means full intensity of that color. For
const
example $02FF0000 is blue, $0200FF00 is green,
Sqrt3=1.732; { The square root of 3. }
$020000FF is red, $02000000 is black, and $02FFFFFF
(all colors) is white.
begin
with Form1 do
The Color Triangle
begin
To create the color triangle, create a new form and call the
intTriangleHeight := ClientHeight-2;
DrawTriangle method (shown in Listing Three on page 38) from
IntTriangleWidth := Trunc(2*intTriangleHeight/Sqrt3);
either a button or menu choice. Note that this code is more
complicated than it needs to be just to draw a triangle. The
Note here that the square root of three is declared as a constant
complexity is there so the user can configure the application to
named Sqrt3. This is because the square root of three is used in
create those cool screens (which we’ll see a bit later).
a number of places in the program including once in a loop.
There is no need to slow down the program by making the com-
First, the code in DrawTriangle determines the coordinates of
puter do a calculation each time Sqrt3 is needed.
the triangle’s three points. The triangle’s height is set to the
form’s height minus two pixels to give a one pixel border at
With the height and width of the triangle determined, it’s a sim-
the top and bottom of the form. The width is a bit more
ple matter to calculate the coordinates of the triangle’s three ver-
complicated to calculate, and you might need to brush up on
tices. A pair of nested for loops is used to visit every pixel in the
your trigonometry and algebra skills to see where the formula
triangle as follows (this code is a simplified version):
comes from.
for y:= intTopY to intTriangleHeight do
The Pythagorean theorem states the square of the hypotenuse begin
of a right triangle is equal to the sum of the squares of the for x:=intTopX-Trunc(y/Sqrt3) to intTopX+Trunc(y/Sqrt3) do
begin
other two sides: { Statements to enter color screen pixels }
end;
end;
h2 = x2 + y2
The intTopY and intTopX variables represent the x and y coordi-
Here h is the length of the hypotenuse, and y is the height of nates of the triangle’s top vertex. Notice the use of the Sqrt3
the triangle, and we are looking for x, the width of the trian- constant again. This time the reader must perform the necessary
gle. Since the color triangle is an equilateral triangle (all its algebra to calculate the start and end of the x loop.
sides are of equal length), we will work with the right triangle
created by the line y. Since the line y bisects the bottom of Within the nested loop, the objective is to vary the intensity of each
the triangle, we know that: color in proportion to the distance from that color’s vertex. The dis-
tance between two points on a plane is calculated with this formula:
1
x= h 2 2
2 d= x 2 − x1 − y 2 − y1
Since the blue vertex is the top vertex, the intTopX and
intTopY variables are used in this calculation. The Sqrt func-
tion returns a real number, so the Trunc function is used to
convert the real number into an integer that can be assigned
to the longBlueDistance variable. Similar calculations are done
for each color.
With the distance known, the amount of blue is proportioned Figure 2: The configuration dialog box.
with the following code:
realBluePart := ((intTriangleWidth-longBlueDistance) / ables that are meant to be accessed by any other program using
intTriangleWidth); a unit should be declared in the interface section of the unit:
longBlue := Trunc($ff*realBluePart)*$ff*$ff;
interface
The realBluePart variable is assigned a fraction between zero and
uses
one since the minimum BlueDistance is zero and the maximum type
BlueDistance is equal to the length of any side of the triangle.
This fraction is then multiplied by 255 to obtain the actual blue var
Form1 : TForm1;
intensity that will be used to color a pixel. Since the blue part of byteBlueOption : byte;
the TColor type is the second byte, the number is multiplied by byteRedOption : byte;
255 * 255 to shift it into its proper position. byteGreenOption : byte;
boolFullScreen : Boolean;
boolColorLimit : Boolean;
Actually, the code would be faster and more accurate if it read: boolPaletteCheck : Boolean;
The shift left operator, shl, shifts the value 16-bits (two bytes) Before any of these variables can be used, they must be initial-
to the left to move it into the proper position. The problem ized. Since there is no guarantee the user is going to configure
with this method is that it removes some of the cross color the program before they initiate the drawing process, it’s a good
interference resulting in the various screens you’ll create at the idea to initialize the variables in the form’s Create method:
end of the article.
procedure TForm1.FormCreate(Sender: TObject);
begin
After each color is calculated, it can be used to assign a color to a byteBlueOption := 0;
screen pixel as follows: byteGreenOption := 0;
byteRedOption := 0;
boolFullScreen := False;
Canvas.Pixels[x,y] := longBlue + longGreen + longRed;
boolColorLimit := True;
boolPaletteCheck := True;
That’s it for creating the color triangle. Download the code or end;
type it in (it’s not that long) and try it.
To give the user access to the configuration dialog box, either a
Configuring the Color Triangle menu choice or a button can call this code:
While writing the original color triangle code, I made a number
procedure TForm1.Config1Click(Sender: TObject);
of coding mistakes that yielded some wild results. The results
begin
were odd enough that I decided to make the program user-con- ConfigDialog.ShowModal;
figurable so the user could recreate the same effects. I did this by end;
creating the dialog box shown in Figure 2.
Note the unit that creates the configuration dialog box must be
Adding a configuration dialog box to this or any other pro- included in the main form’s uses clause for the above code to
gram is relatively easy, but there are a few things you must compile. The ShowModal function shows the passed form and
know. The configuration dialog box must communicate with insists the user close that form before continuing.
the main form.
Getting Around a Circular Reference
One way of doing this is with global variables that both the The first problem the configuration dialog box faces is gaining
main form and configuration dialog box can access. Global vari- access to the main form’s global variables. It seems logical to accom-
plish this by placing the main form’s unit in the dialog box’s uses
clause. If you try this, Delphi will respond with the “Error 68:
Circular Unit Reference” when you compile. This happens because
the main form uses the dialog box’s unit, which in turn uses the
main form’s unit, which in turn uses the dialog box’s unit, etc.
FullScreenButton.Checked := Coloru.boolFullScreen;
TriangleButton.Checked := not Coloru.boolFullScreen;
ColorLimitButton.Checked := Coloru.boolColorLimit;
PaletteCheckButton.Checked := Coloru.boolPaletteCheck;
Compiler Directives
You may have noticed the following two lines of code at the
beginning of the Coloru unit:
{$R-}
{$Q-}
Although these look like comments, they do a lot more. Any com-
ment beginning with a dollar sign ( $ ) tells Delphi to do something
special at compile time. The {$R-} compiler directive tells Delphi to
turn off range checking. This quickens the compile and allows the
math in the DrawTriangle method to go out of range without caus-
ing a run-time exception. The {$Q-} compiler directive tells Delphi
to turn off overflow checking in integer math. Again, this speeds
compilation and allows the integer math in the DrawTriangle
method to overflow during run-time without causing exceptions.
One other compiler directive you might want to play with is {$N-}.
This tells Delphi to do real number math in software instead of
using the floating point coprocessor on your machine. If you use
this directive, you will notice a big slowdown in the program (unless Figure 3 (Top): The Compiler page of the Delphi Project Options dia-
of course, your machine doesn’t have a math coprocessor). log box. Figure 4 (Middle): Fun with colors. Use these settings to
create the color triangle as shown. Figure 5 (Bottom): To generate
this “field of circles”, set the Red, Green, and Blue radio buttons
Conclusion to: S. Root, S. Root, and Nothing (respectively). Deselect Color
So have some fun. Use the configuration dialog box to produce Limit, check Palette Check, and select Full Screen.
the special effects shown in Figures 4, 5, 6, and 7.
colors, and fonts. This may seem like a frivolous exercise, but
Delphi gives the programmer quick and easy access to the form’s when it comes time to write your components you’ll use the
canvas and you can experiment with various lines, boxes, pixels, skills learned here to write custom OnPaint code. ∆
{$R-}
{$Q-}
unit Coloru;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Menus, Config;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
Config1: TMenuItem;
Draw1: TMenuItem;
procedure Config1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure DrawTriangle;
procedure Draw1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
byteBlueOption: Byte;
byteRedOption: Byte;
byteGreenOption: Byte;
boolFullScreen: Boolean;
boolColorLimit: Boolean;
boolPaletteCheck: Boolean;
implementation
{$R *.DFM}
Figure 6 (Top): This “super nova” is created by setting Red to procedure TForm1.DrawTriangle;
Square, and Green and Blue to S. Root. Color Limit and Palette var
Check are not checked. Figure 7 (Bottom): To get this “cosmic { Coordinates of top and bottom left of triangle }
intTopX,intTopY,intLeftX,intLefty,
egg” set Red and Green to Nothing and Blue to S. Root. Deselect
{ Coordinates of bottom right of triangle }
Color Limit and Palette Check.
intRightX,intRightY: Integer;
{ Height and width of triangle }
intTriangleHeight,intTriangleWidth: Integer;
{ Distance current position to triangle point }
The demonstration project referenced in this article is available on longBlueDistance,
the 1995 Delphi Informant Works CD located in longGreenDistance,longRedDistance: Longint;
{ Fraction of ColorDistance to MaxDistance }
INFORM\95\AUG\DF9508. realBluePart,realGreenPart,realRedPart: Real;
{ Color of pixel to be placed on screen }
longNewColor: TColor;
{ Amount of RGB for each color }
longRed,longGreen,longBlue: Integer;
{ First in last pixel in row of triangle }
David Faulkner is a developer with Silver Software in Kula, Hawaii. He is also intXBegin,intXLimit,
Contributing Editor to Paradox Informant, and co-author of Using Delphi: Special { First and last pixel in col of triangle }
Edition (Que, 1995). Mr Faulkner can be reached at (808) 878-2714, or on intYBegin,intYLimit: Integer;
CompuServe at 76116,3513. { Loop counters }
x,y: Integer;
const
{ The Square Root of 3 }
Sqrt3=1.732;
begin
with Form1 do
begin
{ One pixel space on top and bottom }
intTriangleHeight := ClientHeight-2;
{ Get out your trig book }
intTriangleWidth := Trunc(2*intTriangleHeight/Sqrt3);
{ Top is half way across screen } realGreenPart := ((intTriangleWidthlongGreenDistance)/
intTopX := ClientWidth div 2; intTriangleWidth);
{ and one pixel down } longGreen := Trunc($ff*realGreenPart)*$ff;
intTopY := 1;
if boolColorLimit then
intLeftX := (ClientWidth - intTriangleWidth) div 2; longGreen := longGreen and $0000ff00;
intLefty := intTopY+intTriangleHeight;
intRightX := (ClientWidth + intTriangleWidth) div 2; case byteRedOption of
intRightY := intTopY+intTriangleHeight; 0: longRedDistance := Trunc(Sqrt(Sqr(intRightX-x) +
Sqr(intRightY-y)));
if boolFullScreen then 1: longRedDistance := Sqr(Sqr(intRightX-x) +
begin Sqr(intRightY-y));
intYLimit := ClientHeight; 2: longRedDistance := Abs(Sqr(intRightX-x) +
intYBegin := 0; Sqr(intRightY-y));
end end;
else
begin realRedPart := ((intTriangleWidthlongRedDistance)/
intYLimit := intTriangleHeight; intTriangleWidth);
intYBegin := intTopY; longRed := Trunc($ff*realRedPart);
end;
if boolColorLimit then
for y:=intYBegin to intYLimit do longRed := longRed and $000000ff;
begin
if boolFullScreen then longNewColor := longBlue+longGreen+longRed;
begin
intXLimit := ClientWidth; if boolPaletteCheck then
intXBegin := 0; longNewColor := longNewColor and $02ffffff;
end Canvas.Pixels[x,y] := longNewColor;
else { End of for x loop }
begin end;
{ Get out that trig book again } { End of for y loop }
intXBegin := intTopX-Trunc(y/Sqrt3); end;
intXLimit := intTopX+Trunc(y/Sqrt3);
end; { End of 'with Form1 do begin' }
end;
{ For each pixel in this row } {End of procedure}
for x:=intXBegin to intXLimit do end;
begin
case byteBlueOption of procedure TForm1.Config1Click(Sender: TObject);
0: longBlueDistance := Trunc(Sqrt(Sqr(intTopX-x) + begin
Sqr(intTopY-y))); ConfigDialog.ShowModal;
1: longBlueDistance := Sqr(Sqr(intTopX-x) + end;
Sqr(intTopY-y));
2: longBlueDistance:= Abs(Sqr(intTopX-x) + procedure TForm1.FormCreate(Sender: TObject);
Sqr(intTopY-y)); begin
end; byteBlueOption := 0;
byteGreenOption := 0;
realBluePart := ((intTriangleWidth-longBlueDistance) / byteRedOption := 0;
intTriangleWidth); boolFullScreen := False;
longBlue := Trunc($ff*realBluePart)*$ff*$ff; boolColorLimit := True;
boolPaletteCheck := True;
if boolColorLimit then end;
longBlue := longBlue and $00ff0000;
case byteGreenOption of procedure TForm1.Draw1Click(Sender: TObject);
0: longGreenDistance := Trunc(Sqrt(Sqr(intLeftX-x) + begin
Sqr(intLefty-y))); DrawTriangle;
1: longGreenDistance := Sqr(Sqr(intLeftX-x) + end;
Sqr(intLefty-y));
2: longGreenDistance := Abs(Sqr(intLeftX-x) + end.
Sqr(intLefty-y));
end;
End Listing Three
By Rand McKinney
nless you’ve been under a rock for the last year, you’ve at least heard
In recent months, a number of Delphi-centric web pages have appeared. Created by Delphi pro-
grammers, consultants, and writers, these web pages provide a wealth of useful information that’s
just a few mouse clicks away. Many sites have Delphi demonstration applications and components
available for download for free, or as shareware. Some provide forums for questions and answers on
Delphi programming, and others are simply marketing vehicles for Delphi application developers.
These web sites literally span the globe: there are Delphi web pages on servers in Australia, Sweden,
Poland, Germany, and across the United States.
Web Pages
The following is an incomplete list of some of the best web pages devoted to Delphi. Each
page has a URL (Uniform Resource Locator) that is essentially the WWW address of the page.
You can type them or use the HTML file provided with this article (see end of article for
details). As of this writing, these URLs are accurate. However, the web is dynamic by nature
so some may have changed.
comp.lang.pascal
The Usenet newsgroup for discussion of Pascal-related topics,
including Delphi.
alt.comp.lang.borland-delphi
C. Rand McKinney is a Senior Technical Writer for the Delphi team at Borland
International. Previously, he helped to document the InterBase 4.0 Workgroup Server
and client tools. He has also worked as an AI researcher and a space systems analyst.
He can be reached at [email protected].
F or the things we have to learn before doing them, we learn by doing them.
— Aristotle, 384-322 BC
Figure 3: You
Figure 7: This
can highlight
code is attached
a word in
to the OnClick
Windows by
event of the
double-click-
Definition
ing on it.
button.
InfoPower
Woll2Woll’s VCL Components for Database Developers
I
f you’ve been waiting for a third-party vendor to
supply an alternative to Delphi’s built-in data begin using its components to build a database application.
access components, your wait is over. Woll2Woll
Enhanced DataSet and DataSource Controls
Software has released InfoPower, a comprehensive At the core of InfoPower are three new data access components:
collection of native VCL components for database TwwDataSource, TwwTable, and TwwQuery. (There are actually
developers. InfoPower consists of 15 data-aware four if you include a new TwwQBE component, but we’ll ignore
components that add powerful new capabilities to it for now.) These new components are enhanced versions of the
Delphi, including a sophisticated data grid, built-in TDataSource, TTable, and TQuery components. They
build upon the basic VCL versions by adding new properties
enhanced DataSet and DataSource components, fil-
and methods. Many of InfoPower’s visual and non-visual com-
tering capabilities, incremental searching, expanding ponents rely on the extended functionality provided by these
memos, editable lookup combo-boxes, and a set of new components to work. The TwwDataSource, TwwTable, and
standard and user-defined search dialog boxes. TwwQuery components are derived directly from their Delphi
counterparts and are 100% backward compatible. Because of
Users familiar with the capabilities of Paradox for Windows’ fil- this, you can substitute the InfoPower versions directly into your
ters and TableFrame object will appreciate this software because it existing forms, including the ones created by the Form Expert.
brings similar capabilities to Delphi. InfoPower’s components are
also optimized to account for client/server operations so you can While TwwDataSource and TwwQuery serve mainly to interface
access remote data with minimal overhead. In addition, applica- with other InfoPower components, TwwTable adds important
tions you create with InfoPower can be distributed royalty-free. new functionality. A new Filter property has been added to sup-
port the ability to filter tables (explained below). A new
Installing InfoPower wwFindKey method has also been added to permit optimal
Installing InfoPower on your system is easy if you follow the searching against SQL tables. This new method replaces Delphi’s
instructions in the user manual. InfoPower comes with the typical FindKey method that is slow when used against large remote
Windows-hosted installation program that copies the necessary files data sets. Finally a new Pack method lets you remove dead space
to your hard disk. Once installed, you’re instructed to complete a from Paradox and dBASE tables.
series of manual steps to create an alias for the optional demonstra-
tion programs, install the components into Delphi’s component Access to BDE Filters
palette, and merge the InfoPower on-line help into Delphi. By default, Delphi lets you use ranges and queries to select a sub-
set of a table. The Borland Database Engine (BDE) also supports
For those tempted to skip the user manual and dive right into the concept of filters. Unlike ranges, filters are not restricted to
the sample applications, be sure to follow these steps or you indexed fields. A filter offers the flexibility of a query because a
might have trouble running the demonstration files. These files filter lets you use an expression to specify a criteria for any or all
require you to create an alias called InfoDemo that points to the the fields in a table. A filter also provides you with an editable
directory containing the sample tables. result set, which may not always be possible with a query.
Delphi lets you use filters, but only if you are willing to do some Sophisticated Database Grid
engine-level programming. To implement filters in Delphi, you The centerpiece of InfoPower’s enhanced data controls collection
must program complex data structures and make BDE calls. If the is the TwwDBGrid component. This component extends upon
idea of using pointers scares you, you’re better off using the filter- Delphi’s built-in TDBGrid by adding significant new functionali-
ing capability in InfoPower. InfoPower makes using filters a breeze. ty, such as the ability to attach combo-boxes, checkboxes, and
memos to the grid. Fields from multiple tables in a one-to-one
To create a filter, simply use the TwwTable component’s Filter prop- relationship can be displayed on the same row without coding. In
erty and FilterActivate method. The Filter property is a TStringList addition, you can define fixed, non-scrollable columns in the grid
so use the Add method to assign criteria to the filter. Valid criteria so they always appear as the user pans through the fields. You can
statements can contain relational operators (< , > , <= , >= , = ,<>) also alter the default appearance of the grid, including word-wrap-
and the AND/OR logical operators. After specifying the filter, call ping the titles and changing individual cell colors. Another impor-
FilterActivate to apply the criteria against your table. The following tant, but subtle, improvement is the accurate display of the vertical
Object Pascal code illustrates how to create a filter that shows scrollbar’s thumb when scrolling through Paradox or dBASE
orders that have amounts exceeding 100 dollars: tables. Delphi’s TDBGrid leaves the thumb position “stuck” in the
center of the scrollbar. Many of InfoPower’s customers will be pur-
procedure TForm1.Button1Click(Sender: TObject);
begin
chasing the software just to obtain this component.
with TwwOrdersTable1 do
begin
Filter.Clear;
Filter.Add('Amount > 100');
FilterActivate;
end;
end;
Filters also have some disadvantages. They tend to be slower than A TwwDBGrid component with customized titles and a combo box.
ranges because each record is evaluated as it is seen. Also, when you
apply a filter on a table, the RecordCount property reflects the total TwwDBGrid inherits from TDBGrid, so it works just like the
number of records in the table, not the number of records matching built-in grid. To use it, you assign a TwwDataSource to the grid’s
the filter. To obtain an accurate record count, you can use Object DataSource property. In turn, the TwwDataSource component’s
Pascal to iterate through the result set and count the records. DataSet property must refer to a TwwTable, TwwQuery, or
TwwQBE. The grid has several properties and events that you
New QBE Component program to alter its behavior and appearance.
Besides TwwTable and TwwQuery, InfoPower offers a third
data access component: TwwQBE. This useful component Enhanced CheckBoxes, Combo-boxes, etc.
enables you to use Paradox-style query-by-example (QBE) InfoPower makes it convenient to display a field in the grid as a
statements to query tables. This is a welcome addition to checkbox, combo-box, or lookup combo. Each field in a
Paradox for Windows developers who are familiar with QBE TwwDBGrid has a Control Type setting. You can access this set-
and want to make the transition to Delphi. Not only does ting from the Select Fields dialog box. This dialog box appears
TwwQBE let you run existing QBE statements unmodified, it when you click on the grid’s Selected property or the DataSet’s
also permits the creation of some queries not possible when ControlType property. The field’s control type determines the
you use SQL syntax against local tables (i.e. the dreaded appearance of its cell. This can be the default edit box, a checkbox,
“Capability Not Supported” message). or one of InfoPower’s several combo-box or lookup components.
If what you want is an edit box or checkbox, you set the control
To use the TwwQBE component, Woll2Woll recommends type to Field or CheckBox, respectively, and you’re finished.
you create the QBE statement from the Database Desktop or
Paradox for Windows, then load it into the QBE property of To display a combo-box or lookup, you must first bind one of
the component. You can also use the Add method to build InfoPower’s combo-box or lookup components to the field, then
the QBE statement at run-time since the QBE property is a leave it on the form with its Visible property set to False. Next
TStringList. Finally, the AnswerTable and AuxiliaryTables you use the Select Fields dialog box to set the field’s ControlType
properties are available so you can define the result set and property to Combo or LookupCombo, depending on the compo-
any auxiliary tables arising from INSERT, DELETE, or nent type you want to use. After you do this, a second drop-
CHANGETO operations. down list appears showing the names of all the components that
Additional Lookup Dialogs Also in the documentation is a tips and techniques section
To round off its collection of that describes how to achieve common functionality. This
lookup components, information is also available in the on-line help system. Since
InfoPower includes the on-line help is integrated into Delphi’s IDE, the reference
TwwDBLookupComboDlg and is always conveniently available. A
TwwLookupDialog. These two final valuable reference is
components offer functional- InfoPower’s source code that is
ity similar to This wwDBLookupComboDlg com- available as a separate purchase. If
TwwDBLookupCombo ponent displays a calendar. you are interested in learning about
InfoPower is a collection of data-aware
because they let you display how InfoPower works, or how to components that enhance Delphi’s exist-
choices from a lookup table. What’s different is they let you make direct BDE calls, you should ing VCL components, including an
enhanced data grid, database filtering,
present the lookup table as a searchable grid in a dialog box buy this option. lookup combo-boxes, expanding memo
dialogs, and incremental search compo-
instead of a drop-down list. The dialog box can contain an nents. The package has a well-written
index selection combo-box and up to two optional command InfoPower ships with a series of user manual and a comprehensive collec-
tion of sample code. Source code is avail-
buttons that you can program responses to when they are small Delphi sample programs that able separately.
pressed. TwwDBLookupComboDlg is bound to a data field, just illustrate the features of each com- Woll2Woll Software
as TwwDBLookupCombo is. When the user clicks on the ellip- 1032 Summerplace Drive
ponent. Although installation of the San Jose, CA 95122
sis button, the dialog box containing the grid appears. sample programs is optional, I rec- Phone: (800) WOL2WOL, or
(408) 293-9369
ommend running through each one
TwwDBLookupDialog displays a similar dialog box, but is not Price: Introductory price US$149 expires
to get a quick overview of August 31, 1995; source code US$79;
bound to a data field. It’s a non-visual component just as InfoPower’s capabilities. By study- after August 31, US$199, with source
code at US$99 (30-day money-back
Delphi’s common dialog components, so you control it using ing each example, you can learn guarantee).
code. To display the dialog box, you call the component’s how to use InfoPower most effec- Free technical support is provided
Execute method. When the user is finished with the dialog tively in your application. In fact, via:
CompuServe: 76207,2541
box, you can query the component’s LookupTable property to prospective customers can obtain a Internet: [email protected]
Fax: (408) 287-9374
determine what record was selected. TwwDBLookupDialog is demonstration version, Voice: (408) 293-9369
very flexible because you can control exactly when the lookup InfoDemo.ZIP, from CompuServe
Conclusion
InfoPower provides capabilities essential to every professional
database developer. The components are complete and well
thought out, significantly enhancing the development process.
The documentation, help system, and sample code serve as a
very good tutorial in helping new users master the product.