Professional Documents
Culture Documents
Delphi Informant 95 2001
Delphi Informant 95 2001
Delphi Informant 95 2001
From Database
to Browser
Generating HTML from Table Data
ON THE COVER
6 From the Database to the Browser — Keith Wood
41 Case Study — David Rippy
Mr Wood combines his THTMLWriter component with TDataSource to
Lincoln Property Company needed a way to charm potential apartment
create a dynamic, data-aware component for your Delphi Web projects.
tenants. Mr Rippy reports that Ensemble’s solution, a Delphi-powered
kiosk, has proven both congenial and frugal.
FEATURES
11 Informant Spotlight — Robert Vivrette 44 Delphi at Work — Shamiq Cader
Fingerpainting for Adults: Mr Vivrette shows us how easy it is to Too short on time to deal with “year 2000” problems? Mr Cader’s dis-
manipulate colors with Delphi — and builds a custom color property cussion of how to manipulate date and time in Object Pascal can help
editor to prove it. you come to terms with the ticking clock.
By Keith Wood
Dynamic Web pages are becoming more HTML tables are delimited by the <table>
common every day. Organizations are plac- and </table> tags. Within these bound-
ing data on the Web for clients and other aries, tables comprise rows, denoted by the
interested people to access. Managing all <tr> tag, which are composed of headings
this data is quite a task — one well-suited <th>, or cells <td>. Headings and cells con-
to a database. Combining the abilities of tain text and other HTML tags. Additional
the THTMLWriter and TDataSource com- table elements include borders that can be
ponents makes it easier to create your Web sized and made invisible (or colored in some
site and keep it up-to-date. browsers), and a caption that is applied to
the entire table. All table elements can be
THTMLWriter aligned horizontally and vertically.
To recap, the THTMLWriter component
provides numerous methods that either An example of HTML code for a table is
format text into HTML code as strings, or shown in Figure 1. Figure 2 lists the
write HTML to a file for viewing. This THTMLWriter methods used to produce
article is about those methods related to tables. Our new component invokes
HTML tables. these methods at the appropriate times to
present the information from the data-
base in HTML.
<table border=2 width=100% bgcolor=#FFFFFF>
<caption align=center valign=top>A sample HTML table HTML Data Source
</caption>
<tr><th align=right>Part No</th><th>Description</th>
We want the ability to take data from any
<th align=right>List Price</th></tr> source available to Delphi and display it as
<tr><td align=right>900</td><td>Dive kayak</td> an HTML table. Fortunately, Delphi is set
<td align=right>3999.95</td></tr>
<tr bgcolor=#FF0000><td align=right>912</td>
up to easily interact with a large variety of
<td>Underwater Diver Vehicle</td> data sources while hiding most of the opera-
<td align=right>1680.00</td></tr> tion’s complexities. TTable provides an inter-
<tr><td align=right>1313</td><td>Regulator System</td>
<td align=right>250.00</td></tr>
face to database tables (desktop or server),
<tr><td align=right>1314</td> while TQuery gives us access to the same data
<td>Second Stage Regulator</td> via SQL. These components are derived
<td align=right>365.00</td></tr>
</table>
from TDataset, which encapsulates the com-
mon interface for any data source, allowing
both to be treated the same when accessing
Figure 1: A sample table description in HTML.
the data (polymorphism in OO terminology).
By Robert Vivrette
Fingerpainting
Building a Custom Color Property Editor
Well, it still feels cool, but the tools have How Much Money Is $00FF80FF?
changed. Instead of your fingers, you now What happens when you select a color for a
have the Microsoft Windows color system. component, but the color is not one of the
Granted, the system isn’t perfectly designed, pre-defined ones? (By pre-defined, I mean of
but it does allow you to fiddle with colors course, colors such as clYellow, clSilver, and
and come up with new ones. clLime.) Let’s say you want to change a form’s
color via the Object Inspector. You double-
In this article, we’ll investigate how colors are click on the value for the form’s Color proper-
represented in Delphi (and Windows), and ty to display the Color dialog box and see
how you can manipulate them to your that some of the available colors don’t match
advantage. At the end of the article, we’ll the standard set of colors in Windows. If you
cover how to integrate your custom colors select one of these “non-standard” colors, you
directly into the Delphi IDE with the might expect the Object Inspector to display
ColorEdt property editor. a descriptive word for your selection (e.g.
clPaleYellow or clUglyPink). Instead, the
Object Inspector displays something like
$00FF80FF (see Figure 1). What the heck is
this value? And what good is it to you?
interface
uses
WinProcs,Graphics;
implementation
end.
$0 $0 $FF $0
Figure 3: The AdjustColor function.
uses
Into Action SysUtils, WinTypes, WinProcs, Messages, Classes,
Let’s put this function into action with two examples. Graphics, Controls, Forms, Dialogs, ColorAdj, ExtCtrls;
play. If you are viewing 16 or 256 colors, the panels will procedure TForm1.MainPanelClick(Sender: TObject);
be displayed using dithered colors. This is Windows’ way begin
of simulating another color by using only a limited set of if ColorDialog1.Execute then
with ColorDialog1 do begin
primary colors. If your system is displaying 16 million col- MainPanel.Color := Color;
ors or higher, then you’ll see perfectly solid color represen- Panel1.Color := AdjustColor(Color,1.0);
tations for each panel. The 16-million color depth can dis- Panel2.Color := AdjustColor(Color,0.9);
Panel3.Color := AdjustColor(Color,0.8);
play these colors accurately because it’s using exactly the Panel4.Color := AdjustColor(Color,0.7);
specified values of red, green, and blue. The 16 and 256 Panel5.Color := AdjustColor(Color,0.6);
color depths must map the selected RGB values to the Panel6.Color := AdjustColor(Color,0.5);
Panel7.Color := AdjustColor(Color,0.4);
closest one available in either the current Color palette or Panel8.Color := AdjustColor(Color,0.3);
the System palette. Figure 6 shows the source code for the Panel9.Color := AdjustColor(Color,0.2);
Color Demo application. Panel10.Color := AdjustColor(Color,0.1);
end;
end;
The second demonstration program (DEMO2.DPR) shows
how you can put gradient fill patterns on your forms. end.
unit Demo2u;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls,
Buttons, ExtCtrls, ColorAdj;
type
TForm1 = class(TForm)
Label1: TLabel;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
procedure FormPaint(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
private Figure 9: The interface for the ColorEdt property editor.
{ Private declarations }
public
{ Public declarations }
end;
draw variety, allowing us to include small swatches of the col-
ors. This will help a user visually associate the custom colors
var with their constants. Clicking on the color panel activates a
Form1: TForm1;
TColorDialog (provided in Delphi) that allows the user to
implementation select and mix custom colors. After selecting a color in this
way, a user can type in a color constant and click on the Save
{$R *.DFM}
button to add the new color to the list of those currently
procedure TForm1.FormPaint(Sender: TObject); defined.
var
a : Integer;
begin
Although the property editor completely integrates custom
with Inherited Canvas do colors into the Delphi IDE, it must perform one addition-
begin al task to be a complete solution. To allow a user to speci-
Pen.Style := psClear;
for a := 0 to ClientHeight div 2 do
fy any of the constants within a piece of Delphi source
begin code (e.g. a unit or project file) the compiler must know
Brush.Color := how these constants are defined. As a result, the property
AdjustColor(clLime,a*2/ClientHeight);
Rectangle(0,ClientHeight-a*2,
editor includes a button (named Save Source) that writes
ClientWidth+1,ClientHeight-a*2+3); out the currently-defined custom color constants as a
end; source code unit. Then, by adding this unit to any project,
end;
end;
a user can continue to use the custom colors even outside
the IDE.
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
Close;
The interface to the property editor is straightforward. Two
end; principal ways of activating it are to enter a question mark
( ? ) instead of a color name. The second is to type any
end.
color constant into the Object Inspector that has not
already been defined. For the value of a form’s Color prop-
Figure 8: A sample program showing how to do gradient fills.
erty, for example, you could type in clPeriwinkle. This
color is not one of the standard color constants, and it
To build the color property editor, we must first create the wasn’t already specified as a custom color. Therefore, the
interface that allows us to select the colors (see Figure 9). property editor presents our dialog box to allow the user to
This is often the best approach for creating a dialog box- pick the color. To enforce the naming scheme that Borland
based property editor such as this. After creating a stand- has established, a color constant is only recognized as such
alone application, you’ll find that converting it to a property if the first two characters of the name are cl.
editor is fairly simple.
When the custom property editor is displayed, the user can
A name for the color the user has specified will appear in the simply click on the panel under the constant name. This dis-
edit field at the top of the form. The panel below it will show plays the common Color dialog box provided by Windows.
the currently-selected color, and the list box on the right will The user can then select one of the basic colors shown, or create
show the custom colors that have already been defined. a new color by clicking the Define Custom Colors button. After
closing this dialog box, the panel that had been clicked will be
Friendly, Friendly, Friendly the selected color. By clicking the Save button, the property
Some selected enhancements make the property editor as user- editor adds that color constant definition into its internal array
friendly as possible. The list box on the right is of an owner- (which is then displayed by means of the list box). Clicking the
GetValues is a procedure that the Object Inspector uses When Delphi has a TColor value that must be converted, it
whenever it must have a drop-down list property filled calls this function. First, the program calls Delphi’s
with values. That is exactly the type of property we have ColorToIdent function that looks up TColor values that
here. First, let’s examine how we modify the behavior of Borland already defined in the Graphics unit. The
GetValues: GetOrdValue function is declared in the DsgnIntf unit and
returns the numeric representation of a property. If
procedure TRVColorProperty.GetValues(Proc: TGetStrProc);
var
ColorToIdent finds the color constant (i.e. if it’s one of the
I : Integer; stock colors) then GetValue returns the string representation
begin of the color in Result. If it’s not found, however, we need to
for I := 0 to RVColorsList.Count-1 do
look through our string list (RVColorsList) to see if it’s a cus-
Proc(FirstHalf(RVColorsList[I]));
inherited GetValues(Proc); tom color. If it’s not found in that string list, then we need to
end; take the numeric value of the color and format it into hexa-
decimal notation (just as Delphi always does with unrecog-
Within the ColorEdt property editor, a string list is con- nized colors). All that was changed here is the section that
figured to hold the custom color constants that have been searches RVColorsList. The rest is the code in TColorProperty.
Robert Vivrette is a Senior Programmer/Analyst for Pacific Gas & Electric and
Technical Editor for Delphi Informant. He is also author of a free, online journal,
The Unofficial Newsletter of Delphi Users, that can be found at
http://www.informant.com/undu. He can be reached on CompuServe at
76416,1373 (on the Internet, use 76416.1373@compuserve.com).
By Ray Lischner
Subproperty Editors
Undocumented Tricks for Creating Surrogate
Components
ou’re finishing a nifty new calendar component. All you need to complete
Y your finest work is a property editor that lets the user choose the date the
calendar displays. You consult the Component Writer’s Guide and find the
paSubProperties flag. Great! It’s just what you want. The user can double-click
the Date property to expose the Day, Month, and Year properties.
But, how do you create the subproperty edi- class TPropertyEditor, or one of its subclasses.
tors? The short answer is ... you can’t. The Delphi defines a property editor class for
only property editors that can have subprop- every type, and you can register a property
erties are TClassProperty and TSetProperty editor class for a specific property, or any
(and their subclasses). If you want to define property of a particular type.
subproperties for any other type of property,
such as TDateTime, you’re out of luck. Properties can in turn have their own proper-
ties, just as components have properties. A
Or are you? Through a little clever program- property of a property is called a subproperty,
ming, you can create fake subproperties. This as illustrated in Figure 1. In the pre-defined
article instructs you how to create subproper- property editors, the only uses for subproper-
ty editors for any type of property. ties are in set and class properties. A set’s sub-
properties are the set elements, which use the
Subproperty Review TSetElementProperty editor. The subproper-
Let’s quickly review properties and subprop- ties of a class property are the published
erties. The Object Inspector displays the properties of the class.
published properties of a component. When
you write a custom component, you can also Figure 2 illustrates class and set subproperties
supply property editors to make it easier to for the TFont class and the TFontStyles set
use your component at design time. You can type. There are other uses for subproperties,
also write property editors for any of the though. For example, your new calendar
standard components, and you can replace component would be easier to use if the Date
standard property editors with your own. property had subproperties for its constituent
Day, Month, and Year. This is a natural use
Every property has a type — specifically, an for subproperties. Unfortunately, Delphi
integer, floating point (except Real ), enumer- hides the pieces necessary to define your own
ated, set, character, class, method, or string. subproperties.
In Delphi 2, you can also have Variant or
WideChar properties. Each property has a Why Subproperties Are Hard to Define
property editor, which is an instance of the It’s instructive to see how Delphi hides the
TLabel component
Properties of TLabel
Subproperties of Font
Subproperties of FontStyles
So how does the Object Inspector create its property editors? Let’s look at how to define a surrogate component for
The same way you do — by calling GetComponentProperties. TDateTime that would allow users to set the parts of a date
This routine, shown in Figure 3, is the only way to create a and time in the Object Inspector, while still allowing for con-
property editor object. The only problem is that it creates a venient use in the component’s code. This example creates a
property editor for every property of a component. This is subproperty for all the constituent parts of the TDateTime
fine for the Object Inspector, but a subproperty is different. class. It’s a simple exercise to create a property editor for just
the date or time part of TDateTime. This is left as an exercise When the date and time are set, the property editor must be
for the reader. informed of the new value. This happens in the SetDateTime
{ When the user changes a constituent part of the DateTime, { Return the property editor attributes, including
notify the master property editor, so it can update the paSubProperties. }
property value string. As per standard property editor function TS_DateTimeProperty.GetAttributes :
usage, changing a property value sets the value for all TPropertyAttributes;
selected property editors. } begin
procedure TS_PropDateTime.SetDateTime(Value: TDateTime); Result := inherited GetAttributes + [paSubProperties];
begin end;
if fDateTime <> Value then
begin { The value 0.0 is not a valid TDateTime (month=0, day=0),
fDateTime := Value; so just use an empty string. }
if Editor <> nil then function TS_DateTimeProperty.GetValue: string;
Editor.SetDateTime(Value); begin
end; if GetFloatValue = 0.0 then
end; Result := ''
else
Figure 6: Setting the date and time and updating the property Result := DateTimeToStr(GetFloatValue)
editor. end;
Figure 7: Declaration of TS_DateTimeProperty. { Update all the hidden, child property editors, if any. }
procedure TS_DateTimeProperty.SetChildValues;
var
procedure (see Figure 6). The property editor creates an I: Integer;
instance of the surrogate component, and calls begin
if ChildList <> nil then
GetComponentProperties to retrieve the component’s property
for I := 0 to ChildList.Count-1 do
editors as though they were subproperty editors. Because the TS_PropDateTime(ChildList[I]).DateTime :=
user can select multiple components, the property editor GetFloatValueAt(I);
end;
must create a TS_PropDateTime component for each compo-
nent selected by the user. { When Date/Time changes, notify all hidden components. }
procedure TS_DateTimeProperty.SetDateTime(Value: TDateTime);
begin
Figure 7 shows the TS_DateTimeProperty editor. The list
SetFloatValue(Value);
of surrogate components is stored in the ChildList field. SetChildValues;
end;
Get Set
Implementing the new GetValue and SetValue functions is Figure 9: Using the list of child components for
easy, as shown in Figure 8. A zero date time is displayed as TS_DateTimeProperty.
an empty string, because it’s not a valid TDateTime value.
Similarly, an empty string is stored as a zero TDateTime the property editor by calling the SetDateTime method.
value. You must also override GetAttributes to set the This method, in turn, propagates the change to the other
paSubProperties attribute. child components. These methods are shown in Figure 9.
When the TDateTime value changes in the property edi- The hard part is having the Object Inspector request the
tor, the SetValue method calls SetChildValues. This notifies subproperties by calling the GetProperties method. If the
the surrogate components of the new value. When the user has edited the subproperties earlier, the list of surro-
property editor is destroyed, the list of child components gate components already exists. If it doesn’t exist, it’s creat-
must also be freed. You have already seen that when any ed. Because the user can select multiple components, each
child component’s value changes, the component notifies with a different TDateTime value, every TS_PropDateTime
21 November 1996 Delphi Informant
OP Tech
{ When the Object Inspector requests the subproperties, it { Typecast any property editor to TExposePropertyEditor to
is time for TS_DateTimeProperty to do its thing. Create a expose the property's NameIndex fields from the PropInfo.
hidden TS_PropDateTime object to parallel each component This is just a clever hack to gain access to an otherwise
that is currently selected. Then request the property protected field of TPropertyEditor. }
editors for the hidden TS_PropDateTime objects. Unfortu- type
nately, GetComponentProperties sorts the properties TExposePropertyEditor = class(TPropertyEditor)
alphabetically, which makes the subproperties more dif- public
ficult to use. Instead, use the property's run-time type function NameIndex: Integer;
info (TPropInfo) to get its NameIndex. The NameIndex end;
gives the order in which properties are declared, which
is the order in which these particular properties should function TExposePropertyEditor.NameIndex: Integer;
be shown to the user. } begin
procedure TS_DateTimeProperty.GetProperties(Proc: Result := GetPropInfo^.NameIndex
TGetPropEditProc); end;
var
PropDateTime: TS_PropDateTime;
I: Integer; Figure 11: Exposing a protected method with
begin TExposePropertyEditor.
if ChildList <> nil then
SetChildValues
value changes. The ChildList field holds the list of surro-
else
begin gate components.
ChildList := TComponentList.Create;
for I := 0 to PropCount-1 do begin
The GetProperties method must issue a callback procedure
PropDateTime := TS_PropDateTime.Create;
PropDateTime.DateTime := GetFloatValueAt(I); for each subproperty editor. Start by calling
PropDateTime.Editor := Self; GetComponentProperties, which creates and initializes the
ChildList.Add(PropDateTime);
property editors for the subproperties. Set the filter to
end;
end; tkInteger, so GetComponentProperties returns property edi-
tors only for the integer-type properties. All the con-
SubProps := TList.Create;
stituent date and time properties are integers, but so is the
try
{ Build a list of subproperty editors, Tag property. Remove the Tag property by looking at its
in declaration order. } name and freeing the property editor if it is Tag.
GetComponentProperties(ChildList, [tkInteger],
Designer, GetSubProps);
for I := 0 to SubProps.Count-1 do All in Order
{ The subproperty editor list has holes for By default, these property editors are sorted in alphabetical
Name and Tag. Skip them. }
order (Day, Hour, MilliSec, Minute, Month, Second, Year). They
if SubProps[I] <> nil then
Proc(TPropertyEditor(SubProps[I])); are most useful, however, when they are ordered by magnitude
finally (i.e. Year, Month, Day, Hour, Minute, Second, MilliSec), so you
SubProps.Free;
should rearrange them. Thus, GetSubProps stores the property
SubProps := nil;
end; editors in a list, SubProps. The index of each property editor in
end; the list is given by the editor’s NameIndex, which is its index in
declaration order. Remember that in TS_PropDateTime, the
{ Discard property editor for Tag, so only the ones that
are specific to date and time are shown to the user. } order of the published properties is the same order the Object
procedure TS_DateTimeProperty.GetSubProps( Inspector should use. This leaves holes in the list (e.g. for Tag),
PropEdit: TPropertyEditor);
so skip over any nil items in the list.
var
Index: Integer;
begin After the list is built, issue the callback procedure Proc for
if CompareText(PropEdit.GetName, 'Tag') = 0 then
each subproperty. The Object Inspector can then display the
PropEdit.Free
else property values for the subproperties. Figure 10 shows the
begin GetProperties and GetSubProps methods.
{ Keep the sub property editors in
declaration order. }
Index := TExposePropertyEditor(PropEdit).NameIndex; Into the RTTI
if Index >= SubProps.Count then The NameIndex field is part of the Run-Time Type
SubProps.Count := Index+1; Information (RTTI) for a property, in the TPropInfo record.
SubProps[Index] := PropEdit;
end; (This information is not documented, but you can learn
end; about it in the TypInfo.int or TypInfo.pas file. Look in
Delphi’s \Source or \Doc directory.) The GetPropInfo method
Figure 10: Getting the subproperties for TS_DateTimeProperty. returns a pointer to the TPropInfo record for the property.
{ Retrieve all the month names, for the value list. } type
procedure TS_MonthProperty.GetValues(Proc: TGetStrProc); TS_DateTimeTester = class(TComponent)
var private
I: Integer; fDateTime: TDateTime;
begin public
for I := Low(LongMonthNames) to High(LongMonthNames) do constructor Create(Owner: TComponent); override;
Proc(LongMonthNames[I]); published
end; property FancyDateTime: TDateTime read fDateTime
write fDateTime;
property DateOnly: TDateTime read fDateTime
Figure 12: Editing a month by name with TS_MonthProperty. write fDateTime;
end;
NameIndex field. By casting any property editor to
implementation
TExposePropertyEditor, you can call the NameIndex method.
Because TExposePropertyEditor derives from TPropertyEditor, { An object is initialized to zero, but that is not a valid
it can call a protected method, such as GetPropInfo. Because TDateTime value, so initialize the TDateTime to something
that makes sense, such as the current date and time. }
the NameIndex method is not virtual, there is no problem constructor TS_DateTimeTester.Create(Owner: TComponent);
with the fact that the property editor object does not derive begin
from TExposePropertyEditor. inherited Create(Owner);
SimpleDateTime := Now;
end;
Not without Risk
However, a warning is in order. If you were to use the as oper- end.
ator to cast the property editor to TExposePropertyEditor, Figure 14: The unit S_Test.pas used to test the TDateTime prop-
Delphi would raise a run-time exception. Thus, this is a dan- erty editor.
Cross-Platform Delphi
or, IFDEFing for Fun and Profit
t seems we are forever writing applications for two platforms: the one
I that dominates the marketplace (currently Windows 3.1), and the one
that will (Windows 95). Specifically, Delphi programmers must be able to
move easily between the 16-bit, segmented-memory environment of
Delphi 1, and the 32-bit, flat-memory world of Delphi 2 — often with the
same application.
However, compiling 16- and 32-bit executa- obstacle preventing companies from upgrad-
bles from one set of source requires planning, ing their operating systems. Now, however,
and sometimes, compromise. This month’s price is not nearly as big a factor.
column discusses why this is increasingly so.
It also reviews some of the issues and tech- The second development is the release of
niques you need to keep in mind when Windows NT 4.0. Many companies have
building cross-platform applications. been waiting to make their operating system
decision based on this new release of NT.
Background Now that it’s available, some corporations
Delphi 2 has been available for almost nine will upgrade to NT 4.0, while others will
months. Unfortunately for Borland, the decide to migrate to Windows 95.
acceptance of Microsoft Windows 95 is less
widespread than expected, especially among As a result of these developments, it’s likely
large corporations. Likewise, the number of most independent consultants, as well as
end-users running Windows NT is relatively many corporate developers, will find them-
small. Consequently, not many Delphi devel- selves building Delphi applications that must
opers have been developing applications run on both 16- and 32-bit operating sys-
exclusively for the 32-bit platform, which is tems. Unfortunately, unlike Borland’s C/C++
the only platform that can run Delphi 2. In product line, Delphi 2 doesn’t permit you to
fact, based on my interactions with other compile both. Instead, a developer must
Delphi developers, most continue to pro- compile an application using Delphi 1 to
gram exclusively in Delphi 1. create a 16-bit .EXE, and again in Delphi 2
to create the 32-bit version.
This situation won’t last forever; recent devel-
opments pave the way for an increase in 32- Using two versions of Delphi to compile an
bit applications. The first is the cost of RAM application in 16 and 32 bits is often more
and hard disk space. Because Windows 95, than inconvenient. It can involve major com-
and particularly Windows NT, require a lot promises and time-consuming coordination.
of both, the cost of these resources was one The reason is that Delphi 2’s feature set is not
By James Callan
This article explains how you can use inheri- ferent kinds of transactions. Figure 1 illus-
tance, polymorphism, and specialization in trates one such transaction in a commercial
your client/server applications. We’ll begin with product named QuickXpense.
concrete examples of these techniques in action,
and provide a firm conceptual foundation. If you’re like me, you’re under a constant bar-
Moving to database modeling, we’ll introduce rage to add the latest clever features to your
subtypes. We’ll end by creating an Extra projects, but what’s a software designer to do?
Sensory Perception (ESP) game that builds on Lose more sleep? Drink more Jolt Cola? There’s
all the ideas introduced in this article, and a better way, using — yes — polymorphism,
shows you how to add Mighty Morphing and Delphi makes it easier than you think.
Power Grids to your next application.
Polly Who?
What, you may ask, are Mighty Morphing I’m amazed how academics can obfuscate.
Power Grids? They’re those super-snazzy Let’s define polymorphism simply and directly.
dual-row grids in Quicken and other “killer” Literally, the term means having many forms.
applications that visibly change, depending In the world of objects, it means you can ask
on what kind of data you add. These applica- two different objects to do the same thing,
tions use one simple form for dozens of dif- and both will respond sanely.
To build your Psychic Diviner application, create the tables Next, add a MainMenu component to the form, then dou-
shown in Figure 6 using the Database Desktop. Under Tools | ble-click on it to create the form’s main menu. Using the
Alias Manager create a new public alias named SubTypes. Set its Menu Designer, add &File, &View, and &Help as top-level
path to the directory where you have stored the Tests and Trials menu options. Under File add &New and &Delete, followed
tables. Use the Image Editor (see Figure 7) to create 32x32 pixel by a separator (insert a dash character for its Caption), and
bitmaps of a red triangle, a blue cross, a lime circle, three purple E&xit. Add &Accuracy under View and set its shortcut key to
wavy lines, a yellow star, and a magenta square. (The simplest 1 via the drop-down list of keys. Lastly, add an &About
way to create the star is to draw a pentacle and erase the interior option under Help, and close the Menu Designer.
lines.) We’ll use the values T, C, E, W, S, and R in our Trials table
to represent each of these symbols, respectively. On Panel2, place a third Panel and a Button. Set Panel3’s
Alignment property to taLeftJustify, its BevelOuter property to
Forming the Form bvLowered, and its Font.Color property to clBlue. Name the
Start a new project. On the default form, drop two Panel com- Button OkBtn and set its Caption to &OK. Next, make sure the
ponents, aligning one to the top of the form and the other to Caption properties for Panel2 and Panel3 are blank. The lower
the bottom. On the top Panel, place a DBLookupComboBox, panel should now match the one in Figure 8.
a DBEdit, and two Labels. Using the Object Inspector, name
the DBLookupComboBox TestNameLKUFld and the DBEdit On the main form, just above Panel2, drop an additional
control TestDescriptionFld. Set the Caption property of the Panel and name it EditPanel. On EditPanel, place six
first Label to Test &Name:, and the second to BitBtn components and space them equally. Set their
&Description:. Then change their focus controls to the Height and Width properties to 38 and make their Caption
DBLookupComboBox and DBEdit controls, respectively. properties blank; then set their Glyph properties to the
Make the value for the Panel’s Caption property blank and then bitmap files that you created earlier in the Image Editor.
set its BevelOuter property to bvNone. Resize and position the Set the Tag properties for the BitBtn components to 1
controls on the Panel to resemble Figure 8. through 6 (we’ll use the Tags later) and name them
Begin Listing Two — The Psychic Unit function GetBitMap(Symbol: string): TBitMap;
unit Psychic; end;
interface var
Form1: TForm1;
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, implementation
Forms, Dialogs, DBCGrids, Buttons, StdCtrls, Menus, Mask,
DBCtrls, ExtCtrls, DBTables, DB, Gauges { Add to list }; {$R *.DFM}
end.
By David Rippy
— Henry Ford
How do I create a table at run time? ponent created by the code. (When you add
One of the most important aspects of pro- a Table component at design time, these
gramming in Object Pascal is gaining a firm units are added automatically.)
understanding of creating and freeing com-
ponents at run time. A classic example is to The code in Figure 2 is attached to the
programmatically create a Table object that is OnClick event handler of the Populate ListBox
freed once it isn’t needed. This is more effi- button. This code creates the temporary
cient than adding a Table component to the TblTemp Table and assigns the necessary
form. In addition, the form is less cluttered properties, iterates through the FRUIT.DB
and easier to work with at design time. table to populate ListBox1, and finally closes
and frees the table. The first key statement:
The purpose of this example is to create a
TblTemp := TTable.Create(Self);
temporary table (TblTemp) to populate
Form1’s ListBox with the contents of the
Name field from table FRUIT.DB (see creates an instance of TblTemp. Note that
Figure 1). Because the table is only needed because we don’t want to assign an owner to
TblTemp, Self is passed to the Create method.
for a short time — to add items to the
ListBox — there is no need to use a Table
component. Instead, the example creates the When you create a component (or any
TblTemp component at run time, then frees
object) explicitly in this fashion, it’s vitally
it once ListBox1 has been populated. important to free the resources it’s using by
calling the Free method. In this code exam-
The first step is to add the DB and ple, the with construct makes the statement
DBTables units to the uses clause. These a bit hard to discern. Outside a with block,
units must be included to use the Table com- the statement would appear like this:
TblTemp.Free;
Figure 3:
SaveToFile You can adjust the blink rate by setting the Interval property
allows you
to save
of Timer1. The lower the value, the faster the Label will
the SQL blink. As you might have guessed, this technique will work
property on any object with a Visible property — not just Label com-
value to a ponents. — D.R. ∆
text file.
Inquire Within
Engaging Kiosk System Reels in Apartment Shoppers
With these pitfalls in mind, LPC partnered printing costs by supplying printouts of
with Ensemble Corporation’s Multimedia floor plans and other literature as needed.
Division to implement a high-profile, state- Equally important is that the system allows
of-the-art kiosk system for the reception modifications to the literature as often as
area of its Knoxbridge rental office. A kiosk necessary, for a fraction of what a print
system combats the “walkout” problem by shop would charge.
entertaining and informing the customer
when the leasing agent is busy. Also, a kiosk These were the goals of the Knoxbridge
system can dramatically reduce off-site project:
reduce printing costs by at least 75 per-
cent,
reduce the number of walkouts by 50
percent,
provide a system that reflects the corpo-
rate image and quality standards of LPC,
provide a modular system that can be
expanded as needed,
provide professional-quality 2D and 3D
graphics throughout the system, and
provide a high-quality musical sound-
track throughout the system.
Implementation
Several development environments were
available for creating interactive applica-
tions, but only Delphi offered both the
The application has a stately appearance on a 21-inch touch- multimedia aspects and the solid database
screen monitor.
By Shamiq Cader
A Matter of Time
Exploring the TDateTime Object
ave you ever tried to add five minutes and 30 seconds to the current
H time? At first this may seem like a trivial computation. If you look clos-
er, however, the complexity of this logical operation becomes clear. For
example, what do you do if the time is 11:55 PM on the 31st of
December? Suddenly, this doesn’t look so trivial.
The Now function returns the entire date/time value: Because 15 minutes equates to 0.0104166 (or 15/60/24) days,
728927.471743171. adding 0.0104166 to DateTime adds 15 minutes to the date
and time entered by the user. The DateToStr and TimeToStr
Displaying the System Date and Time functions will display the result in the appropriate edit boxes.
System date and time are often displayed in an edit box,
label, or memo field. In all cases, the information must be If the user enters 11:55:00 PM into Enter Time and 2/28/96
converted to a string format. Delphi provides several func- into Enter Date, the result displays 12:10:00 AM and 2/29/96
tions to display the system date and time. in New Time and New Date, respectively. Similarly, if the user
enters 11:55:00 PM as the time and 2/28/95 as the date, the
The program in Figure 2 uses result is displayed as 12:10:00 AM and 3/1/95. Thus, we can
the DateToStr function to see Delphi handles every detail, including leap years. (Error
convert the date part of the checking was not implemented in the program referenced here.
TDateTime format into a We’ll discuss exception handling later.) The code for the Add
readable string that can be 15 Minutes program is shown in Listing Four on page 49.
displayed in an edit box.
Figure 2: Extracting and
Similarly, the TimeToStr Formatting the Date and Time String
displaying the system date function converts the time Although several methods exist for formatting the date and
and time. part of the TDateTime for- time string, a convenient way to do this is to use the
mat into a readable string. FormatDateTime function. FormatDateTime can be used in
The string’s format (e.g. 23:00:00, or 11:00:00 PM) is deter- place of DateToStr or TimeToStr to format the display string
mined by Windows settings (which are outside the scope of to any desired form (see Figure 4). For a complete code list-
this article). ing of the associated .PAS file, see Listing Five on page 50.
If yy was used: DT is of type TDateTime, and Year, Month, and Day are of
type Word. DecodeDate is defined as:
FormatDateTime('yy mmmm dd',MyDate)
DecodeDate(DT: TDateTime, var Year, Month, Day: Word);
In all cases, the format Specifier Example Description FileDateToDateTime takes a Longint
strings used should be
d 8 Displays day without leading zero and returns the corresponding
enclosed within dd 08 Displays day with leading zero TDateTime conversion. This value
quotes. ddd Mon Displays day as abbreviation
dddd Monday Displays full day can then be used within Delphi as the
ddddd 1/8/96 Displays date using short-date familiar TDateTime type.
Date. The date consists format
of the date, month, dddddd Monday January Displays date using long-date
8, 1996 format DateTimeToFileDate takes a
year, and day of the
m 1 Displays month without leading TDateTime type and returns a
week. Figure A zero Longint. This Longint can then be
assumes the date is mm 01 Displays month with leading
zero used from within any DOS applica-
January 8, 1996.
mmm Jan Displays month as abbreviation tion as a regular DOS date/time type.
mmmm January Displays month in full
If the date must be yy 96 Displays year in two digits
yyyy 1996 Displays year in full (Note that this article does not intend
displayed as
to show you how DOS stores date and
Monday/08/01/96, Figure A: Formatting dates.
time. This information is available in
the format
most DOS-related materials. The
statement is:
point is that Delphi provides two
functions to convert the DOS date
FormatDateTime('dddd/dd/mm/yy',MyDate)
and time into a format Delphi recog-
nizes, and vice versa.)
The separator used ( / ) could be changed to ( - ) by changing the format string to:
If the time must be displayed as (10-50-00-p), the format statement is: The GetTime function corresponds to
using the Now function followed by
FormatDateTime('hh-nn-ss-a/p',MyTime) the DecodeTime function. Here
Sec100 corresponds to 100th of a sec-
Notice that the separator can be changed by inserting any value between the format ond:
codes.
procedure GetTime(var Hour, Minute,
Delphi’s online Help has a list of the available format strings. To reference them, Second, Sec100 : Word);
search on “formatting data”.
— Shamiq Cader
end.
End Listing Three
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
if ErrorFlag then
{ Error flag was needed to catch the else part }
ShowMessage('Invalid Date')
else
{ If valid display date }
Edit4.Text := DateToStr(DT);
{ Get DayOfWeek integer and pass it into
the case statement }
DOW := DayOfWeek(DT);
{ Match the string }
case DOW of
1 : Edit5.Text := 'Sunday';
2 : Edit5.Text := 'Monday';
3 : Edit5.Text := 'Tuesday';
4 : Edit5.Text := 'Wednesday';
5 : Edit5.Text := 'Thursday';
6 : Edit5.Text := 'Friday';
7 : Edit5.Text := 'Saturday';
end;
end;
end.
End Listing Six
eveloping a successful software application is not unlike engineering a quality automobile. The most respected auto-
D mobile makers in the world — such as Lexus, BMW, and Volvo — are successful because of their ability to com-
bine performance, reliability, and ergodynamics into a marketable package. So too, the most popular software applica-
tions combine these three qualities.
Unfortunately, this is far from typi- among applications. I don’t care enabling you to mix and match
cal of most software applications what I have to do to invoke a File depending on your current tasks.
built today. While most developers Open dialog box, or to navigate
focus on speed and stability, there is among the fields of the dialog, as Furthermore, Help is more accessi-
a tendency to overlook the final ele- long as once I learn how to do it, I ble. In the past, online Help was
ment of this triad: usability. This don’t ever have to learn something always held in a separate application
propensity to dismiss user interface else.” (PC Magazine) window. Applications provided
(UI) issues as trivial is prevalent some degree of context sensitivity,
among developers and IT managers Organization. UIs are becoming but Windows 95 transformed this
alike. And even if Lexus had a supe- better organized in terms of con- to bring Help into the application
rior engine and unrivaled reliability, trols, commands, and online Help. itself. For more information on how
not many people would buy one if Windows controls certainly have to use “What’s This?”-style Help in
it had wooden seats and an AM evolved since Windows 3.0, which your applications, I recommend an
radio. The same principle holds true had little to offer. Windows 3.1 excellent resource named
for your application: no matter made several advancements, particu- WHTHLP.TXT in the Delphi 2
what is under the hood, the applica- larly with the tabbed dialog box. forum (GO BDELPHI32) on
tion will only be accepted if its UI Windows 95, however, goes far CompuServe.
is successful. This month we’ll look beyond this interface metaphor with
at the Windows interface, focusing several new controls, the most sig- Innovation vs. Consistency. Creating a
on emerging trends in interface nificant of which is the Treeview. successful UI involves balancing inno-
design to keep in mind when creat- The Treeview control is significant vation with consistency. A developer’s
ing Delphi applications. because most applications deal with goal should be to produce an innova-
managing organized collections of tive interface, but one that respects the
Similarity. UIs are becoming increas- data, and this data is often assem- boundaries of Windows 95 standards.
ingly similar. Windows applications bled hierarchically in the real world. Don’t get uppity: the same truth
look far more alike today than they did Many types of database applica- applies to automobile makers. You
five years ago. At one time, it seemed tions, for example, could use a may see Lexus come out with state-of-
that every major Windows software Treeview control rather than relying the-art stereo technology next year, but
vendor had their own distinct look and on the ubiquitous table grid. you won’t see them replace the steering
feel, as did Borland with BWCC.DLL wheel or brake pedal with controls
custom controls. Windows 95’s new Commands are also better orga- they believe work better.
interface — along with Microsoft’s nized. In Windows 3.0, most com-
rigid Win95 logo requirements — mands were accessed either through — Richard Wagner
helped curb this trend. While different a top-level menu or through key-
vendors still put their own twists on an board shortcuts. By Windows 3.1, Richard Wagner is Contributing Editor
application’s UI, Windows 95 applica- most applications employed toolbars to Delphi Informant and Chief
tions are remarkably similar. and some even provided context- Technology Officer of Acadia Software in
sensitive, pull-down menus. In the Boston, MA area. He welcomes your
The result is consistency across Windows 95, users have the most comments at rwagner@acadians.com, or
applications. As Charles Petzold flexible interface environment ever; on the File | New home page at
once said, “Even a bad interface is for example, applications typically http://www.acadians.com/filenew/-
good — so long as it’s consistent have multiple, dockable toolbars, filenew.htm.