100% found this document useful (1 vote)
728 views480 pages

Manual Delphi Certificacion PDF

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
728 views480 pages

Manual Delphi Certificacion PDF

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 480

Embarcadero Training Courseware License Agreement

EMBARCADERO - NONSENSE LICENSE STATEMENT AND LIMITED WARRANTY

IMPORTANT - READ CAREFULLY


This license statement and limited warranty constitutes a legal agreement ("License Agreement")between you (either
as an individual or a single entity) and Embarcadero Technologies, Incorporated ("Embarcadero") for the
Embarcadero training courseware product ("Courseware") identified above, including any software, media, and
accompanying on-line or printed documentation.

BY INSTALLING, COPYING, OR OTHERWISE USING THE COURSEWARE, YOU AGREE TO BE BOUND


BY ALL OF THE TERMS AND CONDITIONS OF THE LICENSE AGREEMENT.

Upon your acceptance of the terms and conditions of the License Agreement, Embarcadero grants you the right to
use the Courseware in the manner provided below.

This Courseware is owned by Embarcadero or its suppliers and is protected by copyright law and international
copyright treaty. Therefore, you must treat this Courseware like any other copyrighted material (e.g., a book). The
Courseware is intended for use by an individual person, and you may not resell or use the Courseware for
commercial gain.

You may transfer the Courseware and documentation, in their entirety, on a permanent basis provided you notify
Embarcadero of your intent to transfer, you retain no copies and the recipient specifically agrees to the terms of this
License Agreement. Except as expressly provided in the License Agreement, you may not transfer, rent, lease, lend,
copy, modify, translate, sublicense, time-share or electronically transmit or receive the Courseware, media or
documentation. You acknowledge that the Courseware in source code form remains a confidential trade secret of
Embarcadero and/or its suppliers and therefore you agree not to modify the Courseware or attempt to reverse
engineer, decompile, or disassemble the Courseware, except and only to the extent that such activity is expressly
permitted by applicable law notwithstanding this limitation.

This Courseware is subject to U.S. Commerce Department export restrictions, and is intended for use in the country
into which Embarcadero sold it (or in the EEC, if sold into the EEC).

LIMITED WARRANTY
Embarcadero warrants that the Courseware, as updated and when properly used, will perform substantially in
accordance with the accompanying documentation and the Courseware media will be free from defects in materials
and workmanship, each for a period of ninety (90) days from the date of receipt. Any implied warranties on the
Courseware are limited to ninety (90) days. Some states/jurisdictions do not allow limitations on duration of an
implied warranty, so the above limitation may not apply to you.

Embarcadero's and its suppliers' entire liability and your exclusive remedy shall be, at Embarcadero's option, either
(a) return of the price paid, or (b) repair or replacement of the Courseware that does not meet Embarcadero's Limited
Warranty and which is returned to Embarcadero with a copy of your receipt. DO NOT RETURN ANY PRODUCT
UNTIL YOU HAVE CALLED THE EMBARCADERO CUSTOMER SERVICE DEPARTMENT AND
OBTAINED A RETURN AUTHORIZATION NUMBER. This Limited Warranty is void if failure of the
Courseware has resulted from accident, abuse, or misapplication. Any replacement Courseware will be warranted
for the remainder of the original warranty period or thirty (30) days, whichever is longer. Outside the United States,
neither these remedies nor any product support services offered by Embarcadero are available without proof of
purchase from an authorized non-U.S. source.

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EMBARCADERO AND ITS


SUPPLIERS DISCLAIM ALL OTHER WARRANTIES AND CONDITIONS, EITHER EXPRESSOR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, TITLE, AND NONINFRINGEMENT, WITH REGARD TO THE COURSEWARE,
AND THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY
GIVES YOU SPECIFIC LEGAL RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM
STATE/JURISDICTION TO STATE/JURISDICTION.

LIMITATION OF LIABILITY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO


EVENT SHALL EMBARCADERO OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION,
DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO
USE THE COURSEWARE PRODUCT OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT
SERVICES, EVEN IF EMBARCADERO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
IN ANY CASE, EMBARCADERO'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS LICENSE
AGREEMENT SHALL BE LIMITED TO THE GREATER OF THE AMOUNT ACTUALLY PAID BY YOU
FORTHE COURSEWARE PRODUCT OR U.S. $25; PROVIDED, HOWEVER, IF YOU HAVE ENTERED INTO
AN EMBARCADERO SUPPORT SERVICES AGREEMENT, EMBARCADERO'S ENTIRE LIABILITY
REGARDING SUPPORT SERVICES SHALL BE GOVERNED BY THE TERMS OF THAT AGREEMENT.
BECAUSE SOME STATES AND JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF
LIABILITY, THE ABOVE LIMITATION MAY NOT APPLY TO YOU.

U.S. GOVERNMENT LICENSEES


The Courseware and any accompanying documentation are “Commercial Items”, as that term is defined at 48 CFR
Section 2.101, consisting of “Commercial Computer Courseware” and“ Commercial Computer Courseware
Documentation”, as such terms are used in 48 CFR Sections12.212 and 227.7202, as applicable. Consistent with 48
CFR Sections 12.212 or 227.7202-1 through227.7202-4, as applicable, the Commercial Computer Courseware and
Commercial Computer Courseware Documentation are being licensed to U.S. Government end users (a) only as
Commercial Items and (b) with only those rights as are granted to all other end users pursuant to the terms and
conditions herein. Unpublished rights reserved under the copyright laws of the United States.

GENERAL PROVISIONS
This statement may only be modified in writing signed by you and an authorized officer of Embarcadero. If any
provision of this statement is found void or unenforceable, the remainder will remain valid and enforceable
according to its terms. If any remedy provided is determined to have failed for its essential purpose, all limitations of
liability and exclusions of damages set forth in the Limited Warranty shall remain in effect. This statement shall be
construed, interpreted and governed by the laws of the State of California, U.S.A. This statement gives you specific
legal rights; you may have others which vary from state to state and from country to country. Embarcadero reserves
all rights not specifically granted in this statement.

IMPORTANT
These lab exercises are for educational purposes only. They have not been tested in a production environment
and are not intended for commercial use. Embarcadero does not provide technical support for these examples.
Table of Contents
Introduction ................................................................................................................................................................. 9
Course Introduction ................................................................................................................................................. 10
Meeting Organizer Requirements ............................................................................................................................ 11
What is New in Delphi 2009 ................................................................................................................................... 12
Prototyping ................................................................................................................................................................ 17
Prototyping the Application ..................................................................................................................................... 18
The Main Form ........................................................................................................................................................ 20
Meeting Form .......................................................................................................................................................... 21
Meeting Time and Room ......................................................................................................................................... 22
Meeting Room Info ................................................................................................................................................. 23
User Info .................................................................................................................................................................. 24
Scheduled Meetings ................................................................................................................................................. 25
Login Form .............................................................................................................................................................. 26
Course Roadmap ....................................................................................................................................................... 27
Configuring the Project............................................................................................................................................. 29
Creating a Project .................................................................................................................................................... 30
Project Manager....................................................................................................................................................... 32
Project File............................................................................................................................................................... 32
Project Options ........................................................................................................................................................ 33
Compiling and Linking ............................................................................................................................................ 46
Build Configurations ............................................................................................................................................... 47
Object-Oriented-Programming ................................................................................................................................ 51
Object-Oriented Programming ................................................................................................................................ 52
Classes vs. Objects .................................................................................................................................................. 54
Example ................................................................................................................................................................... 56
Class Scoping .......................................................................................................................................................... 65
Polymorphism.......................................................................................................................................................... 67
Properties ................................................................................................................................................................. 70
Typecasting Objects ................................................................................................................................................ 73
Overloading Methods .............................................................................................................................................. 75
Abstract Methods..................................................................................................................................................... 77
Class Methods ......................................................................................................................................................... 78
Nested Type Declarations ........................................................................................................................................ 79
Class Helpers ........................................................................................................................................................... 81
Sealed Classes ......................................................................................................................................................... 83
Advanced Concepts ................................................................................................................................................. 84

Table of Contents. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
Business Code............................................................................................................................................................. 89
Real World Problem ................................................................................................................................................ 90
Business Rules vs. Business Logic .......................................................................................................................... 91
What is Interface Code?........................................................................................................................................... 92
Example ................................................................................................................................................................... 93
The UML Class Diagram ......................................................................................................................................... 99
Introduction to the UML (Unified Modeling Language) ....................................................................................... 100
Together................................................................................................................................................................. 109
Example ................................................................................................................................................................. 116
Delphi Class Explorer ............................................................................................................................................ 126
The Singleton Design Pattern ................................................................................................................................. 131
What is the Singleton Pattern ................................................................................................................................ 132
When should be used ............................................................................................................................................. 132
Using the Singleton Pattern ................................................................................................................................... 132
Basic Interface Elements ......................................................................................................................................... 145
VCL Architecture .................................................................................................................................................. 146
Developing the Application User Interface ........................................................................................................... 150
Basic Skills Example ............................................................................................................................................. 177
Visual Form Designer and Code Editor ................................................................................................................ 197
Introduction ........................................................................................................................................................... 198
Visual Form Designer ............................................................................................................................................ 199
Creating Interfaces ................................................................................................................................................. 200
Code Editor ............................................................................................................................................................ 202
Two-Way Tool ...................................................................................................................................................... 203
An Object's Lifetime................................................................................................................................................ 205
Overview ............................................................................................................................................................... 206
Component’s Owner .............................................................................................................................................. 207
Terminating Components ...................................................................................................................................... 208
Exception Handling ................................................................................................................................................. 209
Introduction ........................................................................................................................................................... 210
Handling Exceptions.............................................................................................................................................. 213
Resource Protection ............................................................................................................................................... 215
Raising and Re-Raising Exceptions ....................................................................................................................... 217
Delphi’s Debugger ................................................................................................................................................... 219
Introduction ........................................................................................................................................................... 220
Controlling Program Execution ............................................................................................................................. 223
Analyzing Data at Runtime ................................................................................................................................... 230
Controlling the Debug Flow .................................................................................................................................. 231

Table of Contents. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
Advanced Database Development .......................................................................................................................... 233
Delphi Database Architecture ................................................................................................................................ 235
Database Access Technologies .............................................................................................................................. 241
Using DBExpress to Access Data............................................................................................................................ 245
DBExpress ............................................................................................................................................................. 246
Connecting to a Database Server ........................................................................................................................... 248
TClientDataSet......................................................................................................................................................... 253
TClientDataSets ..................................................................................................................................................... 254
DBExpress Example .............................................................................................................................................. 270
Deploying Application Files .................................................................................................................................... 287
Deploying Database Applications ........................................................................................................................... 289
Using Packages......................................................................................................................................................... 293
What is a Package? ................................................................................................................................................ 294
Why Use Packages?............................................................................................................................................... 295
Model-View-Controller Pattern ............................................................................................................................. 299
MVC Components ................................................................................................................................................. 301
Relationships between Components ...................................................................................................................... 302
Benefits of MVC ................................................................................................................................................... 302
Drawbacks to MVC ............................................................................................................................................... 303
Introduction to TeeChart ........................................................................................................................................ 305
TeeChart Feature Matrix........................................................................................................................................ 306
TeeChart Components ........................................................................................................................................... 309
The TeeChart Editor .............................................................................................................................................. 311
Introduction to XML ............................................................................................................................................... 315
XML Overview...................................................................................................................................................... 316
Basic XML ............................................................................................................................................................ 320
XSL ....................................................................................................................................................................... 325
XML Parsers .......................................................................................................................................................... 335
XML and VCL Components ................................................................................................................................. 337
Introduction to VCL for the WEB ......................................................................................................................... 355
The History of Web Development with Delphi ..................................................................................................... 356
What IntraWeb brings to Delphi............................................................................................................................ 357
IntraWeb Components ........................................................................................................................................... 358
Application Mode or Page Mode? ......................................................................................................................... 366
IntraWeb Server Controller ................................................................................................................................... 367
Database development on the Web ........................................................................................................................ 372
Frame usage in Web Applications ......................................................................................................................... 374
AJAX programming in Web Applications ............................................................................................................ 376

Table of Contents. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
Introduction to Rave Reports ................................................................................................................................. 381
Rave Reports Components .................................................................................................................................... 382
The Rave Visual Designer ..................................................................................................................................... 384
Changing Report Fields Manually ......................................................................................................................... 391
Viewing the Results ............................................................................................................................................... 393
Generating the Report under Delphi ...................................................................................................................... 394
Beyond Rave Designer .......................................................................................................................................... 395
Introduction to DataSnap ....................................................................................................................................... 397
Key Concepts......................................................................................................................................................... 398
DataSnap Client Components on the Component Palette ...................................................................................... 399
DataSnap Server Components on the Component Palette ..................................................................................... 400
DataSnap in the Object Repository ........................................................................................................................ 401
Creating a Three-Tier Application ......................................................................................................................... 401
Building the Meeting Organizer ............................................................................................................................. 405
Requirements ......................................................................................................................................................... 406
Configuring the Project ......................................................................................................................................... 407
Creating the Main Form......................................................................................................................................... 409
Visual Form Inheritance ........................................................................................................................................ 416
Effective Project Development .............................................................................................................................. 423

Table of Contents. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
I ntroduction

Delphi 2009 Application Development for Win32


-9-
Introduction
The process of developing software is composed of very distinct yet interconnected phases.
Learn what is involved in a specific task, knowing what has to be done (learning the appropriate approaches, when
needed), and verify and deliver the outcome of the efforts while managing the underlying processes: these are steps
most developers have learnt informally.
In this module, the new features of Delphi 2009 will be introduced, as well as Meeting Organizer Requirements .

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 10 -
Meeting Organizer Requirements
Requirements are specifications that the application being developed must provide to be successful. Requirements
are found in many sources - business rules, business process models, product marketing, prototypes, development
meetings and more.

System Needs
Meeting Organizer shall allow employees to manage their meetings, i.e., to schedule new meetings, cancel meetings,
update meetings and retrieve meeting schedules. The system will also allow users to receive asynchronous
notifications about their meeting calendar updates and meeting reminders.
For this release of the system, meetings are defined as a face-to-face meeting that requires a physical location (a
meeting room). In future releases, the definition of the meeting may be expanded to include conference calls or
virtual meetings using Internet or other communication channels.
System administrators fall under a special category of users. They manage a database of users and a database of
meeting rooms.
The system provides a web-based interface that supports operations as managing meetings (scheduling a new
meeting, canceling and updating currently scheduled meetings), managing users (adding a new user, removing a
user, updating user data), and managing meeting rooms (adding a new meeting room, removing a meeting room
from the system, updating meeting room data).
The system must support privacy and ensure only authorized users can access it.
The system must support audits of the user authorization process (login operations).

System Requirements
Based on the scenario described above, all requirements were mapped as use cases in CaliberRM. Use cases are
used when designing the application with modeling tools.
In this way, requirements are mapped out of usage flows where actors (users) use the system. Such usage flows
include alternative flows or ‘unexpected’ conditions.
In the Meeting Organizer application, there are the following main use cases:
• Log In
• Manage Meeting Rooms
• Manage Meetings
• Manage Users
• Purge Meetings
• Send Meeting Reminders
Each of these use cases describes the flow the system must follow. They will be used to develop the application.

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 11 -
What is New in Delphi 2009

Separating Data and Business Logic using new DataSnap.


DataSnap is a complete set of tools that allows developers to manage all the data included in an application, building
powerful applications where the data can be safely and efficiently manipulated. With the new enhancements is
possible to write Server Methods, which can be called as if they were client code, improving the performance and
the reliability of applications.

New VCL Components


Delphi 2009 provides a rich set of features involving the new VCL components, and enhancing older ones, to help
developers to build powerful user interfaces. With support for Windows Vista Style and Microsoft Office 2007 User
interface, Delphi applications can use all the latest features included on Windows.

Unicode
The entire Delphi environment uses Unicode system, and every application can run on any windows platform,
regardless the region. Unicode is a character encoding scheme that allows virtually all alphabets to be encoded into a
single character set. Unicode allows computers to manage and represent text most of the world’s writing systems.

IDE Enhancements
A class Explorer was added to the IDE, which allows all the classes to be grouped and organized by hierarchy and
structure. A Resource Manager that manages the addition of Windows resources to an application and a Build
Configuration that provides an easy selection of build configurations. The project option was greatly improved,
where now there are two columns, one column with the options and the other with the respective values. This new
feature allows a faster scan on the page to find to specific option. The project manager view has some changes,
providing three file views: Directory(Nested) Current method, each directory is a separate node, Directory(Flat)
where each file reside under a directory node containing the full path and List, where directory nodes are not visible.
The import Components and New VCL Components can include type libraries, ActiveX controls and assemblies.

COM and ActiveX


With a new enhanced support of COM and ActiveX technologies, developers can create various types of COM and
Active X objects, like Active Forms.

Ajax and Visual Components for Web


Delphi 2009 contains a new set of VCL components to help developers to create powerful web applications, using
features such drag and drop components and AJAX integrated with Delphi development.

Debugger
A feature named Wait Chain Traversal feature has been added to help thread conventions or deadlock problems.
Using a feature included on Windows Vista the Wait Chain can detect which object is being waited by the blocked
thread and then this object will be replaced by its subsequent object.

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 12 -
Database
DataSnap, which provides a middle tier application server, was enhanced to provide connections between
components in different tiers, like the new class TDSServer that manages the creation and and lifetime of server
methods class. For example, once the dataSnap connection is established, methods can be called just like stored
procedures. Also The Unicode support was added to Oracle, Interbase and MySql drivers.

Anonymous Method
An anonymous method is a procedure or function without a name associated with it. It treats a block of code as an
entity that can be assigned to a variable or used as parameter to a method.
In addition, an anonymous method can refer to variables and bind values to the variables in the context in which the
method is defined.
See the declaration:

type
TNameProcedureProcedure = reference to procedure (n: string);
var
x1 :TNameProcedureProcedure;
A very important feature of anonymous methods is that they may reference variables that are visible to them where
they were defined. These variables can be bound to values and wrapped up with a reference to the anonymous
method.
If there is the need to execute part of one code more than once, define it as anonymous and reference it in the rest of
the code. Using anonymous methods makes the code simpler and shorter, besides avoiding the overhead and effort
of creating a class that may never be used anywhere else. The resulting code is easier to understand.
The combination of assignability and variable capture is quite powerful, because it effectively means that the
signature of the procedure reference type can be changed in the definition of the anonymous method. In other
words, the developer can pass values that are not included in the arguments declared in the procedure reference
declaration.

Generics
Generics allow the developer to write more general code, while keeping type safety. Generics are also called
Parameterized types or Generic types. A generic method can refer to any global function, method, ow anonymous
method that takes no parameters and returns a value.
Here is a generic method reference:

type
TFunc<T> = reference to function: T;

Notice, that the type is not important yet in the declaration. See the code below when are the type declaration:

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 13 -
procedure DoIt;
var
evenNumbers: TFunc<Integer>;
decades: TFunc<Integer>;
begin
evenNumbers := MakeCounter(0, 2);
decades := MakeCounter(0, 10);

WriteList(evenNumbers, 10);
WriteList(decades, 10);
end;

Figure 1: Generics Console application running

TStringBuilder
The Tiburon Runtime Library includes a class called TStringBuilder. It is a class designed to “construct” strings.
TStringBuilder contains any number of overloaded functions for replacing, adding, and inserting content into a
given string.
The string builder class facilitates the creation of single strings out of a variety of different data types. All Append,
Insert, and Replace functions return an instance of TStringBuilder, making it possible to chain them together to
create a single string.
See the code below:

procedure TForm2.FormDblClick(Sender: TObject);


var
MyStringBuilder: TStringBuilder;
Price: double;
begin
MyStringBuilder := TStringBuilder.Create('');
try
Price := 3.24;
Label1.Caption := MyStringBuilder.Append('The apples are $').Append(Price).
Append(' a pound.').ToString;
finally
MyStringBuilder.Free;
end;
end;
Notice that it is easy to declare a StringBuilder. Now run the application.

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 14 -
Figure 2: before double-click.

Figure 3: after double-click.

TStringWriter
Implements a TextWriter for writing information to a string.

TStringWriter = class(TTextWriter);

TTextWriter
Represents a writer that can write a sequential series of characters. This class is abstract.

TTextWriter = class;

TStringReader
Implements a TextReader that reads from a string.

TStringReader = class(TTextReader);

TTextReader
Represents a reader that can read a sequential series of characters.

TTextReader = class;

Introduction. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 15 -
Delphi 2009 Application Development for Win32
- 16 -
P rototyping
This module’s main objective is to introduce the advantages of prototyping by demonstrating the prototyping of the
Meeting Organizer application’s user interface.
This module covers:
• What is Prototyping
• Advantages of Prototyping
• Demonstration of Prototyping a User Interface.

Delphi 2009 Application Development for Win32


- 17 -
Prototyping the Application
After getting some requirements, the developer can start designing and prototyping the application to demonstrate it
to the customer and identify any further requirements based upon direct customer feedback. Further possible
enhancements and amendments to the application can be discussed, reaching the set of features for the first cut.
• Prototyping is the process of quickly putting together a working model (a prototype) in order to test various
aspects of the design, illustrate ideas or features, and gather early user feedback. Prototyping is often treated
as an integral part of the development process, where it is believed to reduce project risk and cost.
Frequently, one or more prototypes are made in a process of incremental development, where each
prototype is influenced by the performance of previous designs. This way, problems or deficiencies in
design can be corrected. When the prototype is sufficiently refined and meets the functionality, robustness,
manufacturability and other design goals, the product is ready for production.

Advantages
Prototyping reduces development time
• Through prototyping, the requirements can be refined to a deeper extent, leading to fewer changes during
development
Prototyping reduces development costs
• Saving time results in money savings. Less rework means less money spent.
Prototyping requires user involvement
• One of the fundamental benefits of prototyping is that it causes users to get much more involved in the
development of the system than they usually are. For most system developments, the user is the one for
which the system is being built. The user knows the problem domain better than anyone else on the
development team.
With prototyping, developers receive quantifiable user feedback
• Since the users are 'in the loop', their feedback is more direct and timely.
Prototyping facilitates system implementation and results in higher user satisfaction
• Implementing software without high user involvement is often a problem. Users are most often not satisfied
with the way the system looks, feels and performs. This problem arises because the new system has not been
seen by the users until it is finished. This initial friction with the users isn't a problem when prototyping is
used, because users participate in the development and then become familiar with the system before it is
fully implemented.
Prototyping exposes developers to future system enhancements
• Since developing software systems often takes time, the design team is constantly required to respond to
changes in the company, in the problem domain, and also in the requirements of the system. There is usually
a 'freeze-date', where no new requirements are added to the system. Because the development team is more
deeply involved with the users, they are able to foresee potential new requirements and log them for use for
the next revision of the system.

Disadvantages
• With prototyping, users often expect the performance of the ultimate system to be the same as the
prototypes, especially if this is not made explicit to them as part of the prototyping process.
• Prototypes are frequently optimized to highlight a specific feature of the proposed system. It is important to
keep the users aware that the prototype is not the finished system and that its primary focus is to define
requirements.
• Developers can become too attached to their prototypes. When developers work too hard on prototypes, it
can be a problem to get them to throw them away. This problem also applies to the features that a developer

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 18 -
may implement. They may work well and be elegant solutions, but they may not have any bearing on the
actual user, or on the business requirements of the system.
• When sophisticated software prototypes are employed, the time-saving benefit of prototyping can be lost
due to the re-development effort required to transform them into the actual system.
The key to prototyping is the fact that it is supposed to be done quickly. If developers lose sight of this fact, they
may try to develop a prototype that is very complex and robust, taking a lot of time. This time would be better spent
on coding the actual product.

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 19 -
The Main Form
This is the main form. Through it, users will have access to their meetings.
Notice that although the form seems to be complete, there is room for additional changes. Remember that it is used
for the users to have a grasp of the system. Its only functionality is to show other forms.

Figure 1 – Meeting Organizer’s Main Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 20 -
Meeting Form
With this form, the user can schedule new meetings or view meeting info.
Notice that participants and the meeting room can be added in this form. Moreover, after entering the Meeting Time,
the user can search for a free meeting room for that specific date and time.

Figure 2 – Meeting Organizer’s Meeting Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 21 -
Meeting Time and Room
In this form, the user can search for a free room for the specified date and time.

Figure 3 – Meeting Organizer’s Meetings Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 22 -
Meeting Room Info
Administrators are able to manage the meeting rooms. With this form, administrators can add, modify, or delete
meeting rooms when needed.

Figure 4 – Meeting Organizer’s Room Info Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 23 -
User Info
Just as with the Meeting Rooms, administrators can also manage the users.

Figure 5 – Meeting Organizer’s User Info Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 24 -
Scheduled Meetings
Through this form, the user can view all his/her scheduled meetings in a convenient way.

Figure 6 – Meeting Organizer’s Meeting Scheduler Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 25 -
Login Form
One more requirement: all users must login to the system before they start using it.
Of course, users are allowed to change their password after logged in.

Figure 7 – Meeting Organizer’s Login Form

Prototyping. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 26 -
Course R oadM ap
This module highlights key milestones in this course for learning how to develop and deploy the sample application.

Delphi 2009 Application Development for Win32


- 27 -
Introduction
So far, the application requirements were gathered and a prototype was created to show the customer how the
application was mapped to all these requirements.
In the following modules, the developer will continue to apply the development phase tasks to complete the
application.

Basic Skills
In this module, the developer will learn some fundamental subjects such as OOP, UML, Design Patterns, graphical
interface elements and project configuration. Each of these topics alone could justify a complete training, so only
their essential aspects will be addressed.

Creating the Interface


When implementing the application’s functionality in Delphi, the developer deals primarily with components and
code.
In this module, the developer will learn how to use some essential Visual Form Designer and the Code Editor
features, as well as some key interface components. Object lifetime, exception handling, and a few debugger
features will also be depicted.

Connecting to a Database
When an application needs to store information, it does so through the use of a database. In this module, the
developer will learn about database access technologies and their differences, as well as how to manipulate data.

Deploying
The application has been developed, and now it is time to deploy it. In this module, the developer will see which
files must be installed to make the application work. The developer will also see the usage of runtime packages, a
feature that facilitates deployment and maintenance.

Incrementing the Application


Design Patterns has been seen before. Now, another common pattern that can be used in the development of
applications – MVC (Model-View-Control) – will be explained.
Using this pattern increases the reuse of objects and the flexibility of the design and implementation.

Course RoadMap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 28 -
Configuring the P roject
This module’s main objective is to describe Delphi Win32 Application options.
This module covers:
• Creating a project
• Project Manager
• Project Options.
• Compiling and Linking a project
• Build Configurations

Delphi 2009 Application Development for Win32


- 29 -
Creating a Project
Application development is all about projects. When the developer creates an application in Delphi, he/she is
actually creating a project. A project is a collection of files that make up an application. Some of these files are
created at design time. Others are generated automatically when compiling the project source code.
Regardless of how simple it may look, by doing so, the developer will have the chance to get in touch with the
whole content of the module. The new items that are introduced will be detailed in a timely manner as the
development goes further. To create a project, follow the steps below:
File | New | VCL Forms Application – Delphi

Figure 1 - Creating a Project


In order to view the content of a project, use a project management tool - Project Manager (Figure 4). The Project
Manager displays a hierarchical view of the unit names, the forms contained in the unit (when there is any). It also
shows the paths to the files in the project. Although many of these files can be edited directly, it is recommended to
use the visual tools provided for that.
Before continuing, save the project. To do so, choose File | Save all from the menu.
When a project is saved for the first time, Delphi requests the developer to supply a unit name, which is
automatically saved with the project. After saving the unit, Delphi requests the developer to assign a name to the
project. Name the unit as “UMain” and the project as “AppDevWin32”. Delphi also creates two project file types:
.dpr and .dproj.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 30 -
Figure 2 - Saving a Unit

Figure 3 - Saving a project

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 31 -
Project Manager

Figure 4 - Project Manager


At the top of the project hierarchy, there is a group file. Multiple projects can be combined into a project group. This
allows opening more than one project at a time in the Project Manager. Project groups let the developer organize and
work on projects that are related - such as applications that work together, or the different parts of a multi-tiered
application. If working on a single project, there is no need of a project group file to create an application.
Project files contain directions for building an application or shared object. When the developer adds and removes
files using the Project Manager, the project file is updated. Specify the project options using the Project Options
dialog. It has tabs for various aspects of the project, such as forms, application, and compiler. These project options
are stored in the project file.
Units and forms are the basic building blocks of an application. A project can share any existing forms and unit files,
including those that reside outside the project directory tree. This includes custom procedures and functions that
have been written as standalone routines.

Project File
The general architecture of Delphi programs recognizes project files as main program files. If the developer is
familiar with Pascal, Delphi’s DPR file corresponds to Pascal’s program file. However, unlike traditional Pascal
programs, the DPR file rarely needs human intervention. It acts as a wrapper for all of the forms that constitute the
project. In essence, it creates a reference to the forms in the project, and then instructs the main form to open itself.
The DPR file content can be viewed from the Project | View Source menu item. It presents the following structure:
• program: the program identification.
• uses: lists all files (units) bound to the project.
• begin..end: this section is responsible for the application’s initialization. This defines which forms are
created at the time the application is executed, but this is usually specified through the project options
dialogs provided rather than direct editing of this file.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 32 -
Figure 5 - Project (DPR) File

Project Options
In the IDE project options dialog, the developer is able to manipulate not only the implementation model of the
applications, but also the design-time behavior of the project and the runtime behavior of the application.
To specify the project options, choose Project | Options.
Keep reading for a further look into the available options.

Delphi Compiler
Use this page to set the Delphi compiler options for the current project.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 33 -
Figure 6 – Project Options : Delphi Compiler
• Conditional Defines: this setting allows listing symbols referenced in conditional compiler directives.
• DCP output directory: Specifies where the .dcp (Win32) or .dcpil (.NET) file is placed at compilation
time. If left blank, the global DCP output directory is used instead.
• Default Namespace: indicates the default namespace used for all units in the project.
• Namespace Prefixes: specifies the prefixes for namespaces, allowing the creation of a shorthand version of
the namespace in the uses clause that is in the code file.
• Output directory: Specifies where the compiler should put the executable file.
• Package output directory: Specifies where the compiler puts generated package files (.BPL).
• Search Path: this setting allows including into the project those units that do not reside in the project’s
home directory. The developer must include the entire search path. Separate multiple directory path names
with semicolons.
• Unit Aliases: this setting is mainly used for backwards compatibility. In this setting, the developer can
specify alias names for units that may have either had their names changed or were merged into a single
unit.
• Unit Output Directory: specifies a separate directory to contain the .dcu (Win32) or .dcuil (.NET) files.

Compiling
This dialog window is used to set the compiler options for the current project.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 34 -
Figure 7 - Project Options: Compiling Window

Code Generation
• Code inlining control: responsible for setting the Delphi compiler directive or clearing it up. Default = off.
• Code page: decimal number that stands for a specific character encoding table, with different standard
values for different languages.
• Emit runtime type information: Controls generation of runtime type information. Corresponds to {$M}.
Default = False (not emit runtime type information).
• Minimum enum size: Specifies the smallest amount of memory to be allocated for an enum. Choices are
Byte, Word, Double Word, Quad Word. Default = Byte
• Optimization: Enables compiler optimizations. Corresponds to {$O}
• Pentium-safe FDIV: Delphi for Win32 only. Generates Delphi code to detect a faulty floating-point
division instruction. Corresponds to {$U}.
• Record field alignment: Controls alignment of fields in Delphi record types and class structures. The
possible values are: Off (equivalent to {$A1}) fields are never aligned, Byte (equivalent to {$A2}) fields in
class structures are aligned on byte boundaries, Word (equivalent to {$A2}) fields in class structures are
aligned on word boundaries, Double Word (equivalent to {$A4}) fields in class structures are aligned on
double-word boundaries, Quad Word (equivalent to {$A8} or {$A+})fields in class structures are aligned on
quad word boundaries. Default = Quad Word (8).
• Stack frames: Forces the compiler to generate stack frames on all procedures and functions. Corresponds to
{$W}.
• String format checking: compiler option to control the auto-generation of inline code string format checks.
This is a very dangerous option and can cause unpredictable behavior in applications that may deliver
"incorrect" string payloads.

Debugging
• Assertions: Generates code for assertions. Corresponds to {$C}. Unlike exceptions, assertions can be
removed for the final build. After disabling the option, rebuild the code base to eliminate assertions.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 35 -
• Debug information: Consists of a line-number table for each procedure, which maps object-code addresses
into source text line numbers. Corresponds to {$D}.
• Local symbols: Enables or disables the Generation of local symbol information. Corresponds to {$L}.
• Symbols reference info: Generates symbol reference information used by the Code Editor, and Project
Manager. Corresponds to {$Y}. If Reference Info and Definitions Only are both checked ({$YD}), the
compiler records information about where identifiers are defined. If Reference Info is checked but
Definitions Only is unchecked ({$Y+}), the compiler records information about where each identifier is
defined and where it is used. These options have no effect unless Debug Information and Local Symbols
(see above) are selected.
• Use Debug DCUs: The Debug DCUs contain debug information and are built with stack frames. When this
option is checked, the compiler prepends the Debug DCU path to the unit Search path specified in Project |
Options on the Directories/Conditionals page.
• Use imported data references: Enables packaged units to reference variables in other packages.
Corresponds to {$G}

Other Options
• Additional switches to pass to the compiler: Enter the desired compiler switches to add them to the
settings controlled. Use a hyphen as the switch symbol, and separate switches with commas.
• Allow unsafe code: .NET Framework only .Enables the running of code identified as unsafe, thereby
overriding the security system. Unsafe code is defined as potentially dangerous unmanaged code entry
points with security suppressed. Default = false.
• Generate XML documentation: Generates a file containing the XML representation in the project
directory. Corresponds to the --doc compiler switch. Default = False
• Look for 8.3 filenames also: Passes the —P compiler switch for DCC32.exe. Default = False
• Output unit dependency information: Passes the —depends compiler switch for DCC32.exe. Default =
False

Runtime errors
• I/O checking: Checks for I/O errors after every I/O call. Corresponds to {$I}. Default = True.
• Overflow checking: Checks for overflows in integer operations. Corresponds to {$Q}. Default = False.
• Range checking: Checks that array and string subscripts are within bounds. Corresponds to {$R}. Default =
False.

Syntax Options
• Assignable typed constants: Controls whether typed constants can be modified or not. When enabled, the
compiler allows assignments to typed constants. Corresponds to {$J}.Default = False.
• Complete Boolean evaluation: Switches between the two different models of Delphi code generation for
the AND and OR Boolean operators. Corresponds to {$B}. Default = False.
• Extended syntax: Provided for backward compatibility. Do not use this option when writing Delphi
applications. Corresponds to {$X}. Default = True.
• Long strings by default: Delphi for Win32 only. The string keyword corresponds to the AnsiString type
when this option is enabled. Otherwise, the string keyword corresponds to the ShortString type. Corresponds
to {$H}. Default = True
• Open parameters: This option controls the meaning of variable parameters declared using the string
keyword in the huge strings disabled state. Corresponds to {$P}. Default = True.
• Strict var-strings: This option controls type on short strings passed as variable parameters. Corresponds to
{$V}. Default = True.
• Typed @ operator: Controls the types of pointer values generated by the @ operator and the compatibility
of pointer types. Corresponds to {$T}.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 36 -
Hints and Warnings
This is where the developer controls whether compiler hints and warnings are enabled. Define which warnings will
be displayed.

Figure 8 - Project Options: Hints and Warnings Page


The settings of this dialog window are evaluated during the compilation process.
• Output hints: Enables hints during compile time. The hints that are enabled are listed as True under the
Output warnings option. Default = True.
• Output warnings: Enables warnings during compile time and allows selecting the desired specific warnings
that will be displayed during compile time. Default = True

Linking
A place for managing the way the program files are linked, controlling several compilation and memory options.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 37 -
Figure 9 - Project Options: Linking Window
• Debug Information: Generates a line-number table for each procedure that maps object-code addresses into
source text line numbers. Corresponds to {$D+}. Default = False.
• Exe Description: This field can contain a string with up to 255 characters. The string is linked to $D and
included in the executable file. This option is only applicable to DLLs and application executables but not
for packages.
• Generate Console Application: Causes the linker to set a flag in the application’s .exe file, indicating a
console mode application. Default = False.
• Image Base: Specifies the preferred load address of the compiled image. This value is typically only
changed when compiling DLLs. Default = 400000.
• Include Remote Debug Info: Check it only if remote debugging is being used. Default = False.
• Linker Output: It specifies the desired output for the linker. It creates the standard Delphi .dcu format files.
Default = .dcu files.
• Map File: Select the type of map file produced, if any. The map file is placed in the Output Directory that is
specified on the Directories/Conditionals window. It has a .map extension (Default = Off).
• Maximum Stack Size: Applicable only to executable projects. Disable for DLLs. Indicates the total
reserved size of the stack.. Default = 1048576.
• Minimum Stack Size: Applicable only to executable projects. Disable for DLLs. Indicates the initial
committed size of the stack. Default = 16384
• Set Base Address for relocatable images: Controls the default load address for an application, DLL, or
package. Corresponds to {$IMAGEBASE}. Default = 0

Resource Compiler
Use this dialog box to set Resource Compiler options.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 38 -
Figure 10 - Project Options: Resource Compiler Window
• Additional Options: Enter additional options for the resource compiler (BRCC32.EXE).
• Code Page: Specify the code page to be used for resource translation. The default ANSI code page is used,
if no code page is specified.
• Default Language: Specify the default language. For example, -l409 represents English.
• Ignore INCLUDE: Ignore INCLUDE environment variable. Default = False.
• Multi-byte Character Support: Multi-byte character support. Default = False
• Resource Type: 16 bit (-16) builds a 16-bit resource. 32 bit (-32)* builds a 32-bit resource. Default = 32 bit.
• Response File: Is a standard ASCII text file (typically with .RSP extension) that contains one or more
command-line options and/or file names, with entries separated by a space or a new line.
• Verbose Message: The linker emits detailed information messages. Default = False

Directories and Conditionals


Use this dialog to set the paths for directory and conditional definitions.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 39 -
Figure 11 - Project Options: Directories and Conditionals Window
• Defines a Preprocessor Symbol: Defines a list of preprocessor symbols.
• Include File Search Path: Specifies the drive and/or directories that contain program include files.
• Output Directory For .res Files: Specifies the directory for .res output.

Forms
By selecting the Forms tab in the Project Options box (Figure 11), the developer is able to determine the way the
application is expected to handle its forms.

Figure 12- Project Options: Forms Window

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 40 -
Here, the developer can specify the application main form (this is the form that first appears when the application is
executed). The right-side list box displays all available forms for the project. Moving them to the left-side list box,
the developer specifies that these forms should be auto-created.
Forms in the auto-create list are automatically created by the project at the time the application is loaded. If a
particular form will be used throughout an application’s run, this is the best option. The other option tells Delphi not
to create forms, meaning that they must be created by writing source code. This is the best option for forms that are
not used every time the application runs. The more forms there are in the auto-create list, the slower the applications
will start. The loading process will also increasingly demand more memory. If a form that does not imply extra load
time and memory use during startup is desired, explicitly create it in the application. For example, there might be a
setup dialog box that is run very infrequently. It should be a non-auto-create form because it does not have to be
instantiated every time the application runs — only when the user needs to change setup information.

Application
By selecting Application in the project options dialog, some of the application settings can be defined:

Figure 13 - Project Options: Application Window

Application Settings
Using the Title text box, assign the application a name other than the default one in the project file. The Application
dialog also allows specifying which help file should be associated with the application, as well as an icon for the
application. The icon file is stored in the resource file (.RES) until the application is compiled. If by any chance this
resource file is deleted, Delphi changes the application’s icon back to the default icon. Nevertheless, the icon is
bound to the executable at the time the application is compiled. Such activity is in accordance with Delphi’s ability
to create true Windows standalone executables.

Output Settings
In the Output Settings section of this window, the developer will see the Target File extension text box. This is
where the file extension of the target executable file is set. For example, if the project is an ActiveX application, the
standard file extension should be specified as ‘.OCX.’. This would be typically left alone, unless an ActiveX
executable is being created rather than an application.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 41 -
Version Info
Use this dialog box to specify version information for a Delphi Win32 project. When version information is
included, a user can right-click the program icon and select their properties in order to display the version
information.

Figure 14 - Project Options: Version Info Window


This information can be accessed in Windows by right-clicking the EXE icon and then choosing properties.

Packages
Use this window to specify the design-time packages that are installed in the IDE, as well as the runtime packages
that are required by the project.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 42 -
Figure 15 - Project Options: Packages Window

Design Packages
List the design time packages available to the IDE and to all projects. Check the desired design packages to make
them available.

Runtime Packages
Runtime packages determine which runtime packages are used when the executable file is created. Separate package
names with semicolons.

Debugger
Use this dialog to pass command-line parameters to the application, specify a host executable for testing a DLL, or
load an executable into the debugger. This dialog is also available from Run | Parameters.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 43 -
Figure 16 - Project Options: Debugger Window
• Host application: Feed it with the path to an executable file (Click on ‘Browse’ to bring up a file-selection
dialog). If the current project is a DLL or package, use this edit box to specify a host application that calls it.
The developer can also enter the name of any desired executable to run it in the debugger. If the project
open at the moment will be run, there is no need to enter anything in this option.
• Parameters: Feed it with the desired command-line arguments to pass them to the application (or the host
application) when it starts. Use the drop-down button to choose from a history of previously specified
parameters.
• Working Directory: Feed it with the name of the directory that should be used for the debugging process.
Leave it blank and the directory that holds the executable of the application will be used instead.
• Source Path: Specify the directories containing the source files. Separate directories using semicolons (;).
By default, the debugger searches paths defined by the compiler. If the directory structure has changed since
the last compile, the path entered here will include a file in the debugging session. Additional directories are
searched in the following order:
(1) Debug Source path (this option)
(2) Browsing path, specified on the ToolsOptionsEnvironment OptionsDelphi OptionsLibrary page
(3) Debug Source path, specified on the ToolsOptionsDebugger OptionsCodeGear Debuggers page

Symbol Tables
Use this dialog box to specify the location of the symbol tables to be used during debugging.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 44 -
Figure 17 - Symbol Tables Options Page
• Debug Symbols Search Path: Feed it with the directory containing the symbol tables used for debugging.
This is used if the Load All Symbols check box is checked.
• Load All Symbols: If this box is checked, the list is disabled and all symbol tables are loaded by the
debugger
• Load symbols for unspecified modules: Feed it whether symbol tables for modules are loaded during
debugging. If this box is checked, the symbol tables for modules not specified are loaded using the Debug
symbols search path.
• New: Displays the Add Symbol Table Search Path dialog, where the developer can specify a module name
and an associated search table path.
• Edit: Displays the selected module and path in the Add Symbol Table Search Path dialog, enabling the
developer to edit the module name list.
• Delete: Removes the selected module from the Module Name — Symbol Table Path list.

Environment Block
Use this page to indicate which environment variables are passed to your application while you are debugging it.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 45 -
Figure 18 – Environment Block Options Page
• System variables: Lists all environment variables and their values defined at a system level. It is not
possible to delete an existing system variable, but it can be overridden.
• Add Override: Displays the Override System Variable dialog box, allowing the modification of an existing
system variable to create a new user override.
• User override: Lists all defined user overrides and their values
• New: Displays the New User Variable dialog box, allowing the creation of new user override to a system
variable.
• Edit: Displays the Edit User Variable dialog box, which allows changing the user override currently
selected in the User overrides list.
• Delete: Removes the user override currently selected in the User overrides list.
• Include System Variables: Passes the system environment variables to the application being debugged.

Default
The Application dialog also has a default check box. It makes the current settings default for all future projects. The
default check box can be found at the left-bottom corner of every Project/Options window.

Compiling and Linking


To run and distribute the application, the developer must compile it. There is no Pascal interpreter running in the
background, because Delphi creates truly compiled executables.

Compiling
To compile the application, choose Project | Compile <Project Name> from the menu options. There is also the
option to choose Project | Syntax Check <Project Name> to check the source code syntax by compiling the

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 46 -
project, but not linking it. This option does, however, create a compiled output file (a DCU file, if a unit is being
compiled).
The compile command automatically compiles only the modules that need to be compiled. In other words, Delphi
only compiles the source files that have been changed, as well as any others that depend on files that were changed.
This limited compilation speeds the process considerably.
There may be some situations when compilation of all modules in a project is desired. In such cases, choose the
Project | Build <Project Name> menu option. This forces the compiler to recompile every file in the project.
If multiple projects are being developed in a project group, all projects can be compiled or built by selecting the
Compile All Projects or Build All Projects menu items found under the Project menu.
In addition, there is the option to select one or more projects using Ctrl+click in the Project Manager and Compile or
Build the selected projects through the right-click menu.

Linking
Linking in Delphi could not be easier, because it never has to be done! The linker is automatically invoked when
Delphi is told to compile and create an application. Unlike some other linkers, Delphi’s linker is so fast that most
often the linking process goes unnoticed. Combined with the blazing speed of the compiler, Delphi makes it possible
to compile and link programs at an unbelievable rate.
Check the Show compiler progress check box in the Tools | Options | Environment Options dialog to see the
status of the compiler and linker:

Figure 19 - Environment Options Page

Build Configurations
The developer can save project options as a named build configuration. Moreover, he/she can apply a build
configuration to as many projects as desired, as well as change build configurations during the project development.
For example, set project options that are specific for debugging the project, save them as a build configuration, and
then change to another configuration when debugging is finished.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 47 -
Figure 20 - Build Configuration
The options contained in a configuration are available on the Delphi Compiler, Compiling, Hints and Warnings,
Linking, Resource Compiler, and Directories and Conditionals pages of the ProjectOptions dialog box. By default,
the IDE includes a Debug configuration and a Release configuration to the build configuration. The developer can
modify the options in both default configurations, as well as create new build configurations on his/her own. To
apply a selected build configuration to specific projects or project groups, use the Configuration Manager located at
Project | Configuration Manager.

Figure 21- Configuration Manager

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 48 -
Build Events
Delphi also supports Pre and Post Build events. As their names imply, these events are fired either before or after the
build process occurs.

Figure 22 - Build Events


• Pre-Build: Window that will be performed before the rest of the build.
• Post- Build: Window that will be performed after the build has completed.

Using MSBuild to Build the Project


The Microsoft Build Engine (MSBuild) is the new build platform for Microsoft Windows. When explicitly building
a project, the IDE calls MSBuild. The build process is entirely transparent to Delphi developers. MSBuild is called
as part of the Compile, Build, and Run commands available on the Project and Run menus. Nevertheless, the
developer has the option to explicitly run MSBuild from the command line.

Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 49 -
Delphi 2009 Application Development for Win32
- 50 -
Object-Oriented P rogram m ing
In this module, the four main concepts of Object-Oriented Programming will be studied, using examples that
describe each one of them. Keep in mind this module’s main goal is to introduce the visual form inheritance
concept.
Main concepts:
• Abstraction
• Inheritance
• Encapsulation
• Polymorphism
Advanced Concepts
• Visual Form Inheritance
• Business Code and Interface Code

Delphi 2009 Application Development for Win32


- 51 -
Object-Oriented Programming
Object-oriented programming (OOP) has actually been around for a while. The first language considered to be
object-oriented was Simula-68, which was created in 1968. However, OOP became a mainstream technique in the
early 1980’s, largely because of the work of Grady Booch and others.
OOP borrows the ideas of modular programming and adds language support to it, in order to facilitate the crafting of
such modules. Using more traditional language models (as C or Pascal), the developer is forced to create data
structures (arrays, records, etc.), and then create separate routines (functions and procedures) that work on the data.
There is usually a clear distinction between the data and the code that act upon the data.
Although defining data and code are still very much a part of OOP, these elements are often stored inside Objects.
Objects allow the storage of data and code within the same data structure.
An object in Object Pascal is essentially a building block within which the developer can define procedures and
functions, as well as data types. Think of an object as a container not for data only (the fields) but also for related
processing activities (the subroutines).
Before moving onto the syntax of creating objects in Object Pascal, consider some other aspects of OOP. These
concepts must be understood in order to use OOP correctly.
First of all, it must be clear that object-oriented languages and object-based languages are not the same thing. For a
language to be considered an object-oriented language, it must support four basic features: abstraction,
encapsulation, inheritance, and polymorphism. The meaning of these terms will be soon explored. On the other
hand, an object-based language only needs to support a sub-set of these (i.e., the language might support abstraction
and encapsulation, but not inheritance and polymorphism). And it’s that simple: any language that does not support
all four criteria can’t be considered an object-oriented language.

Abstraction
Abstraction is the ability of a language to model the real world characteristics of the problem the program is trying
to solve. If a program that handles customers is being created, it is much easier to deal with a language through
which the developer can create something called a customer. Once created, begin to associate procedures and data
types to a customer building block (object) that is going to be easier to re-use. Abstraction refers to a language’s
ability to better support the creation of the basic building blocks for re-use.

Encapsulation
The goal of encapsulation is to create objects that can be re-used whenever appropriate. This re-use should occur
without having to be concerned about internal details, nor about having to manage undesirable side effects. After all,
it is undesired to update the process_balance procedure every time the customer object is re-used. This is why all
information that is necessary to perform this calculation is encapsulated at the moment the customer object is
created.
Of course, encapsulation does not occur automatically. If a language supports encapsulation, then it is up to the
programmer to use it correctly. In general, every written routine should “clean up after itself”. If a routine is written
to change the screen color and display a message, it should save the old screen at the beginning of the process, and
then restore the original color when it is terminated. Such ability keeps the routine from having undesirable side
effects. Moreover, the object is more reusable, as the strategy works properly for the project.
Cohesion and Coupling are two aspects of encapsulation that play a particularly important role in OOP.
Cohesion refers to the “single mindedness” of design. To support this objective, every member function should do
one thing and one thing only. This low-level granularity considerably aids the creation of reusable code. So, if there
is a routine that performs five distinct operations, it is obviously less reusable than creating five separate routines
that work together cohesively. It is easier to re-use small, cohesive routines.
Coupling is the other important aspect of encapsulation. The most obvious real world example of coupling involves
train cars; train cars couple together in a particular way to facilitate the process of pulling one car out to have it
replaced by another. Train cars couple together in a very well defined manner.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 52 -
In a similar manner, objects that are coupled to one another often need to refer to each other's internal parts. One
module may require that a global variable be declared in another module. This dependency often greatly diminishes
the usability (or re-usability) of either object; that is, because they depend on one another, one cannot be used
without the other.
It is best to keep a single coupling point. Coupling between objects should usually be limited to those parameters a
routine accepts and returns. By providing a single coupling point, it becomes easier to pull a module out and replace
it with another module that uses the same coupling (i.e., same parameters).

Inheritance
Inheritance refers to an object’s ability to borrow another object’s general characteristics for implementation.
Inheritance allows re-using common code. Therefore, languages that support inheritance cut down on the amount of
code that must be written.
Now, look at a real world example of inheritance. Assume a retail customer and a wholesale customer are created.
Building each object from scratch would mean that the developer would have to duplicate a lot of characteristics,
such as name, location, contact information, etc. It would be far more efficient to build a customer class to hold all
the characteristics both retail and wholesale customers have in common. After the base class is created, inherit from
the common ancestor class (e.g., "customer"), and add the specific characteristics needed for each new type.
What makes inheritance so important in a programming language is that the real world is often made up of objects
that fit into a hierarchy. Indeed, the requirement for customers, retailers, and wholesalers to be supported in the first
example is a good instance of one such hierarchy.
When logical classifications of objects come to mind, the developer then realizes that the real world is full of
relationships that manage and/or describe their behavior. Using an object-oriented programming language means
that the developer is free to more closely model programming interfaces and objects upon real-world classifications.
These classifications make components easier to design, re-use, and maintain.

Polymorphism
The last requirement for an object-oriented language is polymorphism. Polymorphism is the ability two related yet
different objects have to interpret the same command and respond to it differently. Send the command ‘to buy’ to
both the retail and wholesale customer, and each of them will know how to buy something. However, each one of
them would have a distinct approach to fulfilling this command—the retail customer will purchase inventory based
on retail sales patterns, whereas the wholesaler will purchase inventory based on the needs of a wholesale
environment.
Polymorphism is the most advanced of the OOP features. If there is interest in learning more about this concept,
there are plenty of excellent books that address this topic, as well as some simple examples given here.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 53 -
Classes vs. Objects
In an object-oriented language, a distinction between a class and an object has to be made. A class defines what an
object looks like, whereas an object is a ready-to-use representation of a class.
Think of the relationship between a class and an object as the relationship between a cherry pie recipe and the actual
cherry pie itself. A class is a recipe, while an object is an instance of a class that the program can interact with.
In programming terms, when defining a class, the developer is defining a type. To create an instance of that type,
create an object.
A class definition is always a type, and an object declaration is always an instance of that type. In other words, an
object is always a “live” variable, declared in a var section of a program or subroutine.

Defining New Classes


Although there are many classes in the object hierarchy, it is likely that there will be the need to create additional
classes if the developer is writing object-oriented programs. The classes written in Delphi will descend from either
TObject or one of its descendants.
The advantage of using classes comes from being able to create new classes as descendants of existing ones. Each
descendant class inherits the fields and methods of its parent and ancestor classes. There is also the option to declare
methods in the new class that override inherited ones, introducing new, more specialized behavior. The general
syntax of a descendant class is as follows:

Type
TClassName = Class (TParentClass)
public
{public fields}
{public methods}
protected
{protected fields}
{protected methods}
private
{private fields}
{private methods}
end;
If no parent class name is specified, the class inherits directly from TObject. TObject defines the basic methods for
all Delphi classes, including a basic constructor and destructor.

Constructors and Destructors


A constructor for a class is a special procedure that performs the work of creating an object. In the class definition,
one of the methods (or class procedures) begins with the constructor reserved word, rather than with procedure.
The constructor is called to initialize the values for an object. The destructor provides the opposite service:
destructors are responsible for deleting an object. A destructor frees all the memory that is no longer needed and
generally ‘cleans up’ when the object is no longer required.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 54 -
Figure 1 – Class Structure Sample
In addition to the fields, properties, and methods defined, TEmployee inherits all the methods from TObject. Place a
type declaration like this one in either the interface or implementation part of a unit, and then create instances of the
new class by calling the Create method that TEmployee inherits from TObject:

var
Employee: TEmployee;
begin
Employee := TEmployee.Create;
end;
The Create method is called a constructor. It allocates memory for a new instance object and returns a reference to
the object. Components on a form are created and destroyed automatically. However, if the developer writes his/her
own code to instantiate objects, he/she is responsible for disposing of them as well. Every object inherits a Destroy
method (called a destructor) from TObject.
To destroy an object, however, call the Free method (also inherited from TObject), because Free checks for a nil
reference before calling Destroy:

Employee.Free;

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 55 -
Example
After having created a project as explained in the previous module, add a new unit File | New | Unit – Delphi (Save
the unit as “UEmployee.pas”)
In this unit, let’s create a new class called “TEmployee” derived from TObject; as shown in the code below:
In this example, declare two classes: TRetailer and TWholesaler. The letter “T” comes from “Type”. It’s not really
necessary, but it’s a convention used by Delphi to name all classes beginning with the letter “T”.

unit RetailerUnit;

interface

type
TRetailer = class
Name: String;
Location: String;
Terms: String;
MultipleLocations: Boolean;
constructor Create;
destructor Destroy; reintroduce;
end;

implementation

constructor TRetailer.Create;
begin
Name := 'Foobar Antiques';
Location := 'New York, NY';
Term := 'COD';
MultipleLocations := True;
end;

destructor TRetailer.Destroy;
begin
end;

end.
Notice that the class was declared in its own unit. It’s always recommended to declare classes in their own separate
units as this provides more flexibility in re-using classes in many projects.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 56 -
unit WholesalerUnit;

interface

type
TWholesaler = class
Name: String;
Location: String;
Terms: String;
ShipByTruck: Boolean;
constructor Create;
destructor Destroy; reintroduce;
end;

implementation

constructor TWholesaler.Create;
begin
Name := 'The Antique Warehouse';
Location := 'Boise, ID';
Term := 'Net 30';
ShipByTruck := True;
end;

destructor TWholesaler.Destroy;
begin
end;

end.
There are some things to learn right now. Notice that the classes were declared in the unit’s interface section. It
enables them to be accessible from outside the unit.
As said before, a constructor is responsible for initializing the object and its values. And although the destructors are
empty, they serve an important function – deallocating all resources allocated by the constructor.
The last thing to notice is that when class methods are being implemented in the class unit, the developer can treat
the properties of a class as if they were regular variables. It means that there is no need to qualify them with the class
name.

Inheritance
One of the required characteristics of an object oriented language, inheritance, is the ability of a class to inherit
properties and functionality from another class and then add its own properties. This characteristic leads to code re-
use – there is no need to to rewrite inherited functionality; simply add to it.
Notice that both TRetailer and TWholesaler have common properties they share. Instead of re-creating those
properties for every class, create a TCustomer class with those properties and inherit the classes from TCustomer.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 57 -
unit CustomerUnit;

interface

type
TCustomer = class
Name: String;
Location: String;
Terms: String;
constructor Create;
end;

implementation

constructor TCustomer.Create;
begin
Name := 'The Customer';
Location := 'New York, NY';
Terms := 'Net 30';
end;

end.
Now, change the TRetailer and TWholesaler classes to use this feature:

unit RetailerUnit;

interface

uses
CustomerUnit;

type
TRetailer = class(TCustomer)
MultipleLocations: Boolean;
constructor Create;
end;

implementation

constructor TRetailer.Create;
begin
Name := 'Foobar Antiques';
Location := 'New York, NY';
Terms := 'COD';
MultipleLocations := True;
end;

end.
…and

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 58 -
unit WholesalerUnit;

interface

uses
CustomerUnit;

type
TWholesaler = class(TCustomer)
ShipByTruck: Boolean;
constructor Create;
end;

implementation

constructor TWholesaler.Create;
begin
Name := 'The Antique Warehouse';
Location := 'Boise, ID';
Terms := 'Net 30';
ShipByTruck := True;
end;

end.
What is the difference? In order to indicate that a class is inherited from another, put the super class name in
parenthesis.

TRetailer = class(TCustomer)
But does this mean that, by not explicitly declaring a super class to inherit from, the class is then created in a
vacuum? The answer is no. Actually, every class in Delphi inherits from the TObject class, whether or not TObject
appears in the class definition.
The TObject class itself defines a basic constructor and destructor. And once the destructor for TRetailer and
TWholesaler is empty, the developer can omit them and re-use the TObject’s destructor.
Of course, once the TCustomer class is defined in another unit, the unit’s name must be added in the uses clause to
reference it.
Once the TCustomer class declares the Name, Location, and Terms properties, they don’t have to be re-declared in
the sub classes. However, the implementation in the TRetailer’s and TWholesaler’s constructor remains the same, as
if the properties were still declared in the class.
The last thing to notice is that the classes now add only one property, MultipleLocations for TRetailer and
ShipByTruck for TWholesaler. This means that these classes inherit all the characteristics from TCustomer, and
each of them adds its own property.
So, one important concept in inheritance is that it’s always an additive operation. A characteristic can always be
added but what is inherited cannot be selectively determined.
It’s time to create a user interface to test what was learned so far. Figure 2 will help further illustrate the form
design; the relevant components and properties are in Table 1.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 59 -
Figure 2 – Sample Form

Component Property Value

Form Name frmMain

Caption OOP Sample

TButton Name btnCreateRetailer

Caption Create Retailer

TButton Name btnDestroyRetailer

Caption Destroy Retailer

TEdit Name edtRetailerName

TEdit Name edtRetailerLocation

TEdit Name edtRetailerTerms

TCheckBox Name chkMultipleLocations

Caption Multiple Locations?

TButton Name btnCreateWholesaler

Caption Create Wholesaler

TButton Name btnDestroyWholesaler

Caption Destroy Wholesaler

TEdit Name EdtWholesalerName

TEdit Name EdtWholesalerLocation

TEdit Name EdtWholesalerTerms

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 60 -
Component Property Value

TCheckBox Name ChkShipByTruck

Caption Ship by Truck?


Table 1 – Sample Form components and properties list.
Before starting the implementation code for the button, declare two private fields in the TfrmMain class definition,
as follows:

private
ARetailer: TRetailer;
AWholesaler: TWholesaler;
The code for the Create Retailer and Create Wholesaler buttons are almost identical. It’s just a matter of creating an
instance of each class and populating the form with their properties.

procedure TfrmMain.btnCreateRetailerClick(Sender: TObject);


begin
if not Assigned(ARetailer) then
ARetailer := TRetailer.Create;

edtRetailerName.Text := ARetailer.Name;
edtRetailerLocation.Text := ARetailer.Location;
edtRetailerTerms.Text := ARetailer.Terms;
chkMultipleLocations.Checked := ARetailer.MultipleLocations;
end;

procedure TfrmMain.btnDestroyRetailerClick(Sender: TObject);


begin
ARetailer.Free;
end;

procedure TfrmMain.btnCreateWholesalerClick(Sender: TObject);


begin
if not Assigned(AWholesaler) then
AWholesaler := TWholesaler.Create;

edtWholesalerName.Text := AWholesaler.Name;
edtWholesalerLocation.Text := AWholesaler.Location;
edtWholesalerTerms.Text := AWholesaler.Terms;
chkShipByTruck.Checked := AWholesaler.ShipByTruck;
end;

procedure TfrmMain.btnDestroyWholesailerClick(Sender: TObject);


begin
AWholesaler.Free;
end;
With this code, the developer is creating an instance of each object and populating the form with the properties of
the classes.
What’s important to notice here is that, to create the object instance, the developer makes a call to the class’
constructor, assigning it to a variable.
Notice that both classes share some properties and methods. Considering that both TRetailer and TWholesaler are
customers, why not create a Customer class and make them both inherit its characteristics? The Customer class can
include all the characteristics of a desired customer; and, by definition, all descendant classes will inherit them.
When working with method inheritance, consider the advantages and disadvantages of the static, virtual, and
dynamic methods.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 61 -
Method Binding
A method is considered to be static by default. When a static method is called, the type variable declared for that
specific class or object will be considered in determining which implementation should be activated. If a new
method is declared in TCustomer and in TRetailer:

type
TCustomer = class

procedure ReceiveInventory;

end;

TRetailer = class(TCustomer)

procedure ReceiveInventory;

end;
Both methods are static methods. And since TRetailer is a child of TCustomer, TRetailer’s implementation of
ReceiveInventory replaces TCustomer’s implementation.
Pay special attention to the following code:

var
Customer: TCustomer;
Retailer: TRetailer;
begin
Customer := TCustomer.Create;
Customer.ReceiveInventory; //calls TCustomer.ReceiveInventory
Customer.Free;
Customer := TRetailer.Create;
Customer.ReceiveInventory; //calls TCustomer.ReceiveInventory
Customer.Free;
Retailer := TRetailer.Create;
Retailer.ReceiveInventory; //calls TRetailer.ReceiveInventory
Retailer.Free;
end;
There are some things to comment on here.
• The first call to Customer.ReceiveInventory doesn’t show anything new. In the second call, use a
TCustomer variable to hold an instance of TRetailer, because TRetailer descends from TCustomer. In spite
of that, the method used for this call was still TCustomer.ReceiveInventory.
• This is because a call to a static method is always bound to the variable that holds the object, not to the
object itself.
• In the third call, there is a call to TRetailer.ReceiveInventory.

Virtual and Dynamic Methods – Polymorphic Behavior


Virtual and dynamic methods, unlike static methods, can be overridden in descendant classes. When an overridden
method is called, the actual (runtime) type of the class or object used in the method call determines which
implementation to activate – not the declared type of the variable.
To override a method, declare it once again with the override directive. An override declaration must match the
order and type of the parameters in the ancestor declaration, as well as its result type (if any).
Slightly changing the prior example:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 62 -
type
TCustomer = class

procedure ReceiveInventory; virtual;

end;

TRetailer = class(TCustomer)

procedure ReceiveInventory; override;

end;
Now there is a very different behavior:

var
Customer: TCustomer;
begin
Customer := TCustomer.Create;
Customer.ReceiveInventory; //calls TCustomer.ReceiveInventory
Customer.Free;
Customer := TRetailer.Create;
Customer.ReceiveInventory; //calls TRetailer.ReceiveInventory
Customer.Free;
end;
Virtual and dynamic methods are semantically equivalent. They differ only in the implementation of method-call
dispatching at runtime. Virtual methods optimize for speed, while dynamic methods optimize for code size.
In general, virtual methods are the most efficient way to implement polymorphic behavior. Dynamic methods are
useful when a base class declares many overrideable methods - which in turn are inherited by many descendant
classes in an application, but only occasionally overridden.
Only use dynamic methods when there is a clear, observable benefit. In all other cases, use virtual methods.
For both static and virtual (or dynamic) methods, the parent’s method can be called from the child using the reserved
word inherited.
Here’s the new declaration for the TCustomer class:

type
TCustomer = class
Name: String;
Location: String;
Terms: String;
constructor Create; virtual;
end;
…and the new declaration for the TRetailer class:

type
TRetailer = class(TCustomer)
MultipleLocations: Boolean;
constructor Create; override;
end;
Now, change the implementation of the TRetailer class:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 63 -
constructor TRetailer.Create;
begin
inherited;
Name := 'Foobar Antiques';
Location := 'New York, NY';
Terms := 'COD';
MultipleLocations := True;
end;
This ability is shared by all objects. In other words, if a descendant from TRetailer, say TCasualRetailer, is created,
the method can be overridden again.

type
TCasualRetailer = class(TRetailer)
GarageSaleLocation: Boolean;
constructor Create; override;
end;

...

constructor TCasualRetailer.Create;
begin
inherited;
GarageSaleLocation := True;
end;
Now, when creating an instance of TCasualRetailer, the constructor first goes to TRetailer’s constructor, which in
turn goes to the TCustomer’s constructor.
The calling flow would be something like this:

Figure 3 – The flow of calls for overridden methods

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 64 -
Class Scoping
Scoping - or object lifetime and visibility - is an important concept in Delphi. This section will apply the concept of
scope to classes. The developer will use scoping to determine whether the class members’ variables and methods
should be visible to the entire program, to other members in the same family tree (inherited), or outside a class
definition.
Family relationships keep only the information needed to know (private), only family (or relatives or friends) should
know about (protected), and also those things everyone knows about or has access to (public).
Similarly, when defining a class, the developer can designate the scoping for the members of the class.

type
TTestClass = class
strict private
{ Strict Private declarations }
private
{ Private declarations }
strict protected
{ Strict Protected declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;

Private
Class fields and methods that are declared to be private are only accessible to methods of the same class. Member
variables declared to be private are never visible outside the class that defines them, with one exception (addressed
below).
When the methods for a class are declared, the variables of this class’ members are visible from inside the methods,
but not outside of the class. By default, the scope of variables defined within a function is private. It provides
outstanding protection for the information.
The only exception is when two or more classes are defined within the same unit. Classes that are defined in the
same unit are able to “see” (or access) the variables and methods present in each other’s private sections. This
visibility emulates the “friend” functionality found in other object-oriented languages (most notably C++). Beware
that using such friendship associations should be done with care. What it does is essentially to sneak into the
protection barrier the class-level scoping would otherwise automatically provide. Use such functionality
parsimoniously, if ever.

Protected
Similarly to private members, variables and methods that are declared in the protected section are available
elsewhere within the declaring class. Unlike the private scope, however, “protected” class members are also visible
to all the descendants of that class. Thus, if class TMammal () declares a protected variable or method, this variable
or method is visible (or accessible) to all objects that derive from mammals, but nowhere outside the parent or its
derivative classes.
The protected scope renders the ability to hide members from the outside world, while still allowing them to be
available for use by child classes, via inheritance.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 65 -
Public
Assigning a public scope to class variables and methods means that they are available for the use of any of the
objects that are able to access the class at runtime. Most of the methods presented in this course have been publicly
scoped. The constructor and destructor methods are usually defined as public because they must be called by the
developer (or the compiler) to create and destroy an object based on its class.
Class and methods are considered to be public, unless there is a different scope explicitly declares for them.
The right way for changing the default scope of units is by changing the $M compiler directive. The $M directive is
how the default scoping is to change units that include the forms unit.

Published
The published scope is very similar to the public scope, in as much as members declared as published are available
at runtime. However, unlike public members, published members are also visible at design time. This additional
visibility means that members assigned as published can have their values set at runtime or design time.
Published is the default scoping for units that include the forms unit. This means that all the properties seen in the
Object Inspector are there because they were declared as being published.

Automated
The automated scope remains in the language only for backward compatibility with Delphi 2.0. The automated
scope was originally applied to class methods and variables that needed to be exported to Microsoft Windows if the
developer were using Delphi to create an OLEAutomation Server. Now, it should not be used.
In addition to private and protected visibility specifiers, the Delphi for .NET compiler supports additional visibility
settings that comply with the .NET Common Language Specification (CLS). They are the strict private and the strict
protected visibility. In Delphi 2009, these scopes are available for the Win32 compiler as well.

Strict Private
Class members with strict private visibility are only accessible within the class in which they are declared. They are
not visible to procedures or functions declared within the same unit.

Strict Protected
Class members with strict protected visibility are visible within the class in which they are declared, and within any
descendant class, regardless of where it is declared. Other members declared in the same module have no access to
the members of this scope.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 66 -
Polymorphism
Polymorphism is the ability two different yet related classes have to react differently to the same call.
As seen earlier, working with virtual methods, the developer can make a call to an instance and the correct method is
called, based on the object type and not the variable declaration.
Thus, to achieve polymorphism in Delphi, declare a virtual method in a super class and override this method in the
descendant classes.
The previous example will be slightly changed in order to see this in practice:

Figure 4 – Sample form with All Purchase button


The new declaration for the TCustomer class is the following:

type
TCustomer = class
Name: String;
Location: String;
Terms: String;
constructor Create; virtual;
procedure Purchase; virtual; abstract;
end;
A new method (Purchase) was added to the TCustomer class. Notice it is declared as virtual and abstract. Abstract
means that this method has no implementation in this class. Subclasses of this class must implement their own
version of any abstract method.
The new declaration of the TRetailer class is:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 67 -
type
TRetailer = class(TCustomer)
MultipleLocations: Boolean;
public
constructor Create; override;
procedure Purchase; override;
end;
…and for TWholesaler:

type
TWholesaler = class(TCustomer)
ShipByTruck: Boolean;
public
constructor Create; override;
procedure Purchase; override;
end;
These are the definitions for both TRetailer.Purchase and TWholesaler.Purchase:

procedure TRetailer.Purchase;
begin
ShowMessage('I need three boxes of frozen chicken!');
end;

procedure TWholesaler.Purchase;
begin
ShowMessage('I need three truckloads of frozen chicken!');
end;
Classes are now updated, and it’s time to implement the user interface in order to show the use of polymorphism.
Start by declaring an array in the form:

var
ARetailer: TRetailer;
AWholesaler: TWholesaler;
CustomerList: array[1..2] of TCustomer;
Now, when instances of the object are created, add each of them into the array:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 68 -
procedure TFrmMain.BtnCreateRetailerClick(Sender: TObject);
begin
if not Assigned(ARetailer) then
ARetailer := TRetailer.Create;

EdtRetailerName.Text := ARetailer.Name;
EdtRetailerLocation.Text := ARetailer.Location;
EdtRetailerTerms.Text := ARetailer.Terms;
ChkMultipleLocations.Checked := ARetailer.MultipleLocations;

CustomerList[1] := ARetailer;
end;

procedure TFrmMain.BtnCreateWholesalerClick(Sender: TObject);


begin
if not Assigned(AWholesaler) then
AWholesaler := TWholesaler.Create;

EdtWholesalerName.Text := AWholesaler.Name;
EdtWholesalerLocation.Text := AWholesaler.Location;
EdtWholesalerTerms.Text := AWholesaler.Terms;
ChkShipByTruck.Checked := AWholesaler.ShipByTruck;

CustomerList[2] := AWholesaler;
end;
Remember both TRetailer and TWholesaler descend from TCustomer, and then it’s possible to store them in the
same array.
Now, the All Purchase button code:

procedure TFrmMain.BtnAllPurchaseClick(Sender: TObject);


var
I: Integer;
begin
for I := 1 to 2 do
if CustomerList[I] <> nil then
CustomerList[I].Purchase;
end;
Declaring the Purchase member as virtual, the developer guarantees that each member of the alias will have their
method called. More than that: code like this can only be written because the Purchase method was declared in the
TCustomer class. Had it not been done, the compiler would have returned an error stating that Purchase was not a
TCustomer’s member.
One of the most important things to notice is that it is not necessary to know in advance about all types of objects
that will be created from the TCustomer model. As long as each object uses the common functions of the base class
(TCustomer), the developer is free to write the software without having to predefine every type of object. Since all
customers should be able to purchase, treat descendants of TCustomer according to all the attributes they share
through their common parentage.
Polymorphism is the reason why so much attention is being paid to object modeling these days. Once a good model
has been defined, writing code to deal with change can be built into any object-oriented programming system.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 69 -
Properties
Until now, fields have been treated as properties. A property, like a field, defines an attribute of an object. But while
a field is merely a storage location, whose contents can be examined and changed, a property associates specific
actions with reading or modifying its data.
Applying properties to the code has much more benefit than what may be yet understood: a property is essentially
wrapper around variables defined in a class – variables that are typically maintained as private data members.
As will be seen, properties provide a terrific mechanism for information hiding. First the developer will review the
syntax for declaring properties, and then delve deeper into discussing how properties work.

Declaring Properties
The basic syntax for a property declaration is the following:

type
TSampleClass = class
private
FColor: TColor;
public
property Color: TColor read FColor write FColor;
end;
To declare a property, use the property reserved word followed by the property name and type.
The read part determines where the property will get the value from, while the write part determines where in the
object the value will be stored. They also define if the property will be a read-only property (omitting the write part),
a write-only part (omitting the read part) or, as in the sample, a read-write property.
The FColor field is where the property value is stored. By convention, all fields used to store property values are
prefixed with an “F” (for “field”).
As the field is declared in the private class, no one outside the class can access the field directly. Instead, the
property must be used.
One might be wondering: what is the advantage of declaring properties, as it seems that more code has to be written?
The answer to this question is provided by Get and Set routines provided instead of direct access to the private
variable.

Get/Set Routines
Even though the property will be accessed by the code as if it were a simple variable, using get/set routines to
actually store and retrieve the value provides full information hiding. Because no one is allowed to access class
variables directly, the developer is free to change the internal data representation of data protected by get/set
routines in the future.
To access any desired private class property, write a routine to “get” the value, and another routine to “set” the value
(thus the name get/set routines).
Consider this version of the code:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 70 -
type
TSampleClass = class
private
FColor: TColor;
function GetColor: TColor;
procedure SetColor(const Value: TColor);
public
property Color: TColor read GetColor write SetColor;
end;
Notice that the read and write modifiers of this updated definition can accept as parameters either a variable
reference or a method (a procedure for set routines and a function for get routines). In the example above, all access
to TColor is done through the get/set routines; nowhere is the value directly accessed.
As mentioned, this data hiding is beneficial because the developer is now free to change how TColor is kept
internally.The internal color representations may be changed to use direct RGB values instead of the enumerated
TColor type. To make this change, simply change the get/set routines to map the new representation, but there is no
need to make any changes to the property itself or to any other code that uses it.
Because of its ability to hide data, properties utilizing get/set routines provide a well-published interface to the
internals of the class, which may change over time. Any code that depends on this class only needs to be re-
compiled when this class is changed. This new definition effectively hides how color has been implemented
internally, and leaves the developer free to change it whenever he/she wants, as long as the published interface isn’t
changed.
Recall also that it has been declared that a property could be accessed as if it were a simple variable. This is still true
even when the get/set routines are present. Once get/set routines have been defined, Delphi takes care of mapping
the get/set routines to the appropriate places they are used in the code. Due to this mapping, when a value is assigned
to a property, it looks as if another variable were being just used/accessed:

Sample.Color := clRed;
When variable assignments to properties are used, Delphi takes care of invoking the corresponding get/set routines.
Incidentally, using properties is how Delphi manages to act so much like an interpreter during design mode. When a
property with an assignment statement is changed during design-mode, not only the internal value of the property is
changed, but all the code necessary in Windows to make the change come true is also executed. When a color is
assigned to a label’s Color property…

LblTest.Color := clRed;
…the assignment is changing the label’s internal FColor property to clRed, but the set routine is actually also being
called to make the color of the label change to red on the form displayed by the screen.
In general, the developer should come to rely upon properties for all the member variables in the classes that he/she
creates. Even if this level of indirection is not needed now, no one can ever predict when it will be needed in the
future.
A good rule of thumb is to implement properties that directly read and write to the actual internal values until there
be the need to create a side effect as a result of an assignment (like updating the design-time screen). When the
implementation of an interesting new side effect is needed, the developer can then update the get and/or set routine
to provide the additional functionality. By starting the published member variables off as properties, one can be
confident that the published interface to the class does not need to change.
There is the option to change classes to make use of properties. In the TCustomer class, change the declaration to the
following:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 71 -
type
TCustomer = class
public
constructor Create; virtual;
procedure Purchase; virtual; abstract;
property Name: string;
property Location: string;
property Terms: string;
end;
Put the cursor in the class’ declaration and press Ctrl-Shift-C (or right-click and select the Complete class with the
cursor). It will be noticed that Delphi will complete the properties declaration, add the corresponding fields and
create the implementation stub, storing the property value to the field:

type
TCustomer = class
private
FTerms: string;
FLocation: string;
FName: string;
procedure SetLocation(const Value: string);
procedure SetName(const Value: string);
procedure SetTerms(const Value: string);
public
constructor Create; virtual;
procedure Purchase; virtual; abstract;
property Name: string read FName write SetName;
property Location: string read FLocation write SetLocation;
property Terms: string read FTerms write SetTerms;
end;
This feature is called “Class completion” and works for methods, too. For properties, the default is to write the
property value using a set method and read the value directly from the field. The default set implementation is to
simply store the value to the field.
There is also the option to change the TRetailer and TWholesaler classes to implement properties:

type
TRetailer = class(TCustomer)
private
FMultipleLocations: Boolean;
procedure SetMultipleLocations(const Value: Boolean);
public
constructor Create; override;
procedure Purchase; override;
property MultipleLocations: Boolean read FMultipleLocations write
SetMultipleLocations;
end;

TWholesaler = class(TCustomer)
private
FShipByTruck: Boolean;
procedure SetShipByTruck(const Value: Boolean);
public
constructor Create; override;
procedure Purchase; override;
property ShipByTruck: Boolean read FShipByTruck write SetShipByTruck;
end;

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 72 -
Typecasting Objects
Delphi is a strongly typed language. In other words, variables must be of the same type to be assignment compatible
with one another. However, that presents a problem in the OOP world, where it would be desirable to treat related
families of objects in a similar manner. Because hierarchies play such an important role in OOP, it is frequently
convenient to treat an object as another object in the same family (i.e., treat the parent object as if it were a child
object or vice versa). A perfect example of the flexibility that loose typing provides is the sender parameter that
comes with almost every event handler. The sender parameter is almost always of type TObject, which means that it
can be treated like any object that is related to TObject. Because TObject is the ultimate base class from which all
other classes are derived, treat any object as a TObject.
To treat objects as other related objects, use runtime type information (RTTI) and typecasting.

Testing Heritage With the ‘is’ Operator


The is operator allows the developer to test the heritage of the object used as its argument. By using the is operator,
the type of an object can be determined, as well as when it may be re-cast as a different type. As previously seen, it
is possible and often desirable to have a single event handler attached to multiple controls. Attach a menu item’s
OnClick routine and a button’s OnClick routine to refer to the same method of the form. Within the method, use the
Sender parameter to determine which control invoked the method. To determine the Sender’s value, the is operator
allows comparisons with different class types.

procedure TFrmTypecast.BtnTypecastClick(Sender: TObject);


begin
if Sender is TMenuItem then
LblResult.Caption := 'Menu'
else
LblResult.Caption := 'Button';
end;
In other words, is allows the developer to determine the class heritage of the object at runtime. This routine would
also work if it were attached to a BitBtn instead of a regular button. Because TBitBtn inherits from TButton, the is
operator will return true for any member of the class. This explains why Sender is of type TObject – TObject is the
ultimate ancestor of all classes in Delphi; any object can be cast to a TObject.

Typecasting
Once the type of the object has been determined, there is the option to change some of its properties. Typecasting, or
treating the object like a different related object, accomplishes this. Whereas is allows checking the type of the
object at run-time, as allows casting it to another type at run-time.

The ‘as’ Operator


The as operator works much like the is operator, except that instead of returning true or false, it returns the cast
object. The example below shows how to change the caption – rather than the label - on a button when it is clicked.

procedure TFrmTypecast.BtnTypecastClick(Sender: TObject);


begin
if Sender is TMenuItem then
LblResult.Caption := 'Menu'
else
(Sender as TButton).Caption := 'Button';
end;
(Sender as TButton) casts the sender object to the TButton type, and allows the developer to refer to any of the
properties and/or methods of the TButton via the normal dereferencing operator (the period). As previously stated, a
TObject can be cast to any other object type. However, not all objects can be cast to other objects. A button cannot

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 73 -
be cast as a TMenuItem – they are not of the same lineage. If the developer attempts to do so, an EInvalidCast
exception is generated.
The as operator is not the only way to cast objects in Object Pascal. Alternatively, use the C-style typecasting.

C-Style Typecasting
C-style typecasting gets its name from the C language because the syntax is very similar. C-style typecasting
involves placing the object the developer wants cast into parenthesis, with the casting type in front of it. The
example now looks like this:

procedure TFrmTypecast.BtnTypecastClick(Sender: TObject);


begin
if Sender is TMenuItem then
TMenuItem(Sender).Caption := 'Menu'
else
(Sender as TButton).Caption := 'Button';
end;
The code now casts Sender to a TMenuItem, similar to what was done to the button.

The ‘as’ Operator and C-Style Typecasting


With two different casting methods to choose from, which is better? Whenever possible, use the as operator. If the
cast is invalid, Delphi raises an exception. This is not the case with C-style typecasting. If an invalid cast with C-
style typecasting is performed, undefined results will occur. The compiler will not complain or raise an exception,
but serious problems may happen in the application as well as the operating system.
Given the problems associated with C-style typecasting, one might think that it should never be used. While it is
more problematic, there are a few situations where the as operator typecasting cannot be used, e.g., when there is the
need to cast an object in the Evaluate/Modify dialog in the Debugger. Instead, use C-style typecasting. If C-style
typecasting is being used in code, it is advisable to always protect the typecast with an is operator to make sure it is a
valid cast before actually typecasting the object as in the previous example.
C-Style typecasting performs faster than the as style. When the code has already checked the type, as in the example
above, it is faster to use C-style. This is particularly true when a great deal of code iteration is involved.
Typecasting allows the creation of generic collections of objects by creating an array of a base type, and then casting
all the members to their actual type. Every form has a components array, which is defined as an array of
TComponent. Because all components in Delphi are derived from TComponent, the developer can store any type of
component in the array and then cast it to the appropriate type to manipulate it. This is how typecasting and the
object hierarchy are used in Delphi to loosen the restrictions that strong typing places on the programmer.
Another example of this ability to create generic containers is that the objects list that is maintained for
TStringLists.StringLists can hold a collection of strings, but each string can have an object associated with it.
Because TObject is the base class for all classes in Delphi, the developer can associate any Delphi object with an
entry in a StringList, and cast it to the appropriate type when needed.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 74 -
Overloading Methods
A method can be redeclared using the overload directive. In this case, if the redeclared method has a different
parameter signature from its ancestor, it overloads the inherited method without hiding it. Calling the method in a
descendant class activates whichever implementation matches the parameters in the call. If a virtual method is
overloaded, use the reintroduce directive when re-declaring it in descendant classes.

type
T1 = class(TObject)
procedure Test(I: Integer); overload; virtual;
end;

T2 = class(T1)
procedure Test(S: string); reintroduce; overload;
end;
...

SomeObject := T2.Create;
SomeObject.Test('Hello!'); // calls T2.Test
SomeObject.Test(7); // calls T1.Test
Within a class, multiple overloaded methods cannot be published with the same name. Maintenance of runtime type
information requires a unique name for each published member.

type
TSomeClass = class
published
function Func(P: Integer): Integer;
function Func(P: Boolean): Integer; // error
...
Methods that serve as property read or write specifiers cannot be overloaded. The implementation of an overloaded
method must repeat the parameter list from the class declaration.

Overriding Versus Hiding


If a method declaration specifies the same method identifier and parameter signature as an inherited method, and
does not include override, the new declaration merely hides the inherited one without overriding it. Both methods
exist in the descendant class, where the method name is statically bound.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 75 -
type
T1 = class(TObject)
procedure Act; virtual;
end;

T2 = class(T1)
procedure Act; // Act is redeclared, but not overridden
end;

var
SomeObject: T1;

begin
SomeObject := T2.Create;
SomeObject.Act; // calls T1.Act
end;

Reintroduce
The reintroduce directive suppresses compiler warnings about hiding previously declared virtual methods.

procedure DoSomething; reintroduce; // the ancestor class also has a


// DoSomething method
Use reintroduce when hiding an inherited virtual method with a new one is desired.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 76 -
Abstract Methods
An abstract method is a virtual or dynamic method that has no implementation in the class where it is declared. Its
implementation is deferred to a descendant class. Abstract methods must be declared with the directive abstract after
virtual or dynamic methods. The main purpose of abstract methods is to provide a specification for methods that
must be implemented by descendant classes.

procedure DoSomething; virtual; abstract;


The developer can call an abstract method only in a class or instance of a class in which the method has been
overridden.

Note: The Delphi for .NET compiler allows an entire class to be declared abstract, even though it does not
contain any virtual abstract methods.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 77 -
Class Methods
Most methods are called instance methods, because they operate on an individual instance of an object. A class
method is a method (other than a constructor) that operates on classes instead of objects. There are two types of class
methods: ordinary class methods and class static methods.

Ordinary Class Methods


The definition of a class method must begin with the reserved word class.

type
TFigure = class
public
class function Supports(Operation: string): Boolean; virtual;
class procedure GetInfo(var Info: TFigureInfo); virtual;
...
end;
The defining declaration of a class method must also begin with class.

class procedure TFigure.GetInfo(var Info: TFigureInfo);


begin
...
end;
In the defining declaration of a class method, the identifier Self represents the class from where the method is called
(which could be a descendant of the class in which it is defined). If the method is called in the class C, then Self is of
the type class of C. Thus Self cannot be used to access instance fields, instance properties, and normal (object)
methods, but it can be used to call constructors and other class methods, or to access class properties and class fields.
A class method can be called through a class reference or an object reference. When it is called through an object
reference, the class of the object becomes the value of Self.

Class Static Methods


Similarly to class methods, class static methods can be accessed without an object reference. Unlike ordinary class
methods, class static methods have no Self parameter at all. They cannot access any instance members, either.
Methods are made class static by appending the word static to their declaration.

type
TMyClass = class
strict private
class var FX: Integer;
strict protected
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;
public
class property X: Integer read GetX write SetX;
class procedure StatProc(s: String); static;
end;
Like a class method, the developer can call a class static method through the class type (i.e., without having an
object reference).

TMyClass.X := 17;
TMyClass.StatProc('Hello');

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 78 -
Nested Type Declarations
Type declarations can be nested within class declarations.

Declaring Nested Types


The nestedTypeDeclaration follows the type declaration syntax defined in Declaring Types.

type
className = class
memberList

type
nestedTypeDeclaration

memberList
end;
Nested type declarations are terminated by the first occurrence of a non-identifier token, procedure, class, type, and
all visibility scope specifiers.
The normal accessibility rules apply to nested types and their containing types. A nested type can access an instance
variable (field, property, or method) of its container class, but it must have an object reference to do so. A nested
type can access class fields, class properties, and class static methods without an object reference, but the normal
Delphi visibility rules apply.
Nested types do not increase the size of the containing class. Creating an instance of the containing class does not
also create an instance of a nested type. Nested types are associated with their containing classes only by the context
of their declaration.

Declaring and Accessing Nested Classes


The following example demonstrates how to declare and access fields and methods of a nested class.

type
TOuterClass = class
strict private
myField: Integer;
public
type
TInnerClass = class
public
myInnerField: Integer;
procedure innerProc;
end;

procedure outerProc;
end;
To implement the innerProc method of the inner class, qualify its name with the name of the outer class.

procedure TOuterClass.TInnerClass.innerProc;
begin
...
end;
To access the members of the nested type, use dotted notation as with regular class member access.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 79 -
var
x: TOuterClass;
y: TOuterClass.TInnerClass;
begin
x := TOuterClass.Create;
x.outerProc;
...
y := TOuterClass.TInnerClass.Create;
y.innerProc;

Nested Constants
Constants can be declared in class types in the same manner as nested type sections. Constant sections are
terminated by the same tokens as nested type sections, namely reserved words or visibility specifiers.
Nested constants can be of any simple type: ordinal, ordinal sub-ranges, enums, strings, and real types.
The following code demonstrates the declaration of nested constants:

type
TMyClass = class
const
x = 12;
y = TMyClass.x + 23;
procedure Hello;
private
const
s = 'A string constant';
end;

begin
writeln(TMyClass.y); // Writes the value of y, 35.
end.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 80 -
Class Helpers
A class helper is a type that, when associated with another class, introduces additional method names and properties
that may be used in the context of the associated class (or its descendants). Class helpers are a way to extend a class
without using inheritance.
A class helper simply introduces a wider scope for the compiler to use when resolving identifiers. When a class
helper is declared, the helper name is stated, as well as the name of the class that will be extended with the helper.
Use the class helper in any place where the extended class can be legally used. The compiler's resolution scope then
becomes the original class, plus the class helper.
Class helpers provide a way to extend a class, but they should not be viewed as a design tool to be used when
developing new code. They should be used solely for their intended purpose, which is language and platform RTL
binding.

Class Helper Syntax


The syntax for declaring a class helper is:

type
identifierName = class helper [(ancestor list)] for classTypeIdentifierName
memberList
end;
The ancestor list is optional. A class helper type may not declare instance data, but class fields are allowed.
The visibility scope rules and memberList syntax are identical to that of ordinary class types.
Multiple class helpers can be defined and associated with a single class type. However, only zero or one class helper
applies in any specific location in source code. The class helper defined in the nearest scope will apply. The class
helper scope is determined in the normal Delphi fashion (i.e., right to left in the unit's uses clause).

Using Class Helpers


The following code demonstrates the declaration of a class helper:

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 81 -
type
TMyClass = class
procedure MyProc;
function MyFunc: Integer;
end;

...

procedure TMyClass.MyProc;
var
X: Integer;
begin
X := MyFunc;
end;

function TMyClass.MyFunc: Integer;


begin
...
end;

...

type
TMyClassHelper = class helper for TMyClass
procedure HelloWorld;
function MyFunc: Integer;
end;

...

procedure TMyClassHelper.HelloWorld;
begin
writeln(Self.ClassName);
end;

function TMyClassHelper.MyFunc: Integer;


begin
...
end;

...

var
X: TMyClass;
begin
X := TMyClass.Create;
X.MyProc; // Calls TMyClass.MyProc
X.HelloWorld; // Calls TMyClassHelper.HelloWorld
X.MyFunc; // Calls TMyClassHelper.MyFunc
Note that the class helper function MyFunc is called, since the class helper takes precedence over the actual class
type.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 82 -
Sealed Classes
Another way to constrain polymorphic capabilities is through the concept of a sealed class. This is the term used for
a class that cannot be specialized any further. Therefore, nobody can derive a new class from a sealed class. The
Delphi syntax for a sealed class is as follows:

Type
FinalClass = class sealed(TObject)
Procedure Count;
End;
If the developer tries to derive a new class from a sealed class, the Delphi compiler will return the message “Error:
Cannot extend sealed class ‘FinalClass’”.
Note that since a sealed class cannot be further extended, it would be helpful to also explicitly mark all polymorphic
methods with the final keyword.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 83 -
Advanced Concepts

Visual Form Inheritance


Up to this point of this module, object-oriented concepts have been introduced.
Now, learn how to apply these concepts with form classes, known as ‘visual’ inheritance. When the visual form
inheritance concept is used in applications, the developer is able to develop an application with fewer
errors/mistakes and much easier maintenance, thus increasing development productivity.
Using visual form inheritance, all the descendant forms share the same code of their ancestors. Therefore, take a
look at methods that can be included in descendant forms. They present the advantage of reducing the amount of
code required to implement forms in applications, making it easier to achieve consistency of form behavior.
Another advantage is that the changes made to the ancestor form code will also be applied to their descendants on
the next compile or build.

Using Form Inheritance


The inheritance of a form from another form is simple. To demonstrate form inheritance, create a new project and
name it “FormsInherited.dpr”.
Save the form unit as “Umain.pas”
To continue this example, add the forms that will work as models for other forms to the project. Add the first form
File | New | Form – Delphi
Save the first form with the following data. This form will be used in the project as the ancestor:

Property Value

Name frmStartInherited

Caption ::. Start Inherited

Unit Name UfrmStartInherited

Now that a form has been added to the project, analyze the project file to see how it has changed.

Figure 5 – Project File

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 84 -
Figure 5 shows the project file and the project initialization statements. Between “begin” and “end”, there is the
Application.CreateForm call, which “auto-creates” forms. Notice that the Main form and the StartInherited form are
currently auto-created.
Auto-creating all forms is not recommended for most projects. The developer should have only the necessary forms
set as “auto-created”.
To change the way the forms are created, do the following:
Go to Project | Options | Forms

Figure 6 – Project Option’s Forms Window


Forms listed in the “Auto-create forms” section will be created as soon as the application is executed. Because of
that, keep only the necessary forms in this section. All other forms should be kept in the “Available forms” section.
In the current example, set only frmMain as auto-create and set the frmStartInherited form as Available Forms. The
“Available forms” will be created at runtime.
Now, write some lines of code that will be inherited by the descendant forms.
The first step is to write the code that will free the memory of the form once it’s closed. To do so, in the form’s
onClose event, type the following code:

procedure TfrmStartInherited.FormClose(Sender: TObject; var Action:


TCloseAction);
begin
Action := caFree;
end;
TCloseAction consists of the following values:

Value Meaning

caNone The form is not allowed to close, so nothing happens.

caHide The form is not closed, but just hidden. The application can still access a hidden form.

caFree The form is closed and all the memory that was allocated for the form is freed.

caMinimize The form is minimized, rather than closed.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 85 -
The next procedure will be the pressed key check. If the ESC key (#27) is pressed, the form should close down. For
this to happen, enter the following code in the form’s KeyPress event:

procedure TfrmStartInherited.FormKeyPress(Sender: TObject; var Key: Char);


begin
if Key = #27 then
Close;
end;
A form that will work as an ancestor for the other forms has just been created.
To use this form as a model for other forms, access the Object Repository. To do so, select File | New | Other |
Delphi Projects | Inheritable Items.
This section holds all project items that can be inherited. Development can be made easier if it is used as the default
folder for inheritable items.

Figure 7 – Object Repository – Inheritable Items


Select the item frmStartInherited, which will be the ancestor for its new form. This new form will re-use all the
components and code added to its ancestor form.

Figure 8 – Inherited Form

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 86 -
Save the new form with the following configuration:

Property Value

Name frmFirstInherited

Caption ::. First Inherited

Unit Name UfrmFirstInherited

Now there is the need to amend the auto-create form option. To do so, select Project | Options | Forms. Move the
frmFirstInherited from the “Auto-create forms” section to the “Available Forms” section.

Testing the application


To test the application and the use of visual form inheritance, make some changes to the main form.
Add a button to the frmMain form in a way that when it is clicked, it loads the frmFirstInherited form.

Figure 9 – Inheritance Form


First: Add the “UfrmFirstInherited” unit to the Umain.pas uses section.
Then: Add the following code to the button’s OnClick event:

procedure TfrmMain.btnInheritedClick(Sender: TObject);


begin
Application.CreateForm(TfrmFirstInherited, frmFirstInherited);
frmFirstInherited.Show;
end;
The first line of code - “Application.CreateForm(TfrmFirstInherited, frmFirstInherited);” -, will create the form at
runtime; while the second line - “frmFirstInherited.Show;” - , will show the form.
Save the application and execute it (F9).
Notice that by pressing the ESC key (Escape) the form will be closed, showing the code re-use - which is one of the
inheritance objectives.
Using this working structure, application maintenance becomes clearly much easier. When a change that is common
to all forms is needed, it isn’t necessary to change every single one of them. Change the ancestor, and the change
will be reflected on all descendants once the project is recompiled.

Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 87 -
Delphi 2009 Application Development for Win32
- 88 -
Business Code
This chapter describes the differences between Business Code and GUI Code. The developer will learn what
Business rules are, and the importance of separating it from the GUI (Graphical User Interface).
At the end of this chapter, the developer will develop a practical example through which the separation between the
Business Logic and the user presentation is shown.
This module covers:
• Real World Problem
• Business Rules vs. Business Logic
• What is Interface Code?
• Example

Delphi 2009 Application Development for Win32


- 89 -
Real World Problem
Several problems can occur when an application that does not separate its data access code, business logic code, and
presentation code is developed. An application developed without a clear separation between its three parts becomes
difficult to maintain, because the high coupling between all the components may cause severe collateral effects
whenever a change is made anywhere.
High coupling makes it difficult – sometimes even impossible - for re-using classes, because they depend on a
considerable number of other classes. Adding new data views often requires re-implementing or cutting and pasting
business logic code, which in turn requires maintenance in multiple places. Data access code presents the same
dilemma, being cut and pasted among business logic methods which, in many cases, are implemented in visual
component events that are triggered by user actions, such as a button click.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 90 -
Business Rules vs. Business Logic
Business Rules and Business logic are tightly related. However, each of them has its own meaning. The connection
and differences between them are described below:

Business Rules
Business rules describe the operations, definitions and constraints that apply to an organization in achieving its
goals. For example, a business rule might state that no credit check is to be performed on return customers. Others
could define a tenant in terms of solvency, or list the preferred suppliers and supply schedules. These rules are then
used to help the organization better achieve goals, communicate among principals and agents, communicate between
the organization and interested third parties, demonstrate fulfillment of legal obligations, operate more efficiently,
automate operations, perform analysis on current practices, etc.

Business Logic
Business logic refers to the logic that embodies business rules rather than the view of data or storage of data. It is a
term used in software architecture to classify a software component, layer (or tier) of software functionality,
software library or similar that performs operations on some kind of data passed back and forth through it. When
software is designed to achieve a separation of concerns, the business logic is meant to be the domain of a business
manager, even a non-technical one.
Business logic is usually associated with the second, or middle-tier, of a three-tier software architecture.
For instance, there may be a persistence component library and an object-relational mapping layer that brings
information from a database or external data source, and then packages that information into more abstract objects.
The persistence layer and the object relational mapping layer are the first tier. This tier hides the persistence
implementation from the layers above.
The objects provided by the object relational mapping layer are designed to have a closer correspondence to the
business needs the software is meant to satisfy. These objects can be described in the language of a person, even a
non-technical person, who is managing the business directly. This is the business logic. It should be designed with
the assistance of business domain experts. If a business is computerizing a manual process that was previously
performed by a specific person/group of people, rely on these people as business domain experts. They have done
this before, and they know what it takes for the decision making process of the job. This is the second tier.
The third tier is the presentation layer, which is more concerned with how the business communicates with the
consumers of the business process.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 91 -
What is Interface Code?
Interface code is any code that does something related to the GUI. It can take input from the user and display
information, but it should not manipulate information in a way other than formatting and displaying it. A clearly
separated portion of code – routines, modules, or classes – should process the business logic.

When separating the presentation from the business logic, make sure that no part of the business logic
makes any reference to the presentation code. This way, the business logic can be centralized and re-used in
other applications.

Why do this?
Following this principle leads to several good results. Firstly, separating out the presentation code breaks the code
into different areas of concern. Any successful presentation can require a lot of programming, and the code
complexity of the presentation differs from the business logic. A clear separation between the parts of the
application allows the developer to concentrate on each aspect of the problem separately – and one thing at a time. It
also lets different people work on separate parts, and therefore apply specialized skills.
Making the business logic independent of the presentation also allows supporting multiple presentations from the
business logic. The ability to support multiple presentations is what software typically must achieve in today's
application. The business logic code is usually easy to port from platform to platform, but the presentation code is
more tightly coupled to the operating system or hardware. Even without porting, demands for changes in the
presentation are likely to occur more often than those for the business logic. Separating the business logic also
makes it easier to avoid code duplication to support the same business rules.
Different screens often require similar validation logic. However, when the validation logic is hidden among all the
screen handling, it’s difficult to identify it. Suppose an application has 10 forms in which date validation is required.
If the date validation is implemented in one of those forms, one is then bound to change all other forms to reflect
that. Alternatively, when forms are told they should use a class for date validation, the changes performed to the
class implementation are then automatically reflected onto all forms using that class.
In the following example, it will be depicted how to centralize the business logic, as well as how to make the
presentation independent from it.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 92 -
Example
In this example, the developer will create a simple application using the InheritedForms example created in the
Using Form Inheritance section (Object-Oriented Programming chapter). The form created will look like a
calculator (without calculation code), and house the business logic separately in another Unit. With this example,
this approach will be better understood.
The example starts:
Copy the InheritedForms project (and its files) to a new folder to keep them from being changed.
Open the InheritedForms.bdsproj project file.
Change the frmMain’s caption to “OOP Business Code and Interface Code”.
Select the btnInherited button and change its caption to “Calculate”.
Click on the View Form button (Shift + F12) in Delphi’s Tool Bar and open the frmFirstInherited form.

Figure 1 - Opening the form


Add the following components to the frmFirstInherited form:

Class Name Caption/Text

TButton btnAdd Add

TButton btnSubtract Subtract

TButton btnDivide Divide

TButton btnMultiply Multiply

TEdit edtFirstNumber

TEdit edtSecondNumber

TEdit edtResult

TLabel lblFirstNumber First Number

TLabel lblSecondNumber Second Number

TLabel lblResult Result


The frmFirstInherited form should look like Figure 2:

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 93 -
Figure 2 - frmFirstInherited form changed
Save the project.
The calculator’s graphical interface is defined. Although the calculation code could be implemented directly in the
form, the objective of this example is to show the advantages of keeping GUI and business logic separate. Thus,
consider the following schema (Figure 3):

Figure 3 - Example's schema


Add a new unit to the project (File | New | Unit – Delphi) and save it as “UCalculate.pas”

Figure 4 - Adding a new unit


Create a class structure using the Code Template. To do so, press CTRL + J. The options will be listed, as seen in
Figure 5. Select the class template.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 94 -
Figure 5 - Code Templates
Now, change the class name to TCalculate, as in the code snippet below:

Type
TCalculate = class
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }

published
{ published declarations }
end;
To add functionalities to the calculator using the TCalculate class created above, add the following methods to the
TCalculate’s public section:

public
Function Add(First, Second: Double): Double;
Function Subtract(First, Second: Double): Double;
Function Divide(First, Second: Double): Double;
Function Multiply(First, Second: Double): Double;
Press CTRL + SHIFT + C to complete the methods definition. Implement the created methods as follows:
Addition:

function TCalculate.Add(First, Second: Double): Double;


begin
Result := First + Second;
end;
Subtraction:

function TCalculate.Subtract(First, Second: Double): Double;


begin
Result := First - Second;
end;
Division:

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 95 -
function TCalculate.Divide(First, Second: Double): Double;
begin
Result := First / Second;
end;
Multiplication:

function TCalculate.Multiply(First, Second: Double): Double;


begin
Result := First * Second;
end;
Notice that the methods of the TCalculate class receive two parameters, which are the values used to process the
calculation.
Finally, add a method to verify the values that will be passed for the class. For this example, consider passing only
positive values. The method will present the following declaration:

Function ValidateNumber(First, Second: Double): Boolean;


Notice that the method will be a function that receives two numbers and returns a Boolean value. If any of the values
passed is greater than zero, the function returns True; otherwise it returns False.
ValidateNumber method implementation:

function TCalculate.ValidateNumber(First, Second: Double): Boolean;


begin
{Only number greater than zero}
Result := True;
if (First <= 0) or (Second <= 0) then
Result := False;
end;
The TCalculate class is finished. A simple class that implements the application’s business logic has just been built.
Now, add the TCalculate class functionalities to the user interface (frmFirstInherited form). To do so, go back to the
frmFirstInherited form (UMain) and add the UCalculate unit to its Uses clause in the interface section (add the unit
by pressing ALT + F11 and selecting the unit in the Use Unit dialog, as shown in Figure 6 - instead of typing it
manually in the code).

Figure 6 - Use Unit dialog


Add a TCalculate variable to the private frmFirstInherited’s private section, as shown in Figure 7.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 96 -
Figure 7 - TCalculate variable declaration
An instance of TCalculate should be created at the moment the frmMain form is created. To do so, add the following
code to the frmFirstInherited’s onCreate event:

procedure TfrmFirstInherited.FormCreate(Sender: TObject);


begin
inherited;
fCalculate := TCalculate.Create;
end;
Add the following code to the onClick event of the buttons in the frmFirstInherited form:
btnAdd:

procedure TfrmFirstInherited.btnAddClick(Sender: TObject);


begin
inherited;
if fCalculate.ValidateNumber(StrToFloat(edtFirstNumber.text),
StrToFloat(edtSecondNumber.Text)) then
edtResult.Text := FloatToStr(fCalculate.Add(StrToFloat(edtFirstNumber.Text),
StrToFloat(edtSecondNumber.Text)))
Else
ShowMessage('Invalid Number!');
end;
btnSubtract:

procedure TfrmFirstInherited.btnSubtractClick(Sender: TObject);


begin
inherited;
if fCalculate.ValidateNumber(StrToFloat(edtFirstNumber.text),
StrToFloat(edtSecondNumber.Text)) then
edtResult.Text :=
FloatToStr(fCalculate.Subtract(StrToFloat(edtFirstNumber.Text),
StrToFloat(edtSecondNumber.Text)))
Else
ShowMessage('Invalid Number!');
end;
btnDivide:

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 97 -
procedure TfrmFirstInherited.btnDivideClick(Sender: TObject);
begin
inherited;
if fCalculate.ValidateNumber(StrToFloat(edtFirstNumber.text),
StrToFloat(edtSecondNumber.Text)) then
edtResult.Text :=
FloatToStr(fCalculate.Divide(StrToFloat(edtFirstNumber.Text),
StrToFloat(edtSecondNumber.Text)))
Else
ShowMessage('Invalid Number!');
end;
btnMultiply:

procedure TfrmFirstInherited.btnMultiplyClick(Sender: TObject);


begin
inherited;
if fCalculate.ValidateNumber(StrToFloat(edtFirstNumber.text),
StrToFloat(edtSecondNumber.Text)) then
edtResult.Text :=
FloatToStr(fCalculate.Multiply(StrToFloat(edtFirstNumber.Text),
StrToFloat(edtSecondNumber.Text)))
Else
ShowMessage('Invalid Number!');
end;
Next, save the application and run it. Observe the result.

Figure 8 - Application running


There is no apparent difference between implementing the business logic directly in GUI or in a separated class.
However, implementing the business logic in a separated class turns out as a better option for a number of reasons: it
allows re-using the code in other parts of the application; future maintenance and changes affecting the GUI will not
affect the business logic; changes made to the business logic will be automatically reflected in all forms that use its
classes; and even more important, this class can be reutilized by another forms in the same application or, even
better, by another application.

Business Code. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 98 -
UM L Class Diagram
This module introduces the UML (Unified Modeling Language) basic concepts. The focus will be on Class
Diagrams, which help develop applications.
This module covers:
• Introduction to the UML
• Together
• Model View
• Example
• Delphi Class Explorer

Delphi 2009 Application Development for Win32


- 99 -
Introduction to the UML (Unified
Modeling Language)
The heart of object-oriented problem solving is the construction of a model. Models abstract the essential details of
the underlying problem from its usually complicated real world. Several modeling constructs are wrapped under the
heading of the UML™, which stands for Unified Modeling Language™.
At the center of the UML are its nine kinds of modeling diagrams, which are described here:
• Use case diagrams
• Class diagrams
• Object diagrams
• Sequence diagrams
• Collaboration diagrams
• Statechart diagrams
• Activity diagrams
• Component diagrams
• Deployment diagrams
Some of the sections of this course contain links to pages that hold more detailed information on the subject they
address and every section is provided with short questions aimed at testing the developer’s understanding of the
section topic.

Why is UML important?


Let's look at this question by using an analogy from a construction trade perspective. Architects design buildings.
Builders use designs to create buildings. The more complicated the building, the more critical the communication
between architect and builder. Blueprints are the standard graphical language that both architects and builders must
learn as part of their trade.
Writing software is not unlike constructing a building. The more complicated the underlying system, the more
critical the communication among everyone involved in creating and deploying the software. In the past decade,
UML has emerged as the software blueprint language for analysts, designers, and programmers alike. It is now part
of the software trade. UML gives everyone - from the business analyst, through the designer, to the programmer - a
common vocabulary to talk about software design.
The UML is applicable to object-oriented problem solving. Anyone interested in learning UML must be familiar
with the underlying tenet of object-oriented problem solving -- it all begins with the construction of a model. A
model is an abstraction of the underlying problem. The domain is the actual world from which the problem comes.
Models consist of objects that interact by sending messages to each other. Think of an object as "alive." Objects hold
things they know (attributes) and the things they can do (behaviors or operations). The values of an object's
attributes determine its state.
Classes are the "blueprints" for objects. A class wraps attributes (data) and behaviors (methods or functions) into a
single distinct entity. Objects are instances of classes.

Use case diagrams


Use case diagrams describe what a system does from the standpoint of an external observer. The emphasis is on
what a system does rather than how it does it.
Use case diagrams are closely connected to scenarios. A scenario is an example of what happens when someone
interacts with the system. Here is a scenario for a medical clinic.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 100 -
"A patient calls the clinic to make an appointment for a yearly checkup. The receptionist finds the nearest empty
time slot in the appointment book and schedules the appointment in that time slot. "
A use case is a summary of scenarios for a single task or goal. An actor is who or what initiates the events involved
in that task. Actors are simply the roles people or objects play. Figure 1 is a Make Appointment use case for the
medical clinic. The actor is a Patient. The connection between actor and use case is a communication association (or
communication for short).

Figure 1 – Use Case


Actors are represented by stick figures. Use cases are represented by ellipses. Communications are lines that link
actors to use cases.
A use case diagram is a collection of actors, use cases, and their communications. Make Appointment has been put
as part of a diagram with four actors and four use cases. Notice that a single use case can have multiple actors.

Figure 2 – Use Case: Collection of actors


Use case diagrams are helpful in three areas.
• Determining features (requirements): New use cases often generate new requirements as the system is
analyzed and the design takes shape.
• Communicating with clients: Their notational simplicity makes use case diagrams a good way for
developers to communicate with clients.
• Generating test cases: The collection of scenarios for a use case may suggest a suite of test cases for those
scenarios.

Class diagram
A class diagram offers an overview of a system, showing its classes and the relationships among them. Class
diagrams are static; they display the interaction actors, but not what happens during the interaction.
The following class diagram models a customer order from a retail catalog. The central class is the Order.
Associated to the Order, there is the Customer who is making the purchase, along with the Payment. There are three
types of payments: Cash, Check, or Credit. The order contains OrderDetails (line items), each with its associated
Item.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 101 -
Figure 3 – Class diagram example
The UML class notation is represented by a rectangle divided into three parts: class name, attributes, and operations.
Names of abstract classes, such as Payment, are presented in italics. Relationships between classes are displayed as
the connecting links.
The class diagram used in this example holds three kinds of relationships:
• Association: A relationship between instances of the two classes. An association between two classes
occurs every time an instance of one class must know about the other in order to perform its work. In a
diagram, an association is a link connecting two classes.
• Aggregation: An association in which one class belongs to a collection. An aggregation has a diamond-end
that points to the part that contains the whole. In this diagram, Order has a collection of OrderDetails.
• Generalization: An inheritance link indicating one class is a superclass of the other. A generalization has a
triangle pointing to the superclass. Payment is a superclass of Cash, Check, and Credit.
An association has two ends. An end may have a role name to clarify the nature of the association. For example, an
OrderDetail is a line item of each Order.
A navigation arrow in an association shows to which direction the association can be traversed or queried. An
OrderDetail can be queried about its Item, but not the other way around. The arrow also informs who “owns” the
association’s implementation; in this case, OrderDetail has an Item. Associations with no navigation arrows are bi-
directional.
The cardinality of an association end is the number of possible instances of the class associated with a single
instance of the other end. Cardinalities are single numbers or ranges of numbers. In our example, there can be only
one Customer for each Order, but a Customer can have any number of Orders.
This table gives the most common multiplicities.

Multiplicities Meaning

0..1 zero or one instance. The notation n . . . m indicates n to m instances.

0..* or * no limit on the number of instances (including none).

1 exactly one instance

1..* at least one instance


Every class diagram has classes, associations, and multiplicities. Navigability and roles are optional items placed in
a diagram to provide clarity.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 102 -
Packages and Object Diagrams
To simplify complex class diagrams, group classes into packages. A package is a collection of logically related
UML elements. The diagram below is a business model in which the classes are grouped into packages.

Figure 4 – Object Diagram


Packages appear as rectangles with small tabs at the top. The package name is on the tab or inside the rectangle. The
dotted arrows are dependencies. One package depends on another when changes in the other could possibly force
changes in the first.
Object diagrams show instances instead of classes. They are useful for explaining small pieces with complicated
relationships, especially recursive relationships.
This small class diagram shows that a university Department can contain lots of other Departments.

Figure 5 – Class diagram


The objects diagram below instantiates the class diagram, providing a concrete example to represent it.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 103 -
Figure 6 – Instantiates the class diagram, providing a concrete example to represent it.
Each rectangle in the object diagram corresponds to a single instance. Instance names are underlined in UML
diagrams. Class or instance names may be omitted from object diagrams as long as the diagram meaning is still
clear.

Sequence Diagrams
Class and object diagrams are static model views. Interaction diagrams are dynamic. They describe how objects
collaborate.
A sequence diagram is an interaction diagram that details how operations are carried out - what messages are sent
and when. Sequence diagrams are organized according to time. The time progresses as the page goes down. The
objects involved in the operation are listed from left to right according to when they take part in the message
sequence.
Below is a sequence diagram for making a hotel reservation. The object initiating the sequence of messages is a
Reservation window.

Figure 7 – Sequence Diagram.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 104 -
The Reservation window sends a makeReservation() message to a HotelChain. The HotelChain then sends a
makeReservation() message to a Hotel. If the Hotel has available rooms, then it makes a Reservation and a
Confirmation.
Each vertical dotted line is a lifeline, representing the time during which an object exists. Each arrow is a message
call. An arrow goes from the sender to the top of the activation bar of the message on the receiver's lifeline. The
activation bar represents the duration of a message’s execution.
In our diagram, the Hotel issues a self call to determine if a room is available. If so, then the Hotel creates a
Reservation and a Confirmation. The asterisk on the self call means iteration (ensuring there are rooms available in
each of the days of the stay in the hotel). The expression in square brackets, [ ], is a condition.
The diagram has a clarifying note, which is text inside a dog-eared rectangle. Notes can be put into any kind of
UML diagram.

Collaboration Diagrams
Collaboration diagrams are also interaction diagrams. They convey the same information as sequence diagrams, but
they focus on object roles rather than when those messages are sent. In a sequence diagram, object roles are the
vertices and messages are the connecting links.

Figure 8 – Collaboration Diagrams


The object-role rectangles are labeled with either class or object names (or both). Class names are preceded by
colons (“:”).
Each message in a collaboration diagram has a sequence number. The top-level message is numbered 1. Messages at
the same level (sent during the same call) have the same decimal prefix, but are suffixed 1, 2, etc. - according to
when they occur.

Statechart Diagrams
Objects have behaviors and state. The state of an object depends on its current activity or condition. A statechart
diagram shows the possible states of the object and the transitions that cause a change in state.
The example diagram models the login part of an on-line banking system. Logging in consists in entering a valid
social security number and personal id number, then submitting the information for validation.
Logging in can be factored into four non-overlapping states: Getting SSN, Getting PIN, Validating, and Rejecting.
From each state comes a complete set of transitions that determine the subsequent state.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 105 -
Figure 9 – Statechart Diagrams
States are rounded rectangles. Transitions are arrows from one state to another. Events or conditions that trigger
transitions are written beside the arrows. The diagram has two self-transitions, one on Getting SSN and another on
Getting PIN.
The initial state (black circle) is a dummy that starts the action. Final states are also dummy states that terminate the
action.
The action that occurs as a result of an event or condition is expressed in the form /action. While in its Validating
state, the object does not wait for an outside event to trigger a transition. Instead, it performs an activity. The result
of that activity determines its subsequent state.

Activity Diagrams
An activity diagram is essentially a fancy flowchart. Activity diagrams and statechart diagrams are related. While a
statechart diagram focuses attention on an object undergoing a process (or on a process as an object), an activity
diagram focuses on the flow of activities involved in a single process. The activity diagram shows how those
activities depend on one another.
For the example, the following process was used.
• "Withdraw money from a bank account through an ATM."
The three classes (people, etc.) involved in the activity are Customer, ATM, and Bank. The process begins at the
black start circle at the top, and ends at the concentric white/black stop circles at the bottom. The activities are
rounded rectangles.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 106 -
Figure 10 – Activity Diagrams
Activity diagrams can be divided into object swimlanes that determine which object is responsible for which
activity. A single transition comes out of each activity, connecting it to the next activity.
A transition may branch into two or more mutually exclusive transitions. Guard expressions (inside brackets[ ]) label
the transitions coming out of a branch. A branch and its subsequent merge – that marks the end of the branch -
appear in the diagram as hollow diamonds.
A transition may fork into two or more parallel activities. The fork and the subsequent join of the threads coming out
of the fork appear in the diagram as solid bars.

Component and Deployment Diagrams


A component is a code module. Component diagrams are physically analog to class diagrams. Deployment diagrams
show the physical configurations of software and hardware.
The following deployment diagram shows the relationships among software and hardware components involved in
real estate transactions.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 107 -
Figure 11 – Deployment Diagrams
The physical hardware is made up of nodes. Each component belongs on a node. Components are shown as
rectangles with two tabs at the upper left.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 108 -
Together
Borland Together is the visual tool used to create UML models for applications.
One of the most exciting new additions to Delphi 2009 is that it now includes a full standard UML 1.5 and UML 2.0
Together modeling capability.
Effective modeling with Together simplifies the development stage of the project. Smooth integration to Delphi
2007 provides developers with easy transition from models to source code. The primary objective of modeling is to
organize and visualize the structure and components of software intensive systems.
Models visually represent requirements, subsystems, logical and physical elements, and structural and behavioral
patterns. While contemporary software practices stress the importance of developing models, Together extends the
benefits inherent to modeling by fully synchronizing diagrams and source code.
The seamless integration found in Delphi 2009 helps foster better communication between the developer and the
other major stakeholders of the project and development teams. By enabling better communication, the developer
can design and develop faster and better, and significantly reduce missteps. The Together integration allows the
provision of advanced models of the systems before and during the development process. Meanwhile, code audits
and metrics ensure that what is being developed is easier to maintain and will be delivered in accordance to the
highest standards.

LiveSource
LiveSource is the one key feature of Together that keeps the model and source code in sync. That is why it only
applies to implementation projects. When a Class diagram is created in an implementation project, it is immediately
synchronized with the implementation code. When a Class diagram is changed, Together updates the corresponding
source code. Together allows synchronizing different aspects of the project in several ways. Use the Reload
command to refresh the Together model from the source code.
As seen on Figure 12 and Figure 13, when changes are made in the diagram, the code will be changed to reflect it.
When the code changes, the diagram will automatically reflect the changes, just as well.

Figure 12 – Together Class Diagram

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 109 -
Figure 13 – Together LiveSource

Diagram Types
The following diagrams and constructs are available: Class diagram, Use Case diagram, Sequence diagram,
Collaboration diagram, Statechart diagram, Activity diagram, Component diagram, Deployment diagram, Class,
Interface, Structure, Enumeration, Delegate, Namespace, Object, Constraint, and Note. See in Figure 14 the UML
diagram types that are now available from the Add New Diagram dialog box.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 110 -
Figure 14 – New Diagram Repository

Design Patterns
Design patterns are formally defined solutions to recurring problems in software development. In most cases, design
patterns define a set of interactions between objects that can be reused in a variety of software solutions.
Borland Together now provides high-level support for design patterns. Delphi 2009 ships with a large number of
industry-standard patterns, including those defined in the now classic book “Design Patterns: Elements of Reusable
Object-Oriented Software “(1995, Addison-Wesley Professional Computing Series) by Erich Gamma, Richard
Helm, Ralph Johnson, and John Vlissides (affectionately known as the Gang of Four, or GoF).
In addition to the bundled patterns, the developer can also create and save custom design patterns. Both the bundled
patterns and the custom patterns can be accessed using the Pattern Organizer, which is shown in Figure 15.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 111 -
Figure 15 – Pattern Organizer

Metrics and Audits


More than simply a modeling tool, Together can be used to analyze existing projects, generating metrics and audits
that can be used to uncover potential problems with applications.
For example, Figure 16 shows a Together-generated Kiviat chart. A Kiviat chart depicts a best practices circle.
Points that are within the inner circle are considered acceptable, and those outside denote areas that may deserve
attention. For example, in the graph shown here, the depth of inheritance hierarchy (DOIH) appears outside the user-
defined inner circle. This metric indicates that one or more classes are abnormally deep, with respect to the root
class (Object or TObject). To create a Kiviat, follow these steps: select Model View, right-click on OOPSample,
select QAMetric and click on Start, then right-click on MainForm, OOPSample or UClass, and select Kiviat chart.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 112 -
Figure 16 – Together-generated Kiviat chart
The inner-circle is user-defined because the developer sets the limits for the metrics used by Together. The
following figure shows the QA Metric dialog box where these metrics are configured.

Figure 17 – QA Metric dialog box

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 113 -
The use and configuration of audits is similar to metrics. Figure 18 shows the QA Audits dialog box, which contains
the audits equivalent to the QA Metrics dialog box.

Figure 18 – QA Audits dialog box

Documentation Generation
One of the major benefits of Together integration is the automatic document generation. Documents are generated as
HTML, thus allowing their visualization in a browser or post them to the team's internal project Web site.
To generate HTML documentation using Together, right-click the Model View pane from a Together-enabled
project and select Generate Documentation. Use the Generate Documentation dialog box, shown in Figure 19, to
select the desired information to be included in the generated documents.

Figure 19 – Generate Documentation dialog box


Figure 20 shows a sample of Together-generated documentation being displayed in a browser.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 114 -
Figure 20 – Sample of Together – Generated Documentation

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 115 -
Example
To see how the UML is used in Delphi, go back to the OO example, in which classes were built and code defined,
without using Model View.
In the example, the following classes were defined: TCustomer, TRetailer, and TWholesaler (notice that the last two
were derived from the first).
For being able to manipulate the class diagrams while working with Model View, enable the project to support it. To
do so, click on the Model View tab and a message will prompt the confirmation of the modeling support, as shown
in Figure 21.

Figure 21 – Modeling support confirmation dialog


Another option to enable the modeling support is to select Project | Together Support.

Figure 22 – Modeling support menu item


The second option is necessary when the Model View tab is selected and the message is not displayed. It usually
occurs when the message is displayed and the NO button is clicked.
Now, go to the example in which the classes will be defined in Model View. For learning purposes, consider
creating all classes in the same unit. To do so, follow these steps:
Add a new unit to the project, selecting File | New | Unit – Delphi.
Save the unit as “UClass.pas”.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 116 -
Select the Model View tab and notice that a new unit has been added, as shown in Figure 23. To access the Class
Diagram, double-click the unit name.

Figure 23 – Class Diagrams


Now the class implementation begins. The first class to be added is the TCustomer class. Right-click the diagram
and select Add | Class, as shown in Figure 24

Figure 24 – Adding classes to a diagram


Notice that a class is being created, but there are other available types as Interface, Structure, Enumeration, etc.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 117 -
Figure 25 – Class added to diagram
All class properties can be modified using the Object Inspector. Change the class name created to TCustomer.
Now, the Fields, Methods and properties necessary to the application will be added. Start by adding the class
properties. To do so, right-click the class and select Add | Property.

Figure 26 – Adding properties


This property will have the following definition:
• Name: Name.
• Type: String.
Notice that after the property was created, two methods were added to the class. These methods complement the
created property. They are used to read (getName) and write (setName) the property values.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 118 -
Now, the methods should be defined and the field should be added to the property. In order to add a field, right-click
the class and select Add | Field. Define the created field as follows:
• Name: FName
• Type: String
Now, let’s define the methods. To do so, right-click the class and select Go To Definition.

Figure 27 – Showing the class code


The class code is then shown (notice the synchronization between the class diagram and the code). Now, complete
the class methods as in the code below:

unit UClass;

interface

type
TCustomer = class
strict private
procedure SetName(val : string);
function GetName : string;

public
property Name : string read GetName write SetName;

var
FName:String;
end;

implementation

function TCustomer.GetName: string;


begin
Result := FName;
end;

procedure TCustomer.SetName(val : string);


begin
FName := val;
end;

end.
Now, add other properties and fields as follows:

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 119 -
Property Type

Location String

Terms String

Field Type

FLocation String

FTerms String

To complete the TCustomer class, add the Create method, which will be the class constructor, and also the Purchase
method. The Purchase method will be a virtual (meaning that it can be overridden), and Abstract (meaning that it
will have no implementation in the TCustomer class, while still needing to have implementation in its inherited
classes - polymorphism) method.
To add the Purchase method, right-click the TCustomer class and select Add | Procedure.
The method should have the following definition:
• Name: Purchase.
• Virtual: True.
• Abstract: True.

Figure 28 – Adding Procedures to the diagram


Now, implement the TCustomer class constructor by right-clicking the TCustomer class and selecting Add |
Constructor.
The created method will be inserted in the code. Implement it by adding the following code:

constructor TCustomer.Create;
begin
Name := 'The Customer';
Location := 'New York, NY';
Terms := 'Net 30';
end;
Thus, a complete class (TCustomer) that was created with Model View is obtained. See the complete class code
below:

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 120 -
unit UClass;

interface

type
TCustomer = class
strict private
procedure SetTerms(val : String);
function GetTerms : String;
procedure SetLocation(val : String);
function GetLocation : String;
procedure SetName(val : string);
function GetName : string;

public
property Name : string read GetName write SetName;

property Location : String read GetLocation write SetLocation;

property Terms : String read GetTerms write SetTerms;

procedure Purchase;virtual; Abstract;


constructor Create;

var
FName:String;
FLocation:String;
FTerms:String;
end;

implementation

function TCustomer.GetName: string;


begin
Result := FName;
end;

procedure TCustomer.SetName(val : string);


begin
FName := val;
end;

function TCustomer.GetLocation: String;


begin
Result := FLocation;
end;

procedure TCustomer.SetLocation(val : String);


begin
FLocation := val;
end;

function TCustomer.GetTerms: String;


begin
Result := FTerms;
end;

procedure TCustomer.SetTerms(val : String);


begin
FTerms := val;

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 121 -
end;

constructor TCustomer.Create;
begin
Name := 'The Customer';
Location := 'New York, NY';
Terms := 'Net 30';
end;

end.
The TCustomer class should look like the following class:

Figure 29 – TCustomer Class Structure


The next class is the TRetailer class. This class will inherit from TCustomer. The Inheritance concept has already
been explained in previous chapters. To create the TRetailer class, right-click the diagram and select Add | Class.
Add a property with the following definition:
• Name: MultipleLocations
• Type: Boolean
Repeat the steps done in the previous class definition (TCustomer).
Now, create a constructor for the TRetailer class. Right-click the TRetailer class and select Add | Constructor.

constructor TRetailer.Create;
begin
inherited Create;
Name := 'Foobar Antiques';
Location := 'New York, NY';
Terms := 'COD';
MultipleLocations := True;
end;
Finally, implement the TRetailer’s Purchase method, which was defined as Abstract in TCustomer and needs to be
implemented in it.
Right-click the TRetailer class and select Add | Procedure. Define the method as follows:
• Name: Purchase
• Override: True
Implement the Purchase Method as follows:

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 122 -
procedure TRetailer.Purchase;
begin
ShowMessage('Borland Dephi 2009 - TRetailer');
end;
Don’t forget to add the Dialogs unit to the project Uses clause.
Override: indicates that a method declared in the previous class is being overridden.
Now that the classes have been created, define the TRetailer class inheritance. This class will inherit from the
TCustomer class. The Model View allows that it be done in design mode.
Notice that in Figure 30, in the Tool Palette, there are the components to create Associations, Generalizations,
among others. In this example, use the Generalization tool, which will implement the class inheritance.

Figure 30 – UML Class Diagram


To implement the inheritance, select the Generalization tool in the Tool Palette, and click first on the class that will
inherit (TRetailer) and next on the TCustomer class. It should look like Figure 31.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 123 -
Figure 31 – Using the Generalization tool for Inheritance
Analyzing the application code, notice the synchronization between the design and the code, which facilitates the
work.
To complete the model, add a TWholesaler class by following the same steps followed to create the previous
classes.
Add the new class to the diagram, defining it as follows, and then rename it as TWholesaler
Add a new property:
• Name: ShipByTruck
• Type: Boolean
Add a new field
• Name: FShipByTruck
• Type: Boolean
• The ShipByTruck property will have to write the setShipByTruck method and will access the FShipByTruck
value directly from it. Define the property as follows:
The ShipByTruck property will have the setShipByTruck method to write and will access the FShipByTruck value
directly from it. Define the property as follows:

property ShipByTruck : Boolean read FShipByTruck write SetShipByTruck;


• As for the TRetailer class, implement its Purchase method, which was defined as Abstract in the TCustomer
class. Right-click the TWholesaler and select Add | Procedure.
Define the method as follows:
• Name: Purchase
• Override: True
Implement the Purchase method as follows:

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 124 -
procedure TRetailer.Purchase;
begin
ShowMessage('Borland Dephi 2009 - TWholesaler');
end;
Now create the TWholesaler class constructor. Right-click the TWholesaler class and select Add | Constructor.
Add the following code to the method:

constructor TWholesaler.Create;
begin
inherited Create;
Name := 'The Antique Warehouse';
Location := 'Boise, ID';
Terms := 'Net 30';
ShipByTruck := True;
end;
Now, define the TWholesaler class inheritance. This class will inherit from the TCustomer class. To do so, use the
Generalization tool in the Tool Palette. Click first in the TWholesaler class and then in the TCustomer class.
The class diagram should look as follows:

Figure 32 – Complete application diagram


With this example, notice that Together (Model View) facilitates the work, allowing a better view of the OO
structure.
As mentioned at the beginning, the objective of this chapter is to introduce modeling, showing the class diagram
concepts in the Object Oriented work.
In the prior examples, only defined classes, such as TCustomer, TRetailer, and TWholesaler were developed. They
are part of the application, but the form class or any other class of the application can be changed.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 125 -
Delphi Class Explorer
The Class Explorer is a new feature that allows a complete navigation through the units of open projects. With the
Class Explorer, it is possible to visualize types, classes, records, interfaces, and namespaces, sorting them by
hierarchy type. It is also possible to optimize the creation of members, such as fields and methods. In the upper
window, it is possible to see units, types, classes, interfaces, and namespaces; in the lower window, the list of the
local and global members is available to be viewed.

Figure 33 – Class Explorer


In the upper left dropdown list of the Class Explorer window, it is possible to choose the desired method to view the
Units: Base to Derived, Derived to Base, and Container methods. In the upper right dropdown list from the same
window, it is possible to select any active project. In this example, Derived to Base is the selected option to view the
units and MeetingOrganizer is the active project, as shown in Figure 2.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 126 -
Figure 34 – Class Explorer on Derived to base
To add a field, declaration, or property, expand the desired unit to show its members, right-click on any unit
member, and select the desired action.

Figure 35 – Class Explorer context menu


Choosing the Add Field… option, the Add Field dialog is displayed. To create a new field, this dialog requests the
field name, the class to which this field should be added, the field type, and its visibility.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 127 -
Figure 36 – Add Field dialog
Choosing the Add Operation… option, the Add Operation dialog is displayed. To create a new operation, it is
necessary to fill the method name, the class to which this operation should be added, the arguments to be passed, the
method type (if it will be a function, a procedure, a constructor, or a destructor), and its visibility.

Figure 37 – Add Operation dialog


Choosing the Add Property… option, the Add Property dialog box is displayed. To create a new property, fill the
property name, the class to which this property should be added, its type, and its visibility. Moreover, fill the reader
and writer textboxes, which represent, respectively, the get and set properties used to read and write the value of an
attribute in a class.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 128 -
Figure 38 – Add Property dialog box
The last two options, Go to Declaration and Go to Definition, are used to find the declaration or the definition of an
object inside the code, respectively. The Go to Declaration option will show the declaration of the object inside the
Type part in the code.

Figure 39 – Go to Declaration
The Go to Definition option will show the object definition inside the Implementation part.

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 129 -
Figure 40 – Go to Definition

UML Class Diagram. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 130 -
The Singleton Design P attern
This module covers:
• A Brief Introduction to the Singleton Design Pattern
• Creating an Application that Utilizes the Singleton Design Pattern

Delphi 2009 Application Development for Win32


- 131 -
Patterns
As mentioned in the previous module, Design Patterns are formally defined solutions to recurring problems in
software development. In most cases, design patterns define a set of interactions between objects that can be re-used
in a variety of software solutions.
The objective of this chapter is to introduce the Singleton Design Pattern.

What is the Singleton Pattern


Singleton is a Design Pattern that refers to a class that has one single instance. The Singleton pattern is typically
used to represent something single in an application.
This is a specialized pattern that gives the client access to an object that is created with only one instance, and is
shared across the application.
The pattern relieves the client from the responsibility of ensuring that there is just one instance, regardless of how
many attempts are made to instantiate the object.
The Singleton pattern is a specialized creational pattern as its primary focus is to facilitate a single shared instance of
our object, rather than to decouple our client from the object's implementation - as with the other creational patterns.

When should be used


The pattern is useful when the design requires an object for which there must be only one instance that will be
shared across the application.
The point of the pattern is to relieve the client from the task of ensuring that there is only once instance of this
object.
This will simplify the client by allowing it to focus upon the business intent rather than on mechanic issues such as
the singleton requirement. It will also avoid a potential client program bug, where one reference to the object
incorrectly allows another instance to be created. The following example shows the client "creating" and using a
singleton object.

Using the Singleton Pattern


To see how to use the Singleton Pattern, a simple application with a form for user authentication will be created. The
authentication information (name and username of a user) can be used in other application parts.
The sample application will have a main form, a login form, a class for the user information and a class for the user
control in which the Singleton will be implemented.

Main Form
Let’s start the sample application:
• Create a new project by choosing File | VCL Forms Application – Delphi
• Save the project, choosing File | Save All
• Save the unit as “UMain.pas”
• Save the project as “Singleton.bdsproj”
Define the form as follows:

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 132 -
Property Value

Caption ::. Singleton

FormStyle fsMDIForm

WindowState wsMaximized
Add a TStatusBar component to the frmMain form and rename it as “sbMain”. This component will show the name
and the username of the logged user.
Now, add two panels to the TStatusBar component. To do this, right-click on sbMain and choose the Panels Editor.
Then, right-click on the Panels Editor and choose Add, as shown in Figure 1.

Figure 1 – TStatusBar’s Panels Editor


The main form will be very simple, as shown in Figure 2. It will only show user information.

Figure 2 – Application’s main form

The User class


The User class will hold the following user information: Name, Username, and Password.
Add a unit to the project choosing File | New | Delphi.
Save the created Unit as “UUser.pas”.

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 133 -
Using the Model View (see UML – Creating Class Diagrams for more information about using the Model View),
add a class to the UUser unit with the following structure:

Figure 3 – TUser class

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 134 -
User class code is presented below:

unit UUser;

interface

type
TUser = class
strict private
procedure SetPassWord(val : String);
function GetPassWord : String;
procedure SetUserName(val : String);
function GetUserName : String;
procedure SetName(val : String);
function GetName : String;

var
FName:String;
FPassWord:String;
FUserName:String;

public
property Name : String read GetName write SetName;
property UserName : String read GetUserName write SetUserName;
property PassWord : String read GetPassWord write SetPassWord;

constructor Create;
end;

implementation

function TUser.GetName: String;


begin
Result := FName;
end;

procedure TUser.SetName(val : String);


begin
FName := val;
end;

function TUser.GetUserName: String;


begin
Result := FUserName;
end;

procedure TUser.SetUserName(val : String);


begin
FUserName := val;
end;

function TUser.GetPassWord: String;


begin
Result := FPassWord;
end;

procedure TUser.SetPassWord(val : String);


begin
FPassWord := val;
end;

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 135 -
constructor TUser.Create;
begin
Name := 'Bruce Young';
UserName := 'BRUCE';
PassWord := 'YOUNG';
end;

end.

User Control class


The next step is to implement the Singleton pattern. The Singleton pattern will be implemented in the TUserControl
class, which will be responsible for the business rules. To do so, add a new unit to the project and save as
“UUserControl.pas”.
Using the Model View, add a class to the UUserControl unit with the following structure:

Figure 4 – TUserControl class


The TUserClass class code is listed below:

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 136 -
unit UUserControl;

interface

type
TUserControl = class
strict private
procedure SetUsername(val : String);
function GetUsername : String;
procedure SetName(val : String);
function GetName : String;

public
function DoUserLogin: boolean;
function ValidateUser(Username, Password: String): boolean;

property Name : String read GetName write SetName;

property Username : String read GetUsername write SetUsername;

strict private var


FName:string;
FUsername:string;
end;

implementation

uses UUser, uLogin, Controls;

{Function to load the form as a modal form}


function TUserControl.DoUserLogin: boolean;
var frmUserLoginForm: TfrmLogin;
begin
frmUserLoginForm := TfrmLogin.Create(nil);
try
result := (frmUserLoginForm.ShowModal = mrOK);
finally
frmUserLoginForm.Free;
end;
end;

{Validate the user. Notice that the user could be storage in a database or in any
other place}
function TUserControl.ValidateUser(Username, Password: String): boolean;
var User: TUser;
begin
User := TUser.Create;
if (Username = User.UserName) and (Password = User.Password) then
begin
FName := User.Name;
FUserName := User.UserName;
result := True;
end
else
Result := False;
end;

function TUserControl.GetName: String;


begin
Result := FName;

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 137 -
end;

procedure TUserControl.SetName(val : String);


begin
FName := val;
end;

function TUserControl.GetUsername: String;


begin
Result := FUsername;
end;

procedure TUserControl.SetUsername(val : String);


begin
FUsername := val;
end;

end.
Remember to add the following code after the implementation reserved word:

uses
UUser, ULogin, Controls;
Don’t worry about the ULogin implementation. This unit will be created later.
Notice that if the developer tries to compile the project now, Delphi will generate an error. It occurs because the
TfrmLogin1 class has not been created yet.
As this class will implement the Singleton pattern, only an instance of TUserControl class will be created. The
whole application will share the same instance of the TUserControl class.
To implement the Singleton Pattern in the TUserControl class, right-click on the class diagram and choose Create
By Pattern.

Figure 5 – Create by Pattern menu item

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 138 -
The Pattern Wizard dialog (Figure 6) will be shown.

Figure 6 – Pattern Wizard dialog


In the Patterns section, choose GoF | Creational | Singleton. In the Pattern Properties section, click the ellipsis
button in the Class Singleton property and select the TUserControl, as shown in Figure 7.

Figure 7 – Select Class Dialog


Click on the OK button to confirm. Click on OK again to close the Pattern Wizard dialog box.

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 139 -
Figure 8 – Singleton Pattern added
Notice that the GetInstance method and the FInstance field were added to the TUserControl class.
The GetInstance method implementation is presented below:

class function TUserControl.GetInstance: TUserControl;


begin
If FInstance = nil Then
begin
FInstance := UUserControl.TUserControl.Create();
end;
Result := FInstance;
end;

Login form
Now, create the login form.
Add a new form to the project, choosing File | Form – Delphi. Rename the created form as “frmLogin1” and save it
as “ULogin.pas”.
Add two TLabel components, two TEdit components and two TButton components. The frmLogin form should look
like Figure 9:

Figure 9 – frmLogin
Change the OK button’s ModalResult property to mrOK and the Cancel button’s ModalResult property to
mrCancel.
Name the first edit as edtUserName and the second as edtPassWord.
Add the UUserControl unit to ULogin’s uses clause.
Add the following code to frmLogin’s onCloseQuery event:

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 140 -
procedure TfrmLogin.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if ModalResult = mrOK then
begin
if not TUserControl.GetInstance.ValidaUser(edtUserName.Text,
edtPassWord.Text) then
begin
MessageDlg('Invalid user/password!', mtWarning, [mbOK], 0);
CanClose := False;
end;
end;
end;
Check the “Auto-create forms” in the Project Options’ Forms page (Figure 10). Only frmMain should be in the
“Auto-create forms” list.

Figure 10 – Project Options dialog: Forms Page


Now, code the frmLogin form call. Go to the project file (choose Project | View Source).

Figure 11 – Project file


Change the project file as follows:

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 141 -
begin
Application.Initialize;
Application.CreateForm(TfrmMain, frmMain);
if TUserControl.GetInstance.DoUserLogin then
begin
frmMain.sbMain.Panels[0].Text := TUserControl.GetInstance.Name;
frmMain.sbMain.Panels[1].Text := TUserControl.GetInstance.UserName;
Application.Run;
end
else
Application.Terminate;
end.
Run the application.

Figure 12 – frmLogin1 form


Although only the frmMain form is set as an “Auto-create form” in the project options, the frmLogin1 form is
shown when the application is executed. This occurs because the code in the project file creates the frmLogin1 form:

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 142 -

if TUserControl.GetInstance.DoUserLogin then

Figure 13 – Singleton application running


Finally, notice that the same class instance of TUserControl class is used in the whole application.

The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 143 -
Delphi 2009 Application Development for Win32
- 144 -
Basic I nterface Elem ents
This module covers:
• Introduction to Delphi’s Visual Component Library (VCL)
• Overview of Main VCL Parent Classes
• Hierarchy of the VCL Classes
• In-depth Introduction to Creating Applications Using the VCL

Delphi 2009 Application Development for Win32


- 145 -
VCL Architecture
VCL is an acronym for Visual Component Library: a set of visual components for rapid development of Windows
applications in Delphi language. VCL contains a wide variety of visual, non-visual, and utility classes for tasks such
as building Windows applications, web applications, database applications, and console applications.
The following figure shows the relationships between the selected classes (among all classes in the VCL hierarchical
tree):

Figure 1 – VCL Hierarchy


When a component is created, it is added to the component library by deriving a new class from one of the existing
class types in the hierarchy.

TObject
All classes descend from TObject. TObject introduces methods that implement fundamental behaviors such as
construction, destruction, and message handling.
TObject encapsulates the fundamental behavior common to all VCL objects by introducing methods that perform
basic functions such as creating, maintaining, and destroying an instance of an object.

TPersistent
TPersistent is the ancestor for all objects that have assignment and streaming capabilities.
TPersistent encapsulates the behavior that is common to all objects that can be assigned to other objects, read and
write their properties from and to a form file (.xfm or .dfm file). For this purpose, TPersistent introduces methods
that can be overridden to:
• Define the procedure for loading and storing unpublished data to a stream.
• Provide the means to assign values to properties.
• Provide the means to assign the contents of one object to another.
Use TPersistent as a base class when declaring objects that are not components, yet still need to be saved to a stream
or have their properties assigned to other objects.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 146 -
TComponent
TComponent is the common ancestor for all components. TComponent implements the following features:
Components are persistent objects that have the following capabilities:
• IDE integration. Ability to appear on an IDE palette and be manipulated in a form designer.
• Ownership. Ability to manage other components. For example, if component A owns component B, then A
is responsible for destroying B when A is destroyed.
• Streaming and filing. Enhancements of the persistence features inherited from TPersistent.
TComponent does not provide any user interface or display features. These features are provided by two classes that
directly descend from TComponent.
Components that can be visible at runtime are sometimes called "visual components". Other components, which are
never visible at runtime, are sometimes called "non-visual components". However, it is more common to refer to
"visual components" as "controls" and "non-visual components" simply as "components."
Do not create instances of TComponent. Use TComponent as a base class when declaring non-visual components
that can appear on the component palette and be used in the form designer.
Properties and methods of TComponent provide the basic behavior that descendant classes inherit, as well as the
behavior these components can override to customize their behavior.

TControl
TControl is the base class for all components that are visible at runtime.
Controls are visual components, meaning the user can see them and possibly interact with them at runtime. All
controls have properties, methods, and events that describe aspects of their appearance, such as the position of the
control, the cursor or hint associated with the control, methods to paint or move the control, and events that respond
to user actions.
TControl has many protected properties and methods that are used or published by its descendants.

TGraphicControl
TGraphicControl supports simple lightweight controls that do not need the ability to accept keyboard input or
contain other controls. Since lightweight controls do not wrap Windows screen objects, they are faster and use fewer
resources than controls based on TWinControl.
TGraphicControl provides a Canvas property to access the control's drawing surface, and a virtual Paint method
called in response to paint requests received by the parent control.

TWinControl
TWinControl provides common functionality for all controls that act as wrappers for Microsoft Windows screen
objects ("windows").
Controls that wrap underlying windows have the following features:
• The control can incorporate the functionality of an underlying window. For example, if the underlying
screen object is a text editor, the control can incorporate the editor ability to manage and display a text
buffer.
• The control can receive user input focus. The focused control can handle keyboard input events. Some
controls change their appearance when they have the focus. For example, button controls typically indicate
the focus by drawing a rectangle around the caption.
• The control can serve as a container for other controls, which are referred to as child controls. This
relationship is signified by the child's Parent property. Container controls provide important services to their

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 147 -
children, including display services for controls that do not implement their own canvases. Examples of
container controls include forms, panels, and toolbars.
• Controls based on TWinControl can display standard screen objects provided by Microsoft Windows, or
customized screen objects developed by the VCL programmer.
• Descendants of TWinControl include abstract base classes that support most kinds of user interface objects.
The most significant descendant is TCustomControl, which provides code to implement a canvas and handle
paint messages. Other important abstract descendants include TScrollingWinControl, TButtonControl,
TCustomComboBox, TCustomEdit, and TCustomListBox. When defining new control classes, consider
these descendants before deriving directly from TWinControl.
• Every TWinControl object has a Handle property, which provides the window handle for the underlying
Microsoft Windows screen object. Use the Handle property to bypass the VCL API and directly access the
underlying window.

VCL Components
Components are a subset of the component library that descends from the class TComponent. Place components on
a form or data module and manipulate them at design time.
Using the Object Inspector, assign property values without writing code. Most components are either visual or
nonvisual, depending on whether they are visible at runtime. Some components appear on the Component Palette.

Visual Components
Visual components, such as Forms.TForm and Buttons.TSpeedButton, are called controls and descend from
Controls.TControl. Controls are used in GUI applications, and appear to the user at runtime. Controls.TControl
provides properties that specify the visual attributes of controls, such as their height and width.

NonVisual Components
Nonvisual components are used for a variety of tasks. For example, if the developer is writing an application that
connects to a database, he/she can place a TDataSource component on a form to connect a control and a dataset used
by the control. This connection is not visible to the user, so TDataSource is nonvisual.
At design time, nonvisual components are represented by an icon. This allows manipulating their properties and
events just as a visual control would be manipulated.

Other VCL Classes


Classes that are not components (that is, classes that descend from TObject) are also used for a variety of tasks.
Typically, these classes are used for accessing system objects (such as a file or the clipboard) or for transient tasks
(such as storing data in a list). Instances of these classes cannot be created at design time, although they are
sometimes created by the components added in the Form Designer.

Working with Components


Many components are provided in the IDE, on the Tool Palette. The user interface of an application is designed by
arranging the visual components, such as buttons and list boxes on a form.
There is also the option to place nonvisual components, such as data access components, on either a form or a data
module. At first, Delphi’s components seem to be just like any other class. However, there are differences between
components in Delphi and the standard class hierarchies that many programmers work with. Some differences are:
• All Delphi components descend from TComponent.
• The components are changed through their properties. When a component is inherited, it is usually to add
specific code to existing event handling member functions.
• Components can only be allocated on the heap, not on the stack.
• Properties of components contain runtime type information.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 148 -
• Components can be added to the Component Palette in the IDE and manipulated on a form.
• Components often achieve a better degree of encapsulation than usually found in standard classes.

Using Events
Most of the written code is executed directly or indirectly in response to events. An event is a special kind of
property that represents a runtime occurrence, often a user action.
The code that responds directly to an event, called an event handler, is a Delphi procedure. The Events window of
the Object Inspector displays all events defined for a given component.
Double-clicking on an event in the Object Inspector generates a skeleton event handling procedure, which can be
filled in with code to respond to that event. Not all components have events defined for them. Some have a default
event, which is the one that the component most commonly needs to handle. For example, the default event for a
button is OnClick. Double-clicking on a component with a default event in the Form Designer will generate a
skeleton event handling procedure for the default event.
Code can be re-used by writing event handlers that respond to more than one event. For example, many applications
provide speed buttons that are equivalent to drop down menu commands. When a button performs the same action
as a menu command, a single event handler can be written and then assigned to the OnClick event for both the
button and the menu item. This is done by setting the event handler in the Object Inspector for both the events for
which a response is desired.

Setting Component Properties


To set published properties at design time, use the Object Inspector and, in some cases, property editors. To set
properties at runtime, assign their values in the application source code.
When a component on a form is selected at design time, the Object Inspector displays its published properties and,
when appropriate, allows their editing. When more than one component is selected, the Object Inspector displays all
properties, except Name, that are shared by the selected components. If the value for a shared property differs
among the selected components, the Object Inspector displays either the default value or the value from the first
component selected. When a shared property is changed, the change applies to all selected components. Changing
code-related properties in the Object Inspector, such as the name of an event handler, automatically changes the
corresponding source code. In addition, changes to the source code, such as renaming an event handler method in a
form class declaration, are immediately reflected in the Object Inspector.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 149 -
Developing the Application User Interface
When the developer opens the IDE or creates a new project, a blank form is displayed on the screen. The
application’s user interface (UI) is designed by placing and arranging visual components, such as windows, menus,
and dialog boxes, from the Component palette onto the form.
Once a visual component is on the form, its position, size, and other design-time properties can be adjusted, and its
event handlers can be coded. The form takes care of the underlying programming details.
The following sections describe some of the major interface tasks, such as working with forms, creating component
templates, adding dialog boxes, and organizing actions for menus and toolbars.

Setting up Forms
TForm is the key class for creating GUI applications. When the developer opens a default project or creates a new
project, a form to start the UI design appears.

Using the Main Form


The first form created and saved in a project becomes, by default, the project’s main form, which is the first form
created at runtime. As forms are added to projects, the developer might decide to designate a different form as the
application’s main form. Also, specifying a form as the main form is an easy way to test it at runtime; unless the
form creation order is changed, the main form is the first form displayed in the running application.
To change the project main form:
• Choose Project | Options and select the Forms page.
• In the Main Form combo box, select the desired form to be used as the project’s main form and choose OK.
• Now, if the application is executed, the form selected as the main form is displayed.

Figure 2 – Selecting the Application Main Form

Hiding the Main Form


There is the option to prevent the main form from appearing when the application starts by using the global
Application variable.
To hide the main form at startup:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 150 -
• Choose Project | View Source to display the main project file.
• Add the following code after the call to Application.CreateForm, and before the call to Application.Run.

Application.ShowMainForm := False;
Form1.Visible := False; { the name of the main form may differ }

Note: The form’s Visible property can be set to False at design time using the Object Inspector, rather than
setting it at runtime as in the previous example.

Adding Forms
To add a form to the project, select File | New | Form. See all the project’s forms and their associated units listed in
the Project Manager; there is also the option to display a list of the forms alone by choosing View | Forms.

Linking Forms
Adding a form to a project adds a reference to it in the project file, but not to any other units in the project. Before
writing code that references the new form, add a reference to it in the referencing forms’ unit files. This is called
form linking.
A common reason to link forms is to provide access to the components in that form. For example, form linking will
often be used to enable a form that contains data-aware components to connect to the data-access components in a
data module.
To link a form to another form, select the form that needs to refer to another.
Choose File | Use Unit and select the name of the form unit that will be referenced and click on OK.
When linking a form to another, Delphi 2009 will add the linked form’s uses clauses in the linking form’s unit. This
will render the linked form and its components available in the scope of the linking form.

Figure 3 – Adding a reference to another unit – Use Unit

Using Forms
When a form is created from the IDE, Delphi automatically creates the form in memory by including code in the
main entry point of the application function. Usually this is the desired behavior and it doesn’t have to change.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 151 -
As the main window persists throughout the program execution, it’s likely that the default behavior doesn’t have to
be changed for creating the main window form.
However, it’s also possible that the developer doesn’t want all his/her application forms to be in memory during the
entire execution of the program. If he/she does not want all application forms to be in memory at once, the dialogs
can be created dynamically when needed.
Forms can be modal or modeless. Modal forms are forms with which the user must interact before switching to
another form (for example, a dialog box requiring user input). Modeless forms are windows that are displayed until
they are obscured by another window or either closed or minimized by the user.

Controlling when forms reside in memory


By default, Delphi automatically creates the application’s main form in memory by including the following code in
the application’s main entry point:

Application.CreateForm(TForm1, Form1);
This function creates a global variable with the same name as the form. So, every form in an application potentially
has an associated global variable. This variable is a pointer to an instance of the form’s class and is used to reference
the form while the application is running. Any unit that includes the form’s unit in its uses clause can access the
form through this variable.
All forms in the project unit that are created this way appear when the program is invoked, and exist in memory for
the duration of the application.

Figure 4 – Defining which Forms will be Auto-created


There is the option to visually control form Auto-creation as shown in the figure above. To access this option, select
Project | Options | Forms.
The forms in the Auto_create forms section will be automatically created, while those forms that will be created
dynamically will be listed in the Available forms section.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 152 -
Note: Only the forms that are necessary for the duration of the application’s execution should stay in the
Auto-create forms section. Frequently, it is only the application’s Main form.

Displaying an auto-created form


If the developer chooses to create a form at startup but he/she does not want it displayed until sometime later during
program execution, the form’s event handler uses the ShowModal method to display the form that is already loaded
in memory:

procedure TForm1.Button1Click(Sender: TObject);


begin
Form2.ShowModal;
end;
In this case, since the form is already in the memory, there is no need to create another instance or destroy that
instance.

Creating forms dynamically


It is possible that the presence of the application’s forms in the memory at once isn’t a desired situation. To reduce
the amount of memory required at load time, create some forms only when there is the need to use them. For
example, a dialog box needs to be in memory only during the time a user interacts with it.
To use the IDE to create a form for dynamic execution, select File | New | Form from the main menu to create the
new form at design time as usual.
Remove the form from the Auto-create forms list of the Project | Options | Forms page.
If the form is modeless, invoke the form when desired, using the form’s Show method,, or use the ShowModal
method if the form is modal.
An event handler for the main form must create an instance of the result form and then destroy it. A possible way to
invoke the result form is as follows.

Note: The ResultsForm is a modal form; thus, the handler uses the ShowModal method.

procedure TForm1.Button1Click(Sender: TObject);


var ResultsForm: TForm2;
begin
ResultsForm := TForm2.Create(Self);
try
ResultsForm.ShowModal;
finally
ResultsForm.Free;
end;
end;
In the above example, note the use of try...finally. Adding ResultsForm.Free to the finally clause ensures that the
memory for the form is freed even if the form raises an exception.
The event handler in the example deletes the form after it is closed, so the form would have to be recreated if
ResultsForm is needed elsewhere in the application. Had the form been displayed using Show, it would be
impossible to delete the form within the event handler, because Show returns while the form is still open.

Creating and Managing Menus


Menus provide an easy way for users to execute logically grouped commands. The Menu Designer enables the easy
addition of a menu to the form. Add a menu component to the form, open the Menu Designer, and type menu items
directly in the Menu Designer window.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 153 -
The developer can add or delete menu items, or drag and drop them to rearrange them during design time. There is
even no need to run the program to see the results; the design is immediately visible in the form, appearing just as it
will during runtime. The code can also change menus at runtime, to provide more information or options to the user.

Opening the Menu Designer


Design menus for the application using the Menu Designer. Before starting using the Menu Designer, add a
TMainMenu component (which is located on the Standard Category) to the form.

Figure 5 – TMainMenu – Standard Category


A MainMenu component creates a menu that is attached to the form’s title bar. To open the Menu Designer, select a
menu component on the form, and then either double-click on the menu component or (from the Properties page of
the Object Inspector) select the Items property.
The Menu Designer appears with the first (blank) menu item highlighted in the Designer and the Caption property
selected in the Object Inspector.

Figure 6 – Menu Design

Building Menus
Add a menu component to the form, or forms, for every menu that will be included in the application. There is the
option to build each menu structure entirely from scratch, or start from one of the pre-designed menu templates.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 154 -
Naming Menus
As with all components, when a menu component is added to the form, it receives a default name, for example,
MainMenu1. Give the menu a more meaningful name that follows language naming conventions.

Figure 7 – Creating Menu Items

Adding, Inserting, and Deleting menu items


The following procedures describe how to perform the basic tasks involved in building the menu structure. Each
procedure assumes that the Menu Designer window is open.
To add menu items at design time, select the position where the menu item is to be created.
If the Menu Designer has just been opened, the first position on the menu bar is already selected.
Now either start typing the caption, or enter the Name property first, placing the cursor in the Object Inspector and
entering a value. In this case, reselect the Caption property and enter a value. To finish the menu item edition, press
the Enter key.
The next placeholder for a menu item is selected. If the Caption property was entered first, use the arrow keys to
return to the menu item that has just been entered. Delphi fills in the Name property based on the value entered for
the caption.
Continue entering values for the Name and Caption properties for each new item that will be created, or press Esc to
return to the menu bar. Use the arrow keys to move from the menu bar into the menu, and then to move between
items in the list. Press Enter to complete an action. To return to the menu bar, press Esc.
To insert a new, blank menu item, place the cursor on a menu item and press the Insert key.
Menu items are inserted to the left of the selected item on the menu bar, and above the selected item in the menu list.
To delete a menu item or command, place the cursor on the desired menu item and press the Delete key.

Note: It is impossible to delete the default placeholder that appears below the item that was last entered in a
menu list or next to the last item on the menu bar. This placeholder does not appear in the menu at runtime.

Adding separator bars


Separator bars insert a line between menu items and items on a toolbar. Use separator bars to indicate groupings
within the menu list or toolbar, or simply to provide a visual break in a list.
To make the menu item a separator bar, either type a hyphen (-) for the caption or press the hyphen (-) key while the
cursor is positioned where a separator is intended to appear on the menu.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 155 -
Figure 8 – Adding Menu Item separator

Creating submenus
Many application menus contain drop-down lists that appear next to a menu item to provide additional, related
commands. Such lists are indicated by an arrow to the right of the menu item. Delphi supports as many levels of
such submenus as needed to build into the menu.
Organizing the menu structure this way can save vertical screen space. However, for optimal design purposes, it is
better to use no more than two or three menu levels in the interface design.

Figure 9 – Creating a Submenu


• Select the menu item under which a submenu is to be created.
• Right-click and choose Create Submenu.
• Type a name for the submenu item, or drag an existing menu item into this placeholder.
• Press Enter to create the next placeholder.
• Repeat the steps above to create each item in the submenu.
• Press Esc to return to the previous menu level.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 156 -
Figure 10 – Submenus definition

Adding images to menu items


Images can help users navigate in menus by matching glyphs and images to the menu item action, in a way that is
similar to toolbar images. Add single bitmaps to menu items, or, optionally, organize images for the application into
an image list and add them to a menu from the image list. If several bitmaps of the same size are being used in the
application, it’s useful to put them into an image list.
To add a single image to a menu or menu item, set its Bitmap property to reference the name of the bitmap that will
be used on the menu or menu item.
To add an image to a menu item using an image list:
• Drop a TMainMenu object on the form.
• Drop a TImageList object on the form.
• Open the ImageList editor by double-clicking on the TImageList object.
• Click on Add to select the bitmap or bitmap group that will be used in the menu (located in Borland’s shared
data directory – e.g. C:\Program Files\Common Files\CodeGear Shared\Images\GlyFX\Icons\BMP\16x16\.
and select the New, Open and Save images. After all these images are selected, click on the OK button.
• Set the TMainMenu object’s Images property to the ImageList just created.
• Create the menu items and submenu items as described previously.
Select the menu item of which an image is desired in the Object Inspector and set its ImageIndex property to the
number of the corresponding image in the ImageList (the default value for ImageIndex is -1, which doesn’t display
an image).

Figure 11 – Sample Menu with images

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 157 -
Developing Dialog Boxes
The dialog box components on the Dialogs Category of the Tool Palette make various dialog boxes available to the
applications. These dialog boxes provide applications with a familiar, consistent interface that enables the user to
perform common file operations such as opening, saving, and printing files. Dialog boxes display and/or obtain data.
Dialog boxes are opened when their specific Execute method is called. Execute returns a Boolean value: Execute
returns True when the user clicks on OK in the dialog box, thus saving all changes; Execute returns False if the user
decides to close the dialog by pressing Cancel, thus discarding any changes that might have been performed.

Using open dialog boxes


One of the commonly used dialog box components is TOpenDialog. This component is usually invoked by a New or
Open menu item under the File option on the main menu bar of a form.
The TOpenDialog component makes an Open dialog box available to the application. The purpose of this dialog box
is to let a user specify a file to open. Use the Execute method to display the dialog box.
When the user chooses OK in the dialog box, the user’s file is stored in the TOpenDialog FileName property, which
can be processed as desired.
The following example uses the TOpenDialog component:
• Add a TMainMenu component to the main form
• Add the TOpenDialog component of the Dialog page to the form
• Add the Menu Item with the caption: “New”
• Double-click on the menu item to invoke the event, and then add the following code to open the dialog:

procedure TForm1.New1Click(Sender: TObject);


var fileName: String;
begin
if OpenDialog1.Execute then
fileName := OpenDialog1.FileName;
end;

Actions for Toolbars and Menus


Several features simplify the work of creating, customizing, and maintaining menus and toolbars. These features
allow organizing lists of actions that users of the application can initiate by pressing a button on a toolbar, choosing
a command on a menu, or pointing and clicking on an icon.
Often a set of actions is used in more than one user interface element. For example, the Cut, Copy, and Paste
commands often appear on both an Edit menu and on a toolbar. All that is needed is adding the action once to use it
in multiple UI elements in the application.

What is an action?
As the application is developed, the developer is able to create a set of actions that can be used on various UI
elements. He/she can organize them into categories that can be dropped onto a menu as a whole set (Cut, Copy,
Paste, …) or one at a time (Tools | Customize…).
An action corresponds to one or more elements of the user interface, such as menu commands or toolbar buttons.
Actions serve two functions:
They represent properties common to the user interface elements, such as whether a control is enabled or checked
They respond when a control fires an event, for example, when the application user clicks a button or chooses a
menu item.
A repertoire of actions can be created, all of which are available to the application through menus, buttons, toolbars,
context menus, and so forth.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 158 -
Actions are associated with other components such as a MainMenu or a Button, among others. To relate an action to
a Menu Item, it’s necessary to associate the following properties:

Figure 12 – Linking actions with the MainMenu

Action List
Action lists maintain a list of actions that the application can take in response to something a user does. By using
action objects, the functions performed by the application from the user interface are centralized. This allows
sharing common code for performing actions (when a toolbar button and a menu item do the same thing), as well as
to provide a single, centralized way to enable and disable actions depending on the state of the application.
The TActionList component belongs to the Tool Palette Standard category, as shown in the following figure:

Figure 13 – TActionList – Standard category

Setting up action lists


Setting up action lists is fairly easy once the basic steps involved are understood:
• Drop a TActionList object onto the form. (ActionList is on the Standard Category)
• Double-click on the TActionList object to display the Action List editor.
• Using the Action List Editor, either define an action or choose among the predefined actions.
• Use one of the predefined actions listed in the editor. Open the list as shown in the figure below, and then
choose the New Standard Action option. The predefined actions are organized into categories (such as
Dataset, Edit, Help, and Window) in the Standard Action Classes dialog box. Select all the standard actions
that will be added to the action list and click on OK.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 159 -
Figure 14 – Editing ActionList

Figure 15 – Editing ActionList – Standard Action Classes


Create a new action: choose New Action. Use a new action and not a predefined action.
Set the properties of each action in the Object Inspector. (The properties set affect every client of the action).
The Name property identifies the action, and the other properties and events (Caption, Checked, Enabled,
HelpContext, Hint, ImageIndex, ShortCut, Visible, and Execute) correspond to the properties and events of its client
controls. The properties of a client that correspond to the ones in an action are typically, but not mandatorily,
assigned the same name. For example, an action’s Enabled property corresponds to a TToolButton’s Enabled
property. However, an action’s Checked property corresponds to a TToolButton’s Down property.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 160 -
Figure 16 – Editing ActionList – Add Action
If the predefined actions are used, the action includes a standard response that occurs automatically. If the action is
created from scratch, write an event handler that defines how the action responds when fired.
Write the following event:

procedure TForm1.actNewExecute(Sender: TObject);


var fileName: String;
begin
if OpenDialog1.Execute then
fileName := OpenDialog1.FileName;
end;
Attach the actions that are in the action list to the clients that require them. Click on the control (the button or menu
item) on the form. In the Object Inspector, the Action property lists the available actions. Select the desired one.
Standard actions, such as TEditDelete or TDataSetPost, perform the action expected. If actions are being created
from scratch, understanding what happens when the action is fired is crucial.

Action Manager
The Action Manager component allows specifying layout information for custom user-defined actions and standard
actions that are contained in an application; as well as a set of controls that can "render" this information as UI
elements.
It allows adding any of the standard actions to the application, and drag and drop any action that is in the application
onto an action band - where it will automatically render as an UI element.
It contains all the layout information that is necessary for the automatic creation of menus, toolbars, and navigation
bars (Outlook bars).
The layout information includes: Order of appearance, reference to an action (including the action's Caption,
ShortCut, Visible, Background Color etc.).
The TActionManager component belongs to the Tool Palette Additional category.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 161 -
Figure 17 – TActionManager – Additional category
Either use the standard actions provided or create new actions from scratch. ActionManager provides the Action
bands, and these act as containers that hold and render sets of actions. Drag and drop items from the Action Manager
editor onto the action band at design time. At runtime, application users can also customize the application’s menus
or toolbars using a dialog box similar to the Action Manager editor.

Creating toolbars and menus


Use the Action Manager to automatically generate toolbars and main menus based on the actions contained in the
application. The Action Manager manages standard actions and any written custom actions. Then, create UI
elements based on these actions and use action bands to render the actions’ items as either menu items or as buttons
on a toolbar.
The general procedure for creating menus, toolbars, and other action bands involves these steps:
• Drop an Action Manager component (TActionManager) onto the form where the toolbar will be created.
• If images are to be placed on the menu or toolbar, drop an ImageList component from the Component
palette Win32 page onto a form. (Add the desired images to the ImageList, or use the one that is provided.)
• Connect the ImageList to the Action Manager: with focus on the Action Manager and in the Object
Inspector, select the name of the ImageList from the Images property.

Figure 18 – Linking the ImageList to the TActionManager


• Add an ActionToolBar, using the TActionManager component.
• Double-click on the Action Manager to display the Action Manager editor.
• Select the Toolbar tab and click on New to add a ToolBar component to the form.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 162 -
Figure 19 – ActionManagerEditor – Adding an ActionToolBar
Add Actions to the Action Manager editor’s action pane: Double-click on the Action Manager to display the Action
Manager editor. Click on the drop-down arrow next to the New Action button and select New Action or New
Standard Action. A tree view is then displayed. Add one or more actions or categories of actions to the Action
Manager’s actions pane. The Action Manager adds the actions to its action lists.
Drag and drop single actions or categories of actions from the Action Manager editor onto the menu or toolbar being
designed. To add user-defined actions, create a new TAction by pressing the New Action button and writing an
event handler that defines how it will respond when fired. Once the actions have been defined, drag and drop them
onto menus or toolbars as would be done with standard actions.

Figure 20 – ActionManagerEditor – Adding a New Action

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 163 -
Coolbars, PageScrollers, Toolbars, and StatusBars
A toolbar is a panel that holds buttons and other controls, and is usually found across the top of a form (under the
menu bar).
A cool bar is a kind of toolbar that displays controls on movable, resizable bands. If there are multiple panels
aligned to the top of the form, they stack vertically in the order they were added.
Put controls of any sort on a toolbar. In addition to buttons, there is the option to use color grids, scroll bars, labels,
etc.
There are several ways to add a toolbar to a form:
• Place a panel (TPanel) on the form and add controls (typically speed buttons) to it.
• Use a toolbar component (TToolBar) instead of TPanel, and add controls to it. TToolBar manages buttons
and other controls, arranging them in rows and automatically adjusting their sizes and positions. If tool
button (TToolButton) controls are used on the toolbar, TToolBar makes it easy to group the buttons
functionally. It also provides other display options.
• Use a cool bar (TCoolBar) component and add controls to it. The cool bar displays controls on
independently movable and resizable bands.
To show the functionalities of these components, follow the steps below.
Create a new project and name it Components.
The form in this project will be named frmComponents and will be saved as uComponents. In this example, the
CoolBar, PageScroller, ToolBar, ImageList, and StatusBar components will be used. They are all found under the
Tool Palette Win32 category.
Add the CoolBar component to the form and name it clbrMain.
Add the PageScroller component to the CoolBar and name it pgscrlrToolbar. When the PageScroller component is
added to the CoolBar, a ‘band’ is created. Actually, the CoolBar component is made up of CoolBand components
(each listed in the Bands property of the CoolBar). An editor that can be used to manipulate the properties of these
CoolBands can be invoked by right-clicking on the CoolBar and choosing Bands Editor… from the speedmenu. The
following editor will be displayed.

Figure 21 – CoolBand Editor with Object Inspector


When a control is added to the CoolBar, a new CoolBand will be created and the CoolBand’s Control property will
be set to that control (as show in the figure above). However, this is not the only way to create new CoolBands. New
CoolBands can be created or deleted from the editor by clicking on the corresponding button.
The PageScroller component also has a Control property. Add a ToolBar component (named tlbrMain) to the
PageScroller, which in turn sets its property. The Control property for both the PageScroller and the CoolBar is used
to designate which control will be displayed in that object.
The ToolBar just created will need to have three buttons and a separator placed between its first and second buttons.
To create these items, right-click on the toolbar and choose the New Button and New Separator menu options.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 164 -
The first ToolButton will be named tlbtnDepressed; the second ToolButton will be named tlbtnLeft, and the third
ToolButton will be named tlbtnRight.
In this example, the second and third buttons will function as toggle buttons. To do this, set the second and third
buttons’ Grouped property to True, and their Style property to tbsCheck. This will make them operate in a mutually
exclusive manner. Toggle the Down property of the 2nd button to True. This will cause the button to be initially
pressed when the form is run.
ToolButtons do not have an image associated with them. In order to create images for the buttons, an ImageList
component will have to be used. Place the ImageList from the Win32 page on the form. It will be named
imglstToolbar. Now, select images by invoking the ImageList Editor. Double-click on the ImageList control and the
following editor will be displayed:

Figure 22 – ImageList Editor


From this editor, add the images that will be displayed on the toolbuttons. Next, tell each of the toolbuttons which
image in the ImageList to display. First, the toolbar’s Images property must be set to imglstToolbar. Each toolbutton
has an ImageIndex property that can be set to its corresponding image. The ImageList is a zero-based array of
images, meaning that if the first toolbutton is intended to display the first image in the ImageList, it will have an
ImageIndex of 0.
Remember that, in order to create a CoolBand in the CoolBar, the control that will be displayed in the CoolBand
must be added to the CoolBar. This will automatically create a new band.
Now, create three more CoolBands. They will be used in later sections of this module.
To create two of the CoolBands, add two Edit boxes from the Standard page to the CoolBar.
The first edit box will be named edtFont and the second will be named edtColor.
To place a label in front of each edit box, invoke the CoolBand Editor and select the CoolBand. In the Object
Inspector, set the Text property for the corresponding band to Font Name: and Color:.
To create the final band, select the Add New button in the CoolBand Editor and then set its Text property to Date:.
The CoolBand Editor will resemble the following:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 165 -
Figure 23 – Completed CoolBand Editor
To complete this example, add a StatusBar to the form. It is found on the Win32 tab and automatically aligns itself
to the bottom of the form. Let’s name it stsbrStatus.
Add two panels through the Panels property, by selecting its property editor.
Increase the width of the first panel to 100 to allow for a larger message, and then input “Left Button Down” for the
text of the second panel. Previously, the Down property for the tlbtnLeft button was set to True.
Now the first panel has to display a message when the user holds down the tlbtnDepressed speed button. This is
accomplished with the use of the OnMouseDown and OnMouseUp events. Open the event handler by double-
clicking on the OnMouseDown event. In the event, enter the following:

stsbrStatus.Panels[0].Text := 'I’'m Depressed!';


The first panel is referenced with the index number of zero. Note the double apostrophes in the message. To include
an apostrophe within text, use an extra one; otherwise Delphi will attempt to terminate the string, thus generating a
number of compile errors. Use the OnMouseUp event to change the message back to a zero length string.

stsbrStatus.Panels[0].Text := '';
Within the OnClick event of the tlbtnRight toolbutton, change the message in the second panel, letting the user
know that the right button is down. Repeat this step for the tlbtnLeft toolbutton. Run the form and watch the
message change. Move the various CoolBands in the CoolBar and the PageScroller that was placed on the first
CoolBand . See below how the form should look at this point:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 166 -
Figure 24 – Form being Executed

PageControls
Adding Pages
As seen in the previous module, within an application, most often there are more related information than the form
can support. Delphi alleviates this problem with its TabSheet and PageControl components.
To verify the functionalities of these components, using the same form, drag a PageControl component, name it
pgcntrlPages, and change its Align property to alClient.
With the cursor over the page control, right-click on the control to get to the speed menu. Select New Page and
change its Caption to Regions and its Name to tbshtRegions.
By adding a new page, a new TabSheet component is created within the current PageControl.
Next, place a RadioGroup component (Standard control tab) on this page and name it rdgrpRegions. Notice that the
Items property value is a TStrings object. Enter the names of four regions through the property editor.
The items in this list are not static as in some other products. By manipulating TStrings through code, items can be
added to and removed from the list at runtime. Transfer this advantage to end-users by giving them the ability to add
items rather than having everything predefined.
A simple Edit component will provide the input location needed. Place a button on the page along with an edit
component below the RadioGroup. Name the edit component as edtAddRegion and the button as btnAddRegion.
If the Text property is blanked out on the edit component, its display will also be blank. A button click is necessary
to append the text from the edit component to the RadioGroup list. In the OnClick event of the button, add the
following event handler code to the button:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 167 -
rdgrpRegions.Items.Add(edtAddRegion.Text);

Figure 25 – Using PageControl – Page Regions


If the developer double-clicks on a component, Delphi will create an event handler for the event it assumes as the
most commonly used.

Dialog Components
Most Windows applications purchased off the shelf provide configuration tools. Windows allows users to change
their wallpaper and screen saver, as well as customize their screen colors. This type of flexibility can be easily
incorporated into Delphi applications. With the two dialog components used here, users can control over the font
and color aspects of their program.

Changing Fonts and Colors


Continue building upon the same form by adding another page to it, and naming it tbshtDialogs. Set its Caption to
“Dialogs.”
Place two buttons and a label on this page. Name the first button btnColor, and set its Caption to “Change Color”.
Name the second button btnFont and set its Caption to “Change Font”. Finally, name the label lblTestSubject and set
its Caption to “How Do I Look?”.
On the form, place the font and color dialogs, and then name them fntdlgFonts and clrdlgColors, respectively. These
are both invisible components; thus, when the form is instantiated and displayed, neither will show.
The objective of this page is to launch a font or color dialog, allowing users to modify the appearance of the label
text when they click on the appropriate button. The result of what they choose in each dialog will also be displayed
in the corresponding edit boxes on the CoolBar. This can all be accomplished with just a few lines of code placed in
the OnClick event handler of each of the buttons:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 168 -
procedure TfrmComponentSampler.btnFontClick(Sender: TObject);
begin
if fntdlgFonts.Execute then
begin
lblTestSubject.Font := fntdlgFonts.Font;
edtFont.Text := fntdlgFonts.Font.Name;
end;
end;

procedure TfrmComponentSampler.btnColorClick(Sender: TObject);


begin
if clrdlgColors.Execute then
begin
lblTestSubject.Color := clrdlgColors.Color;
edtColor.Color := clrdlgColors.Color;
end;
end;

Figure 26 – PageControl – Dialogs Page

The Animate Component


User awareness is crucial in most of today’s applications. When the user initiates a command that turns control over
to the application, it is important to let the user know that the application is processing the request. One of the best
examples of user awareness is the hourglass cursor. Many applications display this hourglass cursor when the
computer is processing the user's request. More and more applications are now going a step further by adding
animation to enhance user awareness. Delphi provides an easy avenue for adding animation within the application.
The Animate component allows the use of Windows AVI animation, which can dramatically heighten user interface
and awareness.
As the first step, create a new TabSheet, named tbshtAnimate, in the PageControl and set its Caption as Animate.
1. Add a Panel, name it pnlAnimate, and set its Align property to alTop.
2. Drop an Animate component (located in the Win32 tab) on the Panel, and set the Animate component’s Name
to anmtSystemAVIs.
3. Drop two Buttons below the Panel and name them btnStart and btnStop.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 169 -
4. Drop a RadioGroup on the Form, name it rdgrpAnimations, and enter items that correspond to the standard
animations:

Figure 27 – PageControl – Animate Page


In the Start button, check to see if the Animate component has a valid AVI animation value by checking its
CommonAVI property. If the component’s CommonAVI property is set to something other than aviNone, activate
the component by setting its Active property to True. The code below disables the Animations radiogroup box and
the Start button.

procedure TfrmComponentSampler.btnStartClick(Sender: TObject);


begin
if anmtSystemAVIs.CommonAVI <> aviNone then
begin
anmtSystemAVIs.Active := True;
btnStart.Enabled := False;
rdgrpAnimations.Enabled := False;
end;
end;
Now, set the Active property for anmtSystemAVIs to False and enable the Start button and Animations radiogroup
box when the user selects the Stop button.

procedure TfrmComponentSampler.btnStopClick(Sender: TObject);


begin
btnStart.Enabled := True;
rdgrpAnimations.Enabled := True;
anmtSystemAVIs.Active := False;
end;
The user can select the type of animation from the Animations radiogroup box. Insert the following code into the
rdgrpAnimations OnClick event handler:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 170 -
procedure TfrmComponentSampler.rdgrpAnimationsClick(Sender: TObject);
begin
case rdgrpAnimations.ItemIndex of
0 : anmtSystemAVIs.CommonAVI := aviCopyFile;
1 : anmtSystemAVIs.CommonAVI := aviCopyFiles;
2 : anmtSystemAVIs.CommonAVI := aviDeleteFile;
3 : anmtSystemAVIs.CommonAVI := aviEmptyRecycle;
4 : anmtSystemAVIs.CommonAVI := aviFindComputer;
5 : anmtSystemAVIs.CommonAVI := aviFindFile;
6 : anmtSystemAVIs.CommonAVI := aviFindFolder;
else
anmtSystemAVIs.CommonAVI := aviNone;
end; //case
end;

Figure 28 – PageControl – Animate Page Running

Using the MonthCalendar Component


The final example will show the use of the MonthCalendar component. Place one of these on a fourth page and align
it to alClient, as shown:

Figure 29 – PageControl – Calendar Page


Name the MonthCalendar component mnthclndrDate. This calendar component has several properties that can be set
to make the calendar look and act according to specific needs. For example, it increases or decreases the number of
months displayed, based on how many will fit in when it is resized.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 171 -
Next, the selected date will be displayed in the Date CoolBand. Place the following code in the OnClick event
handler of the MonthCalendar:

procedure TfrmComponentSampler.mnthclndrDateClick(Sender: TObject);


begin
clbrMain.Bands[3].text := 'Date: ' +
FormatDateTime('mmmm dd, yyyy', mnthclndrDate.Date);
end;
However, if the program is executed now, the Date band does not display the selected date. Since the user did not
select a date from the MonthCalendar, the OnClick event of the MonthCalendar has not been invoked.
The MonthCalendar defaults to the current date when it is created, so that date can be displayed until the user has
made a selection. To do this, point the Form’s OnShow event handler to the MonthCalendar’s OnClick event.
Now, when the user enters the Calendar tab, the Date band displays the current date until the user makes a selection.
The following figure shows the updated form:

Figure 30 – PageControl – Calendar Page Running

New VCL Components


The new version of Delphi contains a new set of visual components to help programmers to enhance the GUI
(Graphical User Interface), providing a better interaction with the user. This section will cover a detailed explanation
about these components.
TButtonedEdit: TButtonedEdit is an edit box that provides buttons and texts to be embedded within it. It also
allows that these buttons can display a popup menu like a dropdown list. For example, is possible to input some
word to be searched and then click on the icon to search the word among the available options inside the popup
menu, like shown on the figure 35

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 172 -
Figure 31 – TButtonedEdit Example
TCategoryPanelGroup: This feature allows panels to be grouped within the application. All these panels can be
collapsed and fully customized with different controls.

Figure 32 – Category Panel


TProgressBar: The TProgressBar, though not a new feature, was greatly improved to support Windows Vista style,
using a similar interface as seen on Windows Vista, like pausing, error diagnostic, and the marquee style. The
following figure shows the new version of TProgressBar.

Figure 33 – Progress Bar with default color

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 173 -
Figure 34 – Progress Bar with Marquee style
Ribbon Controls: These new set of features included on Delphi 2009 allows programmers to build rich and
powerful user interfaces using the same components used in the Office 2007 interface. Is possible to create
applications with similar interfaces as seen on Office 2007, like the tab navigation that was simplified with ribbon
controls.

Figure 35 – File tab showing the Ribbon features


To a add a new tab in the ribbon control, right click inside the Ribbon Control and select Add Tab option

Figure 36 – Procedure to add a new tab


After it a new tab will be created, and to add a group to the specified tab, select the desired tab, right click in it, and
choose Add Group Option.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 174 -
Figure 37 – Procedure to add a new group
TBallonHint: The BallonHint property displays a custom hint for any component that has the property configured.
On the figure 42, a hint is being displayed to explain the functionality of the button.

Figure 38 – BalloonHint Example


TPNGImage: Delphi 2009 now provides support for PNG image types.
TTreeView: TtreeView can provide different glyphs for a node depending on this node is expanded or collapsed.
TButton Enhancements: Images can be placed in the buttons, and can be positioned and aligned as the developer
desires. Split buttons and Command link also are supported on Delphi 2009, increasing the compatibility with
Windows Vista.

Figure 39 – TButton Enhancements


TEdit: TEdit can provide a hint about what should be filled in a specific textbox when there is not a text within it or
when this control is not the focus. On the example showed on figure 44, inside the Username textbox, there is a hint
indicating that a personal username should be entered.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 175 -
Figure 40 – TEdit with Username hint
When the focus changes to Username control, a hint about the type of character that must be entered in the password
control will appear as shown on figure 41.

Figure 41 – TEdit with password hint

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 176 -
Basic Skills Example
The objective of this example is to use all the concepts discussed in this chapter, in an attempt to bring them all
together to reinforce what was learned.
Application requirements:
• The application will have a MDIMainForm named frmMain
• From this form, other two MDIChild will be loaded.
The first form will be used to perform registration and will have a simple search section. There will be buttons to
Include, Search, and Close.
The second form will be only used for search and will be implemented to make use of the Singleton concept. There
will be a search button, and it will show the data in a TListBox.
A form holding common behavior for inheritance should be created. This form will have only code to close the form
with the ESC button, making use of the visual inheritance concept discussed earlier.
To hold user data, a Record structure, RecUser, will be created. It looks like this:

Description Type

Name String(50)

ID_User Integer

UserName String(15)

PassWord String(8)
To work with these data, a class containing the application’s business logic will be created. This class will be
operated between the data entry Form and the Record, completing the MVC - Model View Controller.
A register type array will be created for the addition of more than one user record at a time.

First Step: Create and Save the Project


Create the project: File | New | VCL Forms Application.
Save the project: File | Save All. (save the main form unit as “uMain” and the project as “Sample_Basic_Skills”).

Figure 42 – Saving the project


Modify some of the main form properties:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 177 -
• Caption: to “Basic Skills”.
• Name: to “frmMain”.
• FormStyle: to fsMDIForm; indicating that it will be the application’s Main form.
• WindowState: to wsMaximized.
Save the changes.
The Main form, which will be in the auto-create list, was defined as shown in the figure below using Project
Options.

Figure 43 – Project Options Forms Page

Second Step: Create the base


After creating the project and defining the configurations, it’s time to create the form that will work as the base form
for all other forms that are added to the project.
Add a new Form to the project: File | New | Form - Delphi .

Figure 44 – Adding a New Form to the Project


Save the new Form as “uStartAbstract”.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 178 -
Figure 45 – Save the Project
Modify the following Form properties:
• Name: “frmStartAbstract”.
• FormStyle: fsMDIChild.
Add a TImageList, name it “ImageList”, and add three images into it.

Figure 46 – Adding images to the TimageList


Add a ToolBar to the form and modify the following properties:
• Name: tbBottom
• Align: AlBotton
• Images: ImageList
• ShowCaptions: True
Add three buttons to the ToolBar, as follows (right-click on the ToolBar and select “New Button” in order to add):
• The first button will be used to Include a new record (Name=tbAdd, Caption=Add, ImageIndex=1).
• Add a New Separator.
• The second button will be used to search records (Name=tbSearch, Caption=Search, ImageIndex=2).
• Add another Separator.
The third button will be used to Close the form (Name=tbClose, Caption=Close, ImageIndex=3).

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 179 -
Figure 47 – Adding a button to the ToolBar
The form that will be used as the base for the next forms that are added to the project will have the following
appearance:

Figure 48 – Base form for inheritance


The next step will be to define events such as “Close the Form”, which will be common to all forms that use it as the
ancestor.
Place the following code in the onClick Event of the Close button:

procedure TfrmStartAbstract.tbCloseClick(Sender: TObject);


begin
Close;
end;
The onKeyPress event will verify the pressed keyboard key. If the pressed key is ESC, the form will be closed:

procedure TfrmStartAbstract.FormKeyPress(Sender: TObject; var Key: Char);


begin
if Key = #27 then
Close;
end;
Another event that uses the ancestor form is onClose, which will destroy the form as soon as it is closed. To do so,
use the following code:

procedure TfrmStartAbstract.FormClose(Sender: TObject;


var Action: TCloseAction);
begin
Action := caFree;
end;
Attributing caFree to the Action property value, the form is closed and all memory allocated for the form is freed.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 180 -
Third Step: Add Form using inheritance
In the previous step, a form was built to be the base for the other forms. Now the two application forms will be
created using the visual inheritance concepts.
It is very simple to create a form that descends from another in the application: Select File | New | Other | Delphi
Projects | Inheritable items

Figure 49 – Repository of Inheritable Items


Let’s use the frmStartAbstract form (created previously). Select this form and click on OK. An identical form will
be created.
Modify the new form properties:
• Name: frmAddUser
• Caption: User
Save the Form as “uAddUser”.
As two forms were added to the project, revisit Project Options to set their auto-create status. As defined at the
beginning of this example project, the only form that will be auto-created is the main one, as seen in the figure
below:

Figure 50 – Selecting Auto-create Forms

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 181 -
In the user registration form, the following fields are needed:

Description (class) Name Caption/Text

TEdit edtIDUser

TEdit edtName

TEdit edtUserName

TEdit edtPassWord

TLabel lblIDUser User ID

TLabel lblName Name

TLabel lblUserName UserName

TLabel lblPassWord PassWord

TListBox lbUser
Change the edtUserName CharCase property to ecUpperCase, and the edtPassWord CharCase property to
ecLowerCase. Also change the edtPassWord PassWordChar property to “*”.
The user registration form will look like this:

Figure 51 – Add User Form


The second form to be added to the project will be used to search all users that have registered as recorded in the
Record structure.
Add a form using the “frmStartAbstract” base form as its ancestor, save the form as “uSearchUser” and modify its
properties as follows:
• Name: frmSearchUser
• Caption: Search User
Add a TListBox component to the form, name it “lbSearchUser” and modify its Align property to alClient. The
query form will look like this:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 182 -
Figure 52 – Form to query users

Fourth Step: A Record for users data


So far, you have worked with project creation, used form visual inheritance and basic elements to create interfaces
such as buttons, edits, labels, etc. Now, define the unit that will implement the Record with the users data.
Start adding a unit to the project (Select File | New | Unit – Delphi)

Figure 53 – Adding a unit to the project


Save the unit as “uRcUser”.
To create the Record structure, use the Together integration (Model View) to facilitate the work. Select the Model
View page, as shown in the figure below:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 183 -
Figure 54 – Enabling the Model View
Confirm the modeling support addition.

Figure 55 – Modeling Support


Access the modeling area using the visual way, according to the figure below. Open the Class Diagram, double-
clicking on uRecUser.

Figure 56 – Class Diagram


Define the “RecUser” Record User structure. Right-click on the diagram and select Add | Structure.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 184 -
Figure 57 – Class Diagram – Structure (Record)
Define the record fields by right-clicking on the Record and selecting Add | Field. The first field will be named
"User_ID"; it’s of integer type.

Figure 58 – Class Diagram – Fields


Do the same to add the following fields:

Description Type

Name String

Username String

PassWord String
After it’s done, the user record will look like this:

Figure 59 – Data Structure – RecUser


Another stage of the application has been concluded. The record structure has just been created. Since a database
cannot be accessed, use the data structure (Record) for this function. The definition code of the RecUser Record is:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 185 -
type
RecUser = record
public var
User_ID:integer;
Name:String;
UserName:String;
PassWord:String;
end;

Fifth Step: Business Code


The objective of this stage is to show how to work with a class that will deal with the user rules. This kind of
development uses the MVC (Model View Controller) structure, as well as the class that will be a part of the
Controls.
When working in MVC structure, the Model represents the data access. If a Data Module were being used, it would
be a part of the Model. In the application being created now, Record RecUser will be carrying out this role.
The View is represented by forms; in this case, as you are working with users, by the registration and query forms.
The Controller is responsible for the user’s business rules. Now the class that will represent this level of the model
will be created. In this class, the rules by which a registration is added and the way users are consulted will be
implemented, as well as the way forms are created and destroyed.
First:
• Add a new unit to the project
• Save the unit as “uUserControl”
• Open the Model View and select the class diagram of the added class.
• Add a class to the diagram. Right-click on the button and select Add | Class
• Change the class name to “TUserControl”
• In this class, add an array of RecUser type, as follows:
• Right-click on the class and select Add | Field.

Figure 60 – Adding Fields


Go to the code by right-clicking on the class and selecting Go to Definition. Notice that RecUser is not defined
because it is in another unit; therefore, the unit has to be added to uses. A simpler way to do this is by using
Refactoring. To do so, select the RecUser and right-click on the unit. After that, select Refactoring | Find Unit.
A dialog will be displayed, as seen in the figure below. It shows the unit that was found, which holds the class for
the RecUser Record. Once it’s done, select the unit and press OK.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 186 -
Figure 61 – Find Unit Dialog
The class definition will appear as below:

unit UUserControl;

interface

uses
URecUser;
type
TUserControl = class

public

var
UserList: Array of RecUser;
end;

implementation
end.
Add a method to the class to execute the addition of the user into the record array. Right-click on the class, select
Add | Procedure. Change its name to AddUser and add the necessary parameters:

pID: Integer; pName, pUserName, pPassWord: String


As seen, the parameters carry the values of the record fields that will be passed from the form to the class through
the AddUser method. At the same time, this method will handle the user record addition.
Click on the method and select Go To Definition to implement the AddUser method, according to the following
code:

procedure TUserControl.AddUser(pID: Integer; pName, pUserName, pPassWord:


String);
begin
SetLength(UserList, Length(UserList)+ 1);

UserList[Length(UserList) -1].User_ID := pID;


UserList[Length(UserList) -1].Name := pName;
UserList[Length(UserList) -1].UserName := pUserName;
UserList[Length(UserList) -1].PassWord := pPassWord;
end;
Working with a dynamic array, the number of records that will be inserted is unknown, and perhaps the size of the
array may have to be modified for each inclusion. The SetLength method allows the adjustment of the array size. It

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 187 -
requires the array name and size that should be attributed to it. The other lines using an index (Length(UserList)-1),
represents the current registration, attributing the values to the record fields.
Now, implement the method to query the records. To do so, carry out the same steps as the previous section to create
the “SearchUser” method that will return a TStringList to the form. The difference between the AddUser method
and the SearchUser method is that the former does not return any value (Procedure) while the latter does. It will
therefore need to be a function.

Figure 62 – Adding the SearchUser function


Go to the method definition. Notice that TStringList shows an error, because there is no unit reference in the uses
clause. Add the Classes unit to the uses clause.
SearchUser method definition:

function TUserControl.SearchUser: TStringList;


var List: RecUser;
begin
Result := TStringList.Create;
for List in UserList do
begin
Result.Add('User ID : ' + IntToStr(List.User_ID));
Result.Add('Name : ' + List.Name);
Result.Add('UserName : ' + List.UserName);
Result.Add('PassWord : ' + List.PassWord);
Result.Add('--------------------------------------------------');
end;
end;
As TUserControl is responsible for all business rules in this example, it is also responsible for creating the register
and search forms, and consequently for destroying them.
Add the uAddUser and uSearchUser units to the unit’s uses clause.
Add a new method called CreateFormAdd to create the user register form. This method will be called when the form
is opened to include users.
Add a method called CreateFormSearch, which will be called when the query form is opened.
The code for the CreateFormAdd method:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 188 -
procedure TUserControl.CreateFormAdd;
begin
if frmAddUser = nil then
frmAddUser := TfrmAddUser.Create(nil);
frmAddUser.Show;
end;
The code for the CreateFormSearch method:

procedure TUserControl.CreateFormSearch;
begin
if frmSearchUser = nil then
frmSearchUser := TfrmSearchUser.Create(nil);
frmSearchUser.Show;
end;
Now, add a method called DestroyFormAdd to destroy the user register form when the form is closed.
Add a method called DestroyFormSearch to destroy the user query form when this form is closed.
The code for the DestroyFormAdd method:

procedure TUserControl.DestroyFormAdd;
begin
frmAddUser := nil;
end;
The code for the DestroyFormSearch method:

procedure TUserControl.DestroyFormSearch;
begin
frmSearchUser := nil;
end;

Sixth Step: Singleton Pattern


As mentioned before, the Singleton Pattern creates a single class instance in the application. To have this example
working properly, use this design pattern so that any created form accesses the same object instance; otherwise there
is a danger that an object instance would be created in each of the forms, thus losing track of the registeration data.
The TUserControl class needs the Singleton Pattern applied to it, as it stores the user data. After this, the data will be
kept in a single class instance.
To use the Singleton Pattern, do the following:
Right-click on the Class diagram and select Create by Pattern. The Pattern Wizard Dialog will open:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 189 -
Figure 63 – Adding a Pattern

Figure 64 – Pattern Wizard – Singleton


Select the Patterns GoF | Creational | Singleton section. The Class Singleton property will open in the Pattern
Properties section. Select the class where Singleton will be implemented.
Click on the ellipsis button to show a dialog to find the class. Select the TUserControl class as shown in the figure
below, and click on OK. Choose the class property and confirm.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 190 -
Figure 65 – Singleton Pattern – Select Class
Now you have finished attributing the Singleton Pattern to the TUserControl class, where the pattern definitions
were added.

Figure 66 – Class Diagram – Singleton Pattern


See that the TUserControl class function GetInstance was added, and the other methods will be used through the
reference it returns to the singleton object. When this method is called, the first thing it verifies is whether the class
instance already exists. If the instance exists, it uses the same method; otherwise, it creates the instance.
The code for this method:

class function TUserControl.GetInstance: TUserControl;


begin
If FInstance = nil Then
begin
FInstance := UUserControl.TUserControl.Create();
end;
Result := FInstance;
end;
The project is now in the final phase. So far, you have used project options configuration and object-oriented
programming concepts, as well as visual inheritance for the forms and related code. A layer has been created to
contain the application business rules, and used some basic components for interface creation.
Now, define the main form, which will be where the user launches their activities. Define also how the class that
implements the Singleton Pattern will be used.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 191 -
Seventh Step
The main form will be responsible for loading the user registration and query forms. To do so, add to the form some
components that will help build the user interface:
Add a TMainMenu component to the form and change its Name property to MainMenu. Define the main menu as
follows:

Figure 67 – MainMenu Editor Dialog


Add a TImageList component named ImageList to the form. Add four images into it, one for each of the menu
items.

Figure 68 – ImageList Editor


Add a TActionManager component to the form, and name it ActionManager. Inside this component, actions
responsible for calling the registration and query forms will be added. The Images property will be set as
“ImageList”.
Go to the TActionManager component editor, select the Toolbars tab and add an ActionToolbar called actbMain.

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 192 -
Figure 69 – Editing TActionManager – ActionToolBar
Select the Actions tab and add the first action, which will load the user registration form. The name of this action
will be “actbAdd”. Set its Caption as “Add” and its Category as “User”. In the ImageIndex, add the adjusted image.
The action definition will look like the Figure below:

Figure 70 – Editing TActionManager – Add Action


With the actAdd action selected, click on the Object Inspector’s Event tab. Double-click on the onExecute event and
add the following code. This code will load the register form. Add the uUserControl unit to the form uses clause.

procedure TfrmMain.actAddExecute(Sender: TObject);


begin
TUserControl.GetInstance.CreateFormAdd;
end;
Notice that the CreateFormAdd method has been called directly from the UserControl class, through the GetInstance
method.

class function GetInstance: TUserControl;


Add an Action with the Name of “actSearch”. Set its Caption as “Search”, its Category as “User”, and add the
adjusted image to the ImageIndex. Add the following code to its onExecute event:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 193 -
procedure TfrmMain.actSearchExecute(Sender: TObject);
begin
TUserControl.GetInstance.CreateFormSearch;
end;
The last Action will be used to close the application. Add a new Action and set its Name property as “actClose”, its
Caption as “Close”, its Category as “Close”, and add the adjusted image into its ImageIndex. Add the following
code to its onExecute event:

procedure TfrmMain.actCloseExecute(Sender: TObject);


begin
Application.Terminate;
end;
Now that the main form actions were defined, it is necessary to bind the actions to the menu items. To do so, double-
click on MainMenu to open the editor. Select the Add item, and in the Action property, select the “actAdd” action,
as shown below:

Figure 71 – Editing MainMenu


Repeat this procedure with the two other actions, linking them to their respective menu items.
Select the MainMenu component and change its Images property to “ImageList”.
To finish the work in the main form, double-click on the ActionManager component, select the User category and
drag it to the ToolBar, as shown in the figure below. After this, the shortcuts that are used to execute the actions will
be created. This procedure can be done to any category or any action.

Figure 72 – Editing TActionManager – ShortCut

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 194 -
Final Step: User Login and Search Feature
To finish the application, add the user registration and search procedures to the forms.
Open the user registration form frmAddUser and perform the following changes:
• Add the uUserControl unit to the form uses clause.
• Double-click on the Add button and add the following code:

procedure TfrmAddUser.tbAddClick(Sender: TObject);


begin
inherited;
try
TUserControl.GetInstance.AddUser(StrToInt(edtID.text),
edtName.Text,
edtUserName.Text,
edtPassWord.Text);
ShowMessage('User Added successfully!!!!');
except on E: Exception do
ShowMessage('User Addition failed ' + E.Message);
end;
end;
Now, implement an event for the Search button, which will execute the search procedure and load the StringList
data into the TListBox component. To do so, double-click on the Search button and add the following code to its
event:

procedure TfrmAddUser.tbSearchClick(Sender: TObject);


begin
inherited;
ListBox.Items.Text := TUserControl.GetInstance.SearchUser.Text;
end;
To finish the user registration form, select the form and insert the following code into its onClose event:

procedure TfrmAddUser.FormClose(Sender: TObject; var Action: TCloseAction);


begin
inherited;
Action := caFree;
TUserControl.GetInstance.DestroyFormAdd;
end;
Based on the code above, the Action variable receives the caFree attribute in order to destroy the form instance, and
the DestroyFormAdd method attributes nil to the form.
Open the frmSearchUser form. As the form will be only used for search, select the Add button and change the
Enable and Visible properties to False. Add the uUserControl unit to the form uses clause. In the onClick event of
the Search button, insert the following code:

procedure TfrmSearchUser.tbSearchClick(Sender: TObject);


begin
inherited;
lbSearchUser.Items.Text := TUserControl.GetInstance.SearchUser.Text;
end;
Finally, insert the following code into the onClose event of the frmSearchUser form:

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 195 -
procedure TfrmSearchUser.FormClose(Sender: TObject; var Action: TCloseAction);
begin
inherited;
TUserControl.GetInstance.DestroyFormSearch;
end;
The application is now complete. Save the application and Run (F9) it, testing the items that were added.

Figure 73 – The Application running

Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 196 -
Visual Form Designer and Code
Editor
This module introduces Delphi’s visual form designer and code editor.

Delphi 2009 Application Development for Win32


- 197 -
Introduction
After the correct modeling of the classes involved in application development, the user interface will be created -
used by the application’s user to interact with the objects created.
Considering its importance, the interface should take into account the usability, the modeling of the classes,
disposition and interaction with the information, etc.
The user interface can either help or hinder the usability of the application, which is why it is important to
understand the most common components used for the development of user interfaces.
In this module, it will be learned how to use the Form Designer and Code Editor in application development.

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 198 -
Visual Form Designer
One of Delphi’s most useful and powerful features is the Form Designer. Forms are a key part of any application
with a user interface and that is why the Form Designer is the primary focus of application development in Delphi.
In spite of its name, the Visual Form Designer is not a separate tool. When working with a form, accessing the
‘Design’ tab takes the developer to the Form Designer. This is where the application’s interface is created.
The Form Designer consists of the form itself, the Tool Palette, and the Object Inspector, along with other tools that
are accessible from menus. Therefore, creating the application’s interface is a matter of using all the services offered
by these tools.

Figure 1 - Form Designer

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 199 -
Creating Interfaces
To create an interface, select components in the Tool Palette and then arrange them together in a form.
For instance, to put a button on the form, just select a TButton component in the Tool Palette by clicking on it, and
then click on the form, right in the place where the button will be set.
If the component is not appropriately positioned in the form, drag it and drop it wherever it should be placed.

Figure 2 - Adding a button.


After placing the component on the form, use the Object Inspector to configure the component’s properties.
Even though properties are all relevant – say Caption, which changes the text of a button – one of the properties is of
particular importance: the Name property.
When a component is referenced in code, it is done through its Name property. Thus, naming the component should
be carefully done. Using a naming convention is very useful in doing so.
A convenient naming convention is something akin to the Hungarian Notation. The Hungarian Notation places a few
mnemonic letters at the beginning of a variable (or component) name to help identify characteristics of the variable.
Its rules are:
• Create the mnemonic using the variable or component type, removing the vowels and duplicated consonants
• After the mnemonic, use a descriptive name indicating the usage of the component
• If the mnemonic conflicts with another type, start adding the vowels back
Notice that the selected component has some ‘blue dots’. They are called ‘resizing handles’. As the name implies,
they are used to resize the component to the desired size.

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 200 -
Figure 3 - Using the Object Inspector to change properties.

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 201 -
Code Editor
The ‘Code’ tab grants access to the Code Editor. This is where the code is written for the application.
Notice there are a few things already declared. In the case of a form, Delphi declares the form’s class itself. For
every component placed on the form, Delphi also adds a reference to them.
The colored bar (green and yellow) shows the line where the code was modified. If the form or unit is saved, the bar
becomes all green.
The Code Editor numbers the lines automatically in increments of 10. However, it also shows the number of the line
where the cursor is.
The minus (-) symbol is a useful feature in the Code Editor – known as ‘code folding’, it allows collapsing sections
of code, creating a hierarchical view of the code and making it easier to read and navigate. The collapsed code is not
deleted, but hidden. To collapse the section, just click on the sign, which will turn to a plus (+) sign.

Figure 4 - Using the Code Editor to modify the user’s interface

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 202 -
Two-Way Tool
Notice that when the component name is changed, Delphi automatically changes the reference name in the code.
This reflects one particularly useful feature of the Code Editor and the Form Designer – it is a Two-Way Tool.
It means that changes made in the Form Designer are reflected in the code, and changes made to the code are
reflected in the Designer as well.

Figure 5 - The Object Inspector and the Code Editor reflect changes performed in either of them

Visual Form Designer and Code Editor. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 203 -
Delphi 2009 Application Development for Win32
- 204 -
An Object’s Lifetim e
This module discusses how Delphi creates and destroys objects.

Delphi 2009 Application Development for Win32


- 205 -
Overview
Delphi applications always have many objects – the form, both visual and non-visual components, and so forth. In
addition, if you create your own classes, there are even more objects that should be considered.
As said before, in order to use an object, its instance must be created first. It means that memory must be allocated
for the object by making a call to the object’s constructor.
Notice that in some of the forms created, there is no code for creating the component’s instance. That’s because the
form is responsible for calling the constructor of each of the components. Moreover, the form is the owner of its
components. Thus, while creating the interface, there is no need to call any constructor for the components that
reside on the form.
On the other hand, every object instance must be destroyed (i.e., deallocate the memory used by the object). Again,
there’s no code for that.
The function of the owner is to destroy every instance of the objects it owns.

An Object’s Lifetime. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 206 -
Component’s Owner
As mentioned before, every component in a form is automatically created by the form where it resides.
When a form is created in design time, every component that is put in the form has its Owner property assigned to
that form.
The TComponent class introduces the Owner property. Whenever it is desired to manually create the component,
define its owner through the constructor’s AOwner parameter.
So, if a component will be created manually, use a code like this:
Button1 := TButton.Create(Self);
In this code, use the ‘Self’ reference as the owner. Any component can be the owner of another component, but
normally one of the following is used :
• Self
• Application
• Nil

Self
Self is a reference for the class where the code is declared. Different from what one might think, Self usually refers
to the form, not the object itself. Components whose owner is the form will remain in the memory as long as the
form exists.

Application
The Application object is usually the owner of the forms. It represents the application and is not visible by default.
All components whose owner is the Application object will be destroyed automatically when the application is
terminated.

Nil
Using Nil as the owner means that there is no owner at all. The object is there, but no one is responsible for
destroying the object.

An Object’s Lifetime. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 207 -
Terminating Components
Again, as mentioned before, when a component is put in the form at design time, the component’s owner is the
form. The owner is responsible for destroying all components it owns, including components created at runtime and
assigned to the form.
Does it mean that one shouldn’t care about destroying components? For a better answer, see below when owners are
destroyed.
Components whose owner is the form are destroyed automatically when the form is destroyed. But if the form’s
owner is the Application object, the form will be destroyed only when the application ends. Although not visible, a
form might be instantiated and only destroyed when the application is finished.
To destroy a form instance, use the Free method – the Free method calls the object’s destructor. Never call the
destructor manually. The problem is that if the form isn’t created manually, another call to the form gives an error.
So, when is it safe to destroy a form? The short answer would be “when it isn’t necessary anymore”.
The form has an OnClose event that is used to dispose of the form itself. Creating an event handler for the OnClose
event, the result is a skeleton like this:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin

end;
The Action var parameter is of type TCloseAction and the value that is passed to it is used to determine what should
happen when the form is closed. In our case, the value caFree will do the job. It determines that the form will be
destroyed when it is closed. More information about TCloseAction can be found on the on-line help.
So, the general rule is: if a form is not used constantly, create it and destroy it manually. Essentially, only the main
form should be created automatically (auto created form).

An Object’s Lifetime. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 208 -
Ex ception Handling
This module covers:
• What is an Exception?
• What is Exception Handling?
• Protected Blocks
• Handling Exceptions
• Resource Protection
• Raising and Re-Raising Exceptions

Delphi 2009 Application Development for Win32


- 209 -
Introduction
A code snippet like the following has been seen in the previous module:

procedure TAbstractDataTabForm.butSearchClick(Sender: TObject);


begin
inherited;
Screen.Cursor := crHourGlass;
datControl.DataSet.DisableControls;
try
if (cmbField.ItemIndex >= 0) and (ediValue.Text <> '')
then
datControl.DataSet.Locate(
aFieldNames[cmbField.ItemIndex], ediValue.Text,
[loCaseInsensitive, loPartialKey]);
finally
datControl.DataSet.EnableControls;
Screen.Cursor := crDefault;
end;
end;
This code shows a powerful feature of the Delphi language – structured exception handling.
Exceptions are exceptional conditions that require special handling. They include errors that occur at runtime, such
as divide by zero, and the exhaustion of free store. Exception handling provides a standard way for dealing with
errors - discovering both anticipated and unanticipated problems - while also enabling developers to recognize, track
down, and fix bugs.
In previous versions of Pascal, runtime error trapping was messy. For many operating system-based errors, the
programmer had to rely on compiler switches and status variables to detect and manage problems. Often the
programmer would instruct the compiler to “turn off” error reports for certain sections of code, and then check the
value of a global status variable to see what the problem was. Here is an example of opening a file using traditional
methods as a way to trap the runtime error:

{ $I- } { This compiler switch shuts off I/O checking }


Assign(InFile, InputName);
Reset(InFile);
{ $I+ } { This compiler switch restores I/O checking }
if IOResult <> 0 then
{ error processing code here };
As seen, this process leads to code that is hard to read and maintain. Indeed, this technique would often create so
much error-checking overhead in the code that the reason for the code was hard to discover. As it will be seen, the
structured exception handling features of Delphi solve these problems.
When an error occurs, the program raises an exception, meaning it creates an exception object and rolls back the
stack to the first point it finds where there is code to handle the exception. The exception object usually contains
information about what has occurred. This allows another part of the program to diagnose the cause of the
exception.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 210 -
What is an Exception?
Almost everything in a Delphi program is an object. Exceptions are not different. An Exception is a descendant class
of TObject and encapsulates the fundamental properties and methods for all exceptions.
Exceptions are raised when a runtime error occurs in an application, such as attempting to divide by zero. When an
exception is raised, an exception instance typically displays a dialog box describing the error condition. If an
application does not handle the exception condition, then the default exception handler is called.
To make applications robust, the code needs to recognize exceptions when they occur and respond to them. It is the
developer’s job to recognize places where errors might happen and define responses, particularly in areas where
errors could cause the loss of data or system resources.
When a response to an exception is created, it is done on blocks of code. When there is a series of statements and all
of them require the same kind of response to errors, group them into a block and define error responses that apply to
the whole block.
Blocks with specific responses to exceptions are called protected blocks, because they can guard against errors that
might otherwise either terminate the application or damage data.
Remember that an error is an Exception, but not all Exceptions are errors.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 211 -
Protected Blocks
To prepare for exceptions, place statements that might raise them in a try block. If one of these statements does raise
an exception, control is transferred to an exception handler that handles that type of exception and then leaves the
block. The exception handler is said to catch the exception and specify the actions to take. By using try blocks and
exception handlers, the developer is able to move error checking and error handling out of the main flow of
algorithms, resulting in simpler, more readable code.
Start a protected block using the keyword try. The exception handler must immediately follow the try block. It is
introduced by the keyword except, and signals the end of the try block. This syntax is illustrated in the following
code. If the SetFieldValue method fails and raises an EIntegerRange exception, execution jumps to the exception-
handling part, which displays an error message. Execution resumes outside the block.

try
SetFieldValue(dataField, userValue);
except
on E: EIntegerRange do
ShowMessage(
Format('Expected value between %d and %d, but got %d',
E.Min, E.Max, E.Value));
end;
. { execution resumes here, outside the protected block }
.
.
There must be an exception handling block or a finally block immediately after the try block. An exception handling
block should include a handler for each exception that the statements in the try block can generate.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 212 -
Handling Exceptions
The exception handling block appears immediately after the try block. This block includes one or more exception
handlers. An exception handler provides a specific response to a specific kind of exception. Handling an exception
clears the error condition and destroys the exception object, which allows the application to continue execution.
Exception handlers are defined to allow applications to recover from errors and continue running. Types of
exceptions that might be handled include attempts to open files that don’t exist, writing to full disks, or calculations
that exceed legal bounds. Some of these, such as “File not found”, are easy to correct and retry, while others, such as
running out of memory, can be more difficult for the application or the user to correct.
The application executes the statements in an exception handler only if an exception occurs during the execution of
the statements that are in the preceding try block. When a statement in the try block raises an exception, execution
immediately jumps to the exception handler, where it steps through the specified exception-handling statements,
until it finds a handler that applies to the current exception.
Once the application locates an exception handler that is able to handle the exception, it executes the statement and
then automatically destroys the exception object. Execution continues at the end of the current block.

Writing Exception Handlers


The exception handling block starts with the except keyword and ends with the end keyword. These two keywords
are actually part of the same statement as the try block. That is, both the try block and the exception handling block
are considered part of a single try...except statement.
Inside the exception handling block, include one or more exception handlers. An exception handler is a statement
like the following:

on <type of exception> do <statement>;


For example, the following exception handling block includes multiple exception handlers for different exceptions
that can arise from an arithmetic computation:

try
{ calculation statements }
except
on EZeroDivide do Value := MAXINT;
on EIntOverflow do Value := 0;
on EIntUnderflow do Value := 0;
end;
Most of the time, as in the previous example, the exception handler doesn’t need any information about an exception
other than its type, so the statements following on.do are specific only to the type of exception. In some cases,
however, it is possible that some of the information contained in the exception instance be needed.
To read specific information about an exception instance in an exception handler, use a special variation of on...do
that grants access to the exception instance. The special form requires that a temporary variable be provided to hold
the instance. For example:

on E: EIntegerRange do
ShowMessage(
Format('Expected value between %d and %d', E.Min, E.Max));
The temporary variable (E, in this example) is of the type specified after the colon (EIntegerRange, in this example).
Use the as operator to typecast the exception into a more specific type if needed.
A single default exception handler can be provided to handle any exceptions for which specific handlers haven’t
been provided. To do that, add an else part to the exception-handling block:

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 213 -
try
{ statements }
except
on ESomething do
{ specific exception-handling code };
else
{ default exception-handling code };
end;
Adding default exception handling to a block guarantees that the block handles every exception in some way,
thereby overriding all handling from any containing block.
Remember: Never destroy the temporary exception object. Handling an exception automatically destroys the
exception object. If the developer destroys the object, the application attempts to destroy the object again, generating
an access violation.

Handling Classes of Exceptions


Exceptions are always represented by classes. As such, you usually work with a hierarchy of exception classes. For
example, VCL defines the ERangeError exception as a descendant of EIntError.
When an exception handler is provided for a base exception class, it catches not only direct instances of that class,
but instances of any of its descendants as well. For example, the following exception handler handles all integer
math exceptions, including ERangeError, EDivByZero, and EIntOverflow:

try
{ statements that perform integer math operations }
except
on EIntError do { special handling for integer math errors};
end;
Combine error handlers for the base class with specific handlers for more specific (derived) exceptions. Do this by
placing the on...do statements in the order they will be searched when an exception is thrown. For example, this
block provides special handling for range errors, and another kind of handling for all other integer math errors:

try
{ statements performing integer math }
except
on ERangeError do { out-of-range handling };
on EIntError do { handling for other integer math errors };
end;
Note that if the handler for EIntError comes before the handler for ERangeError, execution would never reach the
specific handler for ERangeError.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 214 -
Resource Protection
An exception handler is code that handles a specific exception (or exceptions) that occurs within a protected block
of code. However, there are times when there is no need to handle the exception, yet you do have code that you want
to execute after the protected block, even if an exception occurs. Typically, such code handles cleanup issues, such
as freeing resources that were allocated before the protected block.
By using finally blocks, it is ensured that if the application allocates resources, it also releases them, even if an
exception occurs. Thus, if the application allocates memory, you can make sure it eventually releases the memory
too. If it opens a file, you can make sure it closes the file later. Under normal circumstances, you can ensure that an
application frees allocated resources by including code for both allocating and freeing. When exceptions occur,
however, you need to ensure that the application still executes the resource-freeing code.
Some common resources that should always be released are:
• Files
• Memory
• Windows resources
• Objects (instances of classes in the application)

Writing Finally Blocks


The following event handler illustrates how an exception can prevent an application from freeing the memory that it
allocates:

procedure TForm1.Button1Click(Sender: TObject);


var
APointer: Pointer;
AnInteger, ADividend: Integer;
begin
ADividend := 0;
GetMem(APointer, 1024);{ allocate 1K of memory }
AnInteger := 10 div ADividend;{ this generates an exception}
FreeMem(APointer, 1024);{ this never gets called because of the exception}
end;
Although most errors are not that obvious, the example illustrates an important point: when an exception occurs,
execution jumps out of the block, so the statement that frees the memory never gets called.
To ensure that the memory is freed, use a try block with a finally block.
Finally blocks are introduced by the keyword finally. They are part of a try...finally statement, which has the
following form:

try
{ statements that may raise an exception}
finally
{ statements that are called even if there is an exception in the try block}
end;
In a try...finally statement, the application always executes any statements in the finally part, even if an exception
occurs in the try block. When any code in the try block (or any routine called by code in the try block) raises an
exception, execution halts at that point. Once an exception handler is found, execution jumps to the finally part,
which is called to clean up the code. After the finally part executes, the exception handler is called. If no exception
occurs, the cleanup code is executed in the normal order, after all the statements in the try block.
The following code is a modified version of the previous example. It uses a finally block so that when it allocates
memory and generates an error, it still frees the allocated memory:

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 215 -
procedure TForm1.Button1Click(Sender: TObject);
var
APointer: Pointer;
AnInteger, ADividend: Integer;
begin
ADividend := 0;
GetMem(APointer, 1024);{ allocate 1K of memory }
try
AnInteger := 10 div ADividend;{ this generates an
exception }
finally
FreeMem(APointer, 1024);{ execution resumes here,
despite the exception }
end;
end;
The statements in the finally block do not depend on the occurrence of an exception. If no statement in the try part
raises an exception, execution continues through the finally block.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 216 -
Raising and Re-Raising Exceptions
To indicate a disruptive error condition, you can raise an exception by constructing an instance of an exception
object that describes the error condition and then calling the reserved word raise.
Raise has two different uses. First, raise can be used to re-raise an exception that has already been handled. It can
also be used to manually raise an exception, simulating an error condition.

Re-Raising Exceptions
Sometimes when an exception is handled locally, it is desired that the handling be augmented in the enclosing block,
rather than replaced. Of course, when the local handler finishes its handling, it destroys the exception instance, and
therefore the enclosing block’s handler never gets to act. You can, however, prevent the handler from destroying the
exception, giving the enclosing handler a chance to respond. Do this by using the raise command with no arguments.
This is called re-raising the exception. The following example illustrates this technique:

try
{ statements }
try
{ special statements }
except
on ESomething do
begin
{ handling for only the special statements }
raise;{ reraise the exception }
end;
end;
except
on ESomething do ...;{ handling you want in all cases }
end;
If code in the statements part raises an ESomething exception, only the handler in the outer exception-handling
block executes. However, if the code in the special statements part raises ESomething, the handling in the inner
exception-handling block is then executed, followed by the more general handling in the outer exception-handling
block. By re-raising exceptions, you can easily provide special handling for exceptions that occur in special
occasions without losing (or duplicating) the existing handlers.
If the handler wants to throw a different exception, it can use the raise statement in the normal way, as described in
Raising an Exception.

Raising an Exception
To raise an exception, call the reserved word raise, followed by an instance of an exception object. This establishes
the exception as coming from a particular address. When an exception handler actually handles the exception, it
finishes by destroying the exception instance, so you never need to do that yourself.
For example, given the following declaration,

type
EPasswordInvalid = class(Exception);
…you can raise a “password invalid” exception at any time by calling raise with an instance of EPasswordInvalid,
like this:

if Password <> CorrectPassword then


raise EPasswordInvalid.Create('Incorrect password entered');
In this case, EPasswordInvalid is a user-defined Exception. Remember that since exceptions are classes, you can
easily derive a class from the Exception class to create a new one.

Exception Handling. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 217 -
Delphi 2009 Application Development for Win32
- 218 -
Delphi’s Debugger
This module covers:
• Debugging Roadmap
• Debugger Environment
• Analyzing Data at Runtime
• Controlling the Debugging Flow

Delphi 2009 Application Development for Win32


- 219 -
Introduction
To better see how the execution flows in an exception handling block, use the step-by-step feature of Delphi’s
debugger.
Actually, in the process of developing a computer program, the program is compiled and tested even before it is sent
to a real testing framework. There are cases in which the compiler refuses to compile a specific line of code. This is
typically a syntax error.
A syntax error occurs when code is written in a manner that is not allowed by the rules of the language. The Delphi
compiler is smart enough to catch those errors and show the line where it is, along with information about the line. A
more difficult kind of error is the semantic error.
A semantic error occurs when the syntax of the code is correct, but the semantics or meaning are not what was
intended. Once the construction follows the language rules, semantic errors are not caught by the compiler. This
leads the program to either do what wasn’t expected or to an abnormal termination.
Finding the source of the problem can be a time consuming task. In this case, a good debugger tool comes in handy.

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 220 -
Debugging Roadmap
It is then time to find that annoying runtime error. Although there’s not an easy way to locate the error, you can
outline a generic approach:
1. Embed debug code in the program
2. Control the program execution
3. Analyze the information

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 221 -
Embedding Debug Code in the Program
First of all, you must embed debug code in the program. Do this by choosing Project | Options… from the menu. It
opens the Project Options dialog box. In this dialog box, choose the Compiler category on the left tree view and you
will find the Debugger groups in the right pane.
You can find further detailed descriptions of each of these options in the on-line help.
For most cases, the default options will suffice. Recompile the project and you’ll get the symbols embedded in the
.dcu files.

Figure 1 – the Debugging options pane in the Project Options Window

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 222 -
Controlling Program Execution
Once the debug information symbols are embedded in the application, all that has to be done is start debugging, isn’t
that right? Well, partially. Make sure the IDE has integrated the debugger, selecting the Tools | Options… menu. In
the Options dialog box, select Debugger Options from the left tree and check the Integrated debugging check box.
From now on, when you either press F9 or select Run | Run on the menu or click on the Run button, the application
will run along with the debugger.
What’s left, then? Should the application be scanned through from its very start until the problem shows up?
Well, no. A debugger tool shouldn’t be used as a chainsaw. Instead, use it as a scalpel. Like a surgeon, you have a
good notion of where the problem might be. When that’s the case, start by defining a breakpoint.

Figure 2 – the Debugging options pane in the Tool | Options Window

Defining Breakpoints
A breakpoint halts the program execution at a certain location or when a particular condition occurs. It means that
the application can be executed at full speed, and the developer can be confident that, when it reaches the
breakpoint, it will stop and the IDE will show the execution point.
As it was noticed, after a compilation, the gutter on the left of the Code Editor shows some “blue balls”. Those “blue
balls” mean “this is a breakpointable line”. Simply click on it to define a breakpoint in that line. Notice that the blue
ball then turns to a breakpoint icon (a red ball).
Find more information about breakpoints in the on-line help.

Debug Layout
By default, when the application is executed with F9 (or Run | Run menu or using the Run button), the IDE layout
changes from Default to Debug.
When the execution reaches the breakpoint, it will automatically stop; the Code Editor then shows the execution
point.

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 223 -
Now, execute the application step by step by choosing F8 (Run | Step over) or F7 (Run | Trace into).
Both commands tell the debugger to execute the next line of code. However, if the next line of code contains a
function call, Trace Into executes the function and stops at the first line of code inside the function. Step Over
executes the function and stops at the first line after the function.
While the execution is stopped, several debug windows can be used to analyze the application.

Figure 3 – The IDE Debug Window

Debugger Environment
IDE has many tools and windows associated with the debugger. Some of those windows are already shown in the
Debug Layout, but there is much more to see.
As mentioned earlier, breakpoints halt the program execution at a certain location or when a certain condition
occurs. A possible way to create a breakpoint, a Source Breakpoint, has been presented, but there was no chance to
define a condition. Besides, there’s more than just the source breakpoint. It’s time to see what breakpoints can do.

Breakpoint List
Regardless of their kind, all breakpoints are shown in the Breakpoint List.
This list shows the breakpoints properties – unit’s file name or address, line or length, condition, action, pass count,
and group.
View the Breakpoint List through the View | Debug Windows | Breakpoints menu item.
The Breakpoint List allows editing a breakpoint property even during a debug session. Simply right-click on the
breakpoint and then either select Properties or use the Properties tool button.

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 224 -
Figure 4 – The Breakpoint List Window

Watch List
During program debugging, there is often the need to know a variable value or an object property. In order to help
with that, the Delphi IDE allows adding watches to the code.
Using a watch, you can track the value of a variable or expression as you step through your code. Every time the
program execution is interrupted, the debugger evaluates all the items listed in the Watch List window and updates
their displayed values.

Figure 5 – The Watch List Window

Local Variables
Due to some optimizations in code, variables declared in a procedure or function are not displayed in the Watch List.
To read data from local variables, use the Local Variable window. The Local Variable window shows all local
variables, even in a non-current frame.

Figure 6 – The Local Variables List Window

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 225 -
Call Stack
The call stack is the order in which functions and procedures have been called. The routines are listed in this
window, with the routine that was first called being at the bottom of the list, and the most recently called one at the
top. To view the call stack, select the View | Debug Windows | Call Stack menu option.
Using the context menu of the call stack, you can quickly find the associated function or procedure source code.

Figure 7 – The Call Stack Window

Threads
The Threads window is used to view the status of all threads that are currently being executed in an application. To
view the Threads window, select View | Debug Windows | Threads.
In this window, you will see all the loaded processes and their associated identifiers (the OS-assigned thread ID), the
State of the thread (Runnable, Stopped, Blocked, or None), and the Location of the source. Processes also have a
state (spawned, attached, or cross-process attached). This state indicates how the process was created. A thread's
Status will be one of the following four options:
• Breakpoint - Stopped due to a breakpoint.
• Faulted - Stopped due to a processor exception.
• Unknown - Not the current thread and therefore its status is unknown.
• Stepped - The last step command was successfully completed.
There is also the option to use the right-click speed menu to display the source location of the thread in the Code
Editor, set the selected thread to active, or terminate the process.

Figure 8 – The Thread Status Window

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 226 -
Modules
From Delphi 4 to Delphi 2005, the Modules View did not change much. It shows a list of the modules that are
loaded by each of the executables being debugged. It is separated in three panes: The Module pane (upper left), the
Source pane (lower left), and the Entry Point pane (right). Each pane has its own speed menu with options that
pertain to that specific pane.
The lower left pane of the Modules window is the Source pane. This pane is used to display the source code for the
selected module in the Code Editor. There are two speed menu options that can be used to accomplish this: (1) Edit
Source and (2) View Source. The Edit Source menu item displays the source code in the Code Editor. The View
Source menu item displays the source code in the Code Editor as well. However, it does not change focus to the
Code Editor while the Edit Source option does.
The last pane is the Entry point pane. Right-click on this pane and choose Go to Entry Point to display the module’s
entry point in the CPU window. The CPU window will be discussed in the following section.
Another difference of this new IDE is that the Modules View is now embedded in the editor view. This change was
made in order to give the Modules View a proper home in the new fully-docked IDE. The multi-pane layout of the
Modules view really doesn't lend itself very well to being a separate IDE view anymore.

Figure 9 – Modules View Window

CPU
Similarly to the Modules window, the CPU window is made up of several panes. These panes display a specific low-
level view of the application while it is running.
The first pane is the Disassembly pane (upper left). It displays the original program source code above the associated
assembly instructions that have been disassembled from the application’s machine code.

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 227 -
The Memory Dump pane (lower left) displays a memory dump (in hexadecimal bytes) of any memory that is
accessible to the currently loaded executable module.
The next pane, Registers (upper middle), displays the current values of the CPU registers.
The Flags pane (upper right) displays the current values of the CPU flags.
Finally, the Machine Stack pane (lower right) displays the current contents of the program stack. By default, the
stack is displayed as hexadecimal longs (32-bit values). Right-click on the various CPU window panes to access the
menu commands that pertain to that pane.

Figure 10 – the CPU monitor Window

FPU
FPU is another debug window (View Debug Windows | FPU). Similar to the CPU window, it is used to display and
change the floating-point and MMX registers within the CPU.

Figure 11 – the FPU monitor Window

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 228 -
Event Log
The Event Log window displays process control messages, breakpoint messages, and Windows messages, among
others.
There may be situations in which errors do not occur while the debugger is used to run the application, but only at
normal speed. In those cases, using the Event Log and the OutputDebugString Windows API is extremely useful.

Figure 12 – the Event log Window

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 229 -
Analyzing Data at Runtime
Only being able to stop the application at a certain point is not enough. There should be a way to examine some data
during the execution, in order to better understand what’s going on.
For such cases, Delphi offers a plethora of information while a program is being debugged, as you could see.

Evaluate/Modify
The Run | Evaluate/Modify… menu item brings the Evaluate/Modify dialog box.
The Evaluate/Modify functionality allows evaluating an expression for which a breakpoint has been set. You can
also pass expression values to the currently running process. For instance, you can modify a value for a variable and
insert that value into the variable, which is something you might want to do if that value is to be passed to another
method at some point during the execution of the application. This allows you to provide in-process values, which is
something you might otherwise not be able to do.

Figure 13 – The Evaluate/Modify dialog box

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 230 -
Controlling the Debug Flow
Only being able to stop the program execution to analyze the application data is not enough. There is often the need
to execute the application step by step to find the odd behavior.
In this case, use the options in the Run menu to control the program execution after it hits a breakpoint.
Most of the debugging time will be spent with a combination of these commands, making it likely that you step over
procedures and functions you do not suspect, and trace into the problematic ones.
More detailed info on these commands can be found in the on-line help.

Figure 14 – Debug flow control menu options

Delphi’s Debugger. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 231 -
Delphi 2009 Application Development for Win32
- 232 -
Advanced Database Developm ent
This module covers:
• Introduction to the components of a Data Module
• Configuring Data Module Components
• Creating a User Interface Using Data Aware Components
• Introduction to Available Technologies for Connecting to a Database

Delphi 2009 Application Development for Win32


- 233 -
Database Applications: Overview
Database applications let users interact with information that is stored in databases. Databases provide structure for
the information, thus allowing it to be shared among different applications.
Delphi provides support for relational database applications. Relational databases organize information into tables,
which contain rows (records) and columns (fields). These tables can be manipulated by simple operations known as
relational calculi.
When designing a database application, the developer must understand how the data is structured. Based on that
structure, a user interface can be designed to display data to the user, allowing them to either enter new information
or modify the existing data.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 234 -
Delphi Database Architecture
Database applications are built from user interface elements, from components that represent database information
(datasets), and from components that connect these to each other and to the source of the database information. The
way these pieces are organized is what the architecture of the database application really is.
While there are many distinct ways to organize the components in a database application, most of them follow the
general scheme illustrated in Figure 1:

Figure 1 – Database Architecture in Delphi

The User Interface Form


It is a good idea to isolate the user interface on a form (Figure 2) that is completely separated from the rest of the
application. This offers several advantages. By isolating the user interface from the components that represent the
database information itself, greater flexibility is introduced into the design: changes to the way the database
information is managed do not require that the user interface be rewritten, and changes to the user interface do not
require that the portion of the application that works with the database be changed. In addition, this type of isolation
allows developing common forms that can be shared between multiple applications, thereby providing a consistent
user interface. By storing links to well-designed forms in the Object Repository, developers can build on the existing
foundations, rather than starting over from scratch for each new project. Sharing forms also makes it possible to
develop corporate standards for application interfaces.

Figure 2 – User Interface Form

Data-aware controls
Data-aware controls (Figure 3) represent data from fields in a database record, and, if the dataset allows them to,
they enable users to edit those data and post changes back to the database. By placing data controls onto the forms in
the database application, you can build the database application's user interface (UI) in a way that makes information
visible and accessible to the users.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 235 -
Figure 3 – Some data-aware controls in a form
Deciding which data-aware controls will be added to the user interface is a process that depends on several factors,
including:
• Choosing between controls that are designed to display and edit plain text, controls that work with formatted
text, controls for graphics, multimedia elements, and so forth.
• Choosing to display information from a single record on the screen, or list the information from multiple
records using a grid (Figure 4).
• Using controls that reflect the limitations of the underlying dataset. For example, a grid wouldn’t be used
with a unidirectional dataset because unidirectional datasets can only supply a single record at a time.
• Adding your own controls or mechanisms to navigate and edit, or you may want to use a built-in control
such as a data navigator (Figure 5).

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 236 -
Figure 4 – DBGrid component

Figure 5 – DBNavigator component

The Data Module


If the user interface has been isolated into its own form, it is possible to use a data module (Figure 6) to house the
components that represent database information (datasets), and the components that connect these datasets to the
other parts of the application. Similarly to user interface forms, data modules can be shared in the Object Repository
so that they can be reused or shared between applications.

Figure 6 – Data Module Component

More on Data Modules


A data module is a special form that contains nonvisual components. All the components in a data module can be
placed on ordinary forms alongside visual controls. However, if the developer plans on reusing groups of database
and system objects, or if he/she wants to isolate the parts of the application that handle database connectivity and
business rules, then data modules provide a convenient organizational tool.
To display the text representation of the data module's properties, right-click on the Data Module and select the
“View as Text” option, as shown in Figure 7.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 237 -
Figure 7 – “View as Text” Option
This is an example of a DataModule text representation:

object DataModule3: TDataModule3


OldCreateOrder = False
Height = 178
Width = 268
end

The DataSource
The data source (Figure 8) acts as a conduit between the user interface and the dataset that represents information
from a database. Several data-aware controls on a form can share a single data source, case in which the display in
each of the controls is synchronized so that, as the user scrolls through records, the corresponding value in the fields
for the current record is displayed in each control.

Figure 8 – DataSource Component

More on DataSource
All datasets must be associated with a data source component if their data is to be displayed and manipulated in
data-aware controls. Similarly, each data-aware control needs to be associated with a data source component so the
control receives and manipulates data.
Important properties and events of the DataSource component:

Property Description

DataSet Specifies the dataset for which the data source component serves as a conduit to
data-aware controls or other datasets.

AutoEdit Determines if a data source component automatically calls a dataset's Edit


method when a data-aware control associated with the data source receives
focus.

Event Description

onDataChange Occurs when the data in a record has changed, either due to field edits or for
moving the cursor to a new record.

onStateChange Occurs when the state of a data source component's dataset changes.

The Dataset
The heart of a database application is the dataset (Figure 9). This component represents a set of records from the
underlying database. These records can be the data from a single database table, a subset of the fields or records in a
table, or information from more than one table gathered in a single view. By using datasets, the application logic is
buffered from restructuring the physical tables in the databases. When the underlying database changes, the way the
dataset component specifies the data it contains might have to be altered, while the rest of the application can
continue to work without alteration.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 238 -
Figure 9 – An example of a Dataset component

More on Datasets
The fundamental unit for accessing data is the dataset family of objects. The application uses datasets for all
database access. A dataset object represents a set of database records that are organized into a logical table. These
records may be the records from a single database table, or they may represent the results of executing a query or
stored procedure.
All dataset objects used in database applications descend from DB. TDataSet 1, and they inherit data fields,
properties, events, and methods from this class.
TDataSet is a virtualized dataset, meaning that many of its properties and methods are virtual or abstract. A virtual
method is a function or procedure declaration in which the implementation of that method can be (and usually is)
overridden in descendant objects. An abstract method is a function or procedure declaration without an actual
implementation. The declaration is a prototype that describes the method (and its parameters and return type, if any)
that must be implemented in all descendant dataset objects, while yet being differently implemented by each of
them.
Because TDataSet contains abstract methods, it cannot be used directly in an application without generating a
runtime error. Instead, create instances of the built-in TDataSet descendants and use them in the application, or
derive your own dataset object from TDataSet or its descendants and write implementations for all its abstract
methods.
TDataSet defines much of what is common to all dataset objects. For example, TDataSet defines the basic structure
of all datasets: an array of DB.TField 2 components that correspond to actual columns in one or more database tables,
lookup fields provided by the application, or calculated fields provided by the application.

Types of DataSets
TDataSet descendants are classified by the method they use to access their data. Another useful way to classify
TDataSet descendants is by considering the type of server data they represent. From this perspective, there are three
basic classes of datasets:

Table-type datasets
Table type datasets represent a single table from the database server, including all of its rows and columns.
Table type datasets allow taking advantage of the indexes defined on the server. Because there is a one-to-one
correspondence between the database table and the dataset, it is possible to use server indexes that are defined for
the database table. Indexes allow the application to sort the records in the table, speed searches and lookups, and can
form the basis of a master/detail relationship. Some table type datasets also take advantage of the one-to-one
relationship between the dataset and the database table to let you perform table-level operations, such as creating
and deleting database tables.

1
TDataSet is the base class for all dataset components that represent data in rows and columns.
2
TField is the common ancestor of all the field components. Field components represent individual fields (columns) in datasets. Use
field components to control the display and editing of data in the applications.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 239 -
Query-type Datasets
Query-type datasets represent a single SQL command or query. Queries can represent the result set from executing a
command (typically a SELECT statement), or they can execute a command that does not return any records (for
example, an UPDATE statement).
To use a query-type dataset effectively, the developer must be familiar with SQL and the server's SQL
implementation, including its limitations and extensions to the SQL-92 standard.

Stored Procedure-type Datasets


Stored procedure-type 3 datasets represent a stored procedure on the database server.
A stored procedure is a self-contained program written in the procedure and trigger language specific to the database
system used. They typically handle frequently repeated database-related tasks, and are especially useful for
operations that act on large numbers of records or that use aggregate or mathematical functions. Using stored
procedures typically improves the performance of a database application by:
• Taking advantage of the server's usually greater processing power and speed.
• Reducing network traffic by moving the processing to the server.
Stored procedures may or may not return data. Those that return data may return them as a cursor (similar to the
results of a SELECT query), as multiple cursors (effectively returning multiple datasets), or they may return data in
output parameters. These differences partially depend on the server: Some servers do not allow stored procedures to
return data, or only allow output parameters. Some servers do not support stored procedures at all. See the server
documentation to determine what is available.
If you choose to use a query-type dataset instead of a stored procedure-type dataset, see the server documentation for
the necessary syntax.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 240 -
Database Access Technologies
Delphi includes many components for accessing databases and representing the information they contain. They are
grouped according to their data access mechanism:
• The DBExpress page of the Component palette contains components that use DBExpress to access database
information. DBExpress is a lightweight set of drivers that provide the fastest access to database
information. However, DBExpress database components also support the narrowest range of data
manipulation functions.
• The ADO page of the Component palette contains components that use ActiveX Data Objects (ADO) to
access database information through OLEDB. ADO is a Microsoft Standard. There is a broad range of ADO
drivers available for connecting to different database servers. Using ADO-based components, the
application can be integrated into an ADO-based environment (for example, making use of ADO-based
application servers).
• The InterBase page of the Component palette contains components that access InterBase databases directly,
without going through a separate engine layer.
The Data Access page of the Component palette contains components that can be used with any data access
mechanism. This page includes TClientDataset, which can work with data stored on disk or, using the
TDataSetProvider component also on this page, with components from one of the other groups.
When designing a database application, the developer must decide which set of components to use. Each data access
mechanism differs in its range of functional support, its ease of deployment, and in the availability of drivers to
support different database servers.

The Data Connection


Different types of datasets use different mechanisms for connecting to the underlying database information. These
different mechanisms, in turn, make up the major differences in the architecture of the database applications that can
be built. There are four basic mechanisms for connecting to the data:
• Connecting directly to a database server. Most datasets use a descendant of TCustomConnection to
represent the connection to a database server.
• Using a dedicated file on disk. Client datasets support the ability to work with a dedicated file on disk. No
separate connection component is needed when working with a dedicated file because the client dataset
itself knows how to read from and write to the file.
• Connecting to another dataset. Client datasets can work with data provided by another dataset. A
TDataSetProvider component serves as an intermediary between the client dataset and its source dataset.
This dataset provider can reside in the same data module as the client dataset, or it can be part of an
application server running on another machine. If the provider is part of an application server, a special
descendant of TCustomConnection is also needed to represent the connection to the application server.
• Obtaining data from an RDS DataSpace object. ADO datasets can use an ADOdb. TRDSConnection
component to marshal data in multi-tier database applications that are built using ADO-based application
servers.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 241 -
Connecting Directly to a Database Server
The most common database architecture is the one in which the dataset uses a connection component to establish a
connection to a database server (Figure 10). The dataset then fetches data directly from the server and posts edits
directly onto the server.

Figure 10 – Connecting directly to the database server


Each type of dataset uses its own type of connection component, which represents a single data access mechanism:
• If the dataset is a DBExpress dataset such as TSQLDataSet, TSQLTable, TSQLQuery, or TSQLStoredProc,
the connection component is a TSQLConnection object. Connect the dataset to the SQL connection
component by setting its SQLConnection property. When using DBExpress datasets, add the connection
component explicitly. Another difference between DBExpress datasets and other datasets is that DBExpress
datasets are always read-only and unidirectional; this means you can only navigate by iterating through the
records in order, and you can't use the dataset methods that support editing.
• If the dataset is an ADO dataset such as TADODataSet, TADOTable, TADOQuery, or TADOStoredProc,
the connection component is a TADOConnection object. Connect the dataset to the ADO connection
component by setting its Connection property. As with BDE datasets, there is no need to explicitly add the
connection component; instead, set the dataset's ConnectionString property.
• If the dataset is an InterBase Express dataset such as TIBDataSet, TIBTable, TIBQuery, or TIBStoredProc,
the connection component is a TIBDatabase object. Connect the dataset to the IB database component by
setting its Database property. As with DBExpress datasets, add the connection component explicitly.
Although each type of dataset uses a different connection component, they all perform many of the same tasks and
present many of the same properties, methods, and events.
This architecture represents either a single-tiered or two-tiered application, depending on whether the database
server is a local database, such as Paradox, or a remote database server. The logic that actually manipulates the
database information resides in the same application that implements the user interface, although isolated into a data
module.
This architecture will be focused in this course. Because the DBExpress technology will be used, there will be a
change in the working method used, when compared to other technologies Delphi offers. As it was said before,
DBExpress datasets are always read-only and unidirectional; it means you can only navigate by iterating through the
records in an orderly manner, and you can't use the dataset methods that support editing.
Despite these limitations, there are great advantages in using DBExpress. For example, unidirectional datasets are
designed for quick lightweight access to database information, with minimal overhead.
In the next chapter, the DBExpress technology will be studied in detail.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 242 -
Now, understand a little more about the architecture that will be used during the course. Don’t worry about
understanding everything right from the start. In the next chapters and examples, more details will be given to
clarify some subjects.
The architecture to be used (Figure 11) includes two extra components to the architecture shown above. These
components are TDataSetProvider and TClientDataSet (and will be further explained ahead).
By using a provider component, connect TClientDataSet to another (source) dataset. The provider packages
database information into transportable data packets (which can be used by client datasets) and applies updates
received in delta packets (which client datasets create) back to a database server.

Figure 11 – Connecting directly to the database server, using a provider and a client dataset
To link the client dataset to the provider, set its ProviderName property to the name of the provider component. The
provider must be in the same data module as the client dataset. To link the provider to the source dataset, set its
DataSet property.
Once the client dataset is linked to the provider and the provider is linked to the source dataset, they automatically
handle all the details necessary for fetching, displaying, and navigating through the database records (assuming the
source dataset is connected to a database). To apply user edits back to the database, all that has to be done is call the
client dataset's ApplyUpdates method.

Advanced Database Development. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 243 -
Delphi 2009 Application Development for Win32
- 244 -
Using DBEx press to Access Data
This module covers:
• DBExpress Components Overview
• Configuring DBExpress Components

Delphi 2009 Application Development for Win32


- 245 -
DBExpress
DBExpress is a set of lightweight database drivers that provide fast access to SQL database servers. For each
supported database, DBExpress provides a driver that adapts the server-specific software to a set of uniform
DBExpress interfaces. When a database application that uses DBExpress is deployed, a DLL (the server-specific
driver) is included with the application files that have been built.
DBExpress allows accessing databases using unidirectional datasets. Unidirectional datasets are designed for quick
lightweight access to database information, with minimal overhead. Like other datasets, they can send an SQL
command to the database server, and if the command returns a set of records, they can obtain a cursor for accessing
those records. However, unidirectional datasets can only retrieve a unidirectional cursor. They do not buffer data in
the memory, which makes them faster and less resource-intensive than other types of dataset. However, because
there are no buffered records, unidirectional datasets are also less flexible than other datasets.
Many of the capabilities introduced by TDataSet are either unimplemented in unidirectional datasets or cause them
to raise exceptions. For example:
• The only supported navigation methods are the TDataSet.First and TDataSet.Next methods. Most of the
others raise exceptions. Some, such as the methods involved in bookmark support, simply do nothing.
• There is no built-in support for editing because it requires a buffer to hold the edits. The
TDataSet.CanModify property is always False; therefore, attempts to put the dataset into edit mode always
fail. You can, however, use unidirectional datasets to update data using an SQL UPDATE command, or
provide conventional editing support by using a DBExpress-enabled client dataset or connecting the dataset
to a client dataset.
• There is no support for filters, because filters work with multiple records - which require buffering. If you
try to filter a unidirectional dataset, it raises an exception. Instead, all limits on which data appear must be
imposed using the SQL command that defines the data for the dataset.
• There is no support for lookup fields - which require buffering to hold multiple records containing lookup
values. If a lookup field is defined on a unidirectional dataset, it does not work properly.
The DBExpress category of the Tool Palette contains components that use DBExpress to access database
information. They are:

Component Description
Encapsulates a DBExpress connection to a database server

Represents any data available through DBExpress, or sends commands to a


database accessed through DBExpress
A query-type dataset that encapsulates an SQL statement and enables
applications to access the resulting records, if any
A table-type dataset that represents all of the rows and columns of a single
database table
A stored procedure-type dataset that executes a stored procedure defined on
a database server
Intercepts messages transmitted between an SQL connection component
and a database server and saves them in a string list
A client dataset that uses an internal TSQLDataSet and a TDataSetProvider
for fetching data and applying updates
A TDataSet used to call DataSnap server methods and retrieve results from
them.
Table 1 – List of DBExpress components

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 246 -
For a long time, Delphi’s main data access technology was Borland Database Engine (BDE). BDE defines a
powerful library of API calls that can create, restructure, fetch data from, update, and otherwise manipulate local and
remote database servers. It provides a uniform interface to access a wide variety of database servers, using drivers to
connect to different databases.
The BDE technology is obsolete, though, and exists in Delphi only for upgrading purposes.
For modern applications, the DBExpress technology is recommended. In this course, BDE won’t be mentioned,
while DBExpress will be carefully approached.
DBExpress advantages are:
• Speed: Since the DBExpress components communicate directly with the database client software, the
developer will experience high performance in all aspects of database access.
• Access to Server Specific Features: DBExpress provides access to each server’s specific features. This
includes features such as two-phase commit functionality, the ability to have multiple concurrent
transactions using the same server connection.
• Delphi’s data access architecture: Since all DBExpress components descend from the Dataset component,
they support a familiar interface.
• Easy Deployment: When a database application that uses DBExpress is deployed, all that has to be done is
include the server-specific driver with the application; DBExpress will detect and use this driver whenever
the application is started.
• Portability: DBExpress is very simple to use and deploy under both Delphi and Kylix.
Thus, DBExpress represents a very fast, efficient, and reliable data access technology. So, if you are starting a new
application and need to access data from a database server, consider using the DBExpress technology.

TSQLDataSet Component
TSQLDataSet is a general-purpose unidirectional dataset for accessing database information using DBExpress. You
can add a TSQLDataSet component to a form or data module at design time, or create one dynamically at runtime.
TSQLDataSet can be used to:
• Represent the records in a database table, the result set of a SELECT query, or the result set returned by a
stored procedure.
• Execute a query or stored procedure that does not return a result set.
• Represent metadata that describes what is available on the database server (tables, stored procedures, fields
in a table, and so on).
Important properties of the SQLDataSet component:

Property Description
Active Specifies whether or not a dataset is open.
Params Represents the parameters for a query or stored procedure.
SQL Specifies the SQL statement to execute on the database server.
Connection Specifies the TSQLConnection component that connects the dataset to
a database server.

TSQLConnection Component
Use TSQLConnection (Figure 1) to represent a DBExpress database connection. Multiple SQL dataset components
can share, their SQLConnection property, a TSQLConnection component's database connection.

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 247 -
Figure 1 – SQLConnection component
Important properties and events of the TSQLConnection component:
Property Description
Connected Specifies whether the connection is active.
ConnectionName Names the connection configuration.
DriverName Specifies the database driver associated with the SQL connection. It can
be: ASA, ASE, DB2, Informix, InterBase, MSSQL, MySQL, and Oracle.
This can be either a dynamic-link library with a name like dbexpint.dll,
dbexpora.dll, dbexpmysql.dll, or dbexpdb2.dll, or a compiled unit that
can be statically linked into the application (dbexpint.dcu, dbexpora.dcu,
dbexpmys.dcu, or dbexpdb2.dcu).
LibraryName Specifies the DBExpress driver TSQLConnection uses to establish a
connection.
LoginPrompt Specifies whether a login dialog appears immediately before opening a
new connection.
Params Lists the connection parameters.
VendorLib Specifies the client software library (DLL or shared object) supplied by
the database vendor.

Event Description
AfterConnect Occurs after a connection is established.
AfterDisconnect Occurs after the connection closes.
BeforeConnect Occurs immediately before establishing a connection.
BeforeDisconnect Occurs immediately before the connection closes.
OnLogin Occurs after the BeforeConnect event and before a connection is
established. Use the OnLogin event to assign values to the User_Name,
Password, and Database parameters immediately before
TSQLConnection attempts to connect to the database server. OnLogin
only occurs if the LoginPrompt property is true. If LoginPrompt is true
but there is no OnLogin event handler, a default login dialog appears in
which the user can enter a user name and password. The connection fails
if correct values for the user name and password are not supplied in the
dialog or by the OnLogin event handler.

Connecting to a Database Server


The first step to start an application that accesses a database server is to establish a connection to this server. Delphi
provides several connection components, each of them being used with a determined data access mechanism.
InterBase Express uses a component called TIBDatabase. You will use a component called TSQLConnection.
Despite having different implementation processes, these components basically have the same function: they
establish a connection to a determined database server. Besides that, they all descend from the TCustomConnection
class, and many methods, properties, and events are common to all of them.
You will now create a new application and establish a connection to a database server. For this example, Borland
InterBase will be used as the database server. To do so, InterBase must be installed and working on your computer
(installing InterBase is not covered here; for further information, consult this product documentation).

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 248 -
After opening Delphi, create a new application for Win32. Go to File | New | VCL Forms Application. Rename the
form as frmEmployees and modify the Caption property to Employees. This form will be used to show information
about employees.
Save the project. Go to File | Save All or type Shift + Ctrl + S. Save the Unit as untEmployee.pas and the project file
as Employee.bdsproj.
Now add a DataModule component to the project. To do so, click on File | New | Other... In the open window,
select Delphi Files on the left side and double-click on the Data Module option, as shown in Figure 2. Change the
Data Module name (Name property) to dmMain. The Data Module component will store the data access
components, thus separating the business logic (data calculation and validation, for example) from the graphical
interface. So as not to make this example too complex, a single Data Module will be used for the whole project.
Save the project and name the Data Module unit as untDMMain.pas.

Figure 2 – Inserting a Data Module into the project


Now, the database connection will be established. Insert a TSQLConnection component in the Data Module.
TSQLConnection can be found in the DBExpress tab of the Tool Palette. Name this component as Connection.
Now, click on the Data Explorer Palette, right-click on InterBase, and select Add New Connection (Figure 3).

Figure 3 – Add New Connection of InterBase


In the DBExpress Connections, you will see the different kinds of connections that can be created (ASA, ASE, DB2,
IB, INFORMIX, MSSQL, MYSQL, and ORACLE). Create a new connection and use the InterBase database server.
When clicking on the Add Connection button, assign the name IBEmployee to the connection, as shown in Figure
4..
After creating the new connection, select it on the Tool Palette and right-click on Modify Connection, as shown in
Figure 5. The next step is to choose the Database. When installing Delphi, several data files are copied to your
computer for testing and studying purposes. These data files are usually in C:\ IBEmployee, but their location
depends on the operational system language and installation directory.

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 249 -
Figure 4 – New Connection dialog box

Figure 5 – Modify Connection.


Besides setting the data files, the username and password can also be set. By default, the username is sysdba and the
password is masterkey, as shown in Figure 6. However, remember that InterBase must be installed and running at
the moment.

Figure 6 – Modify Connection options.


Inside this “C:\” folder, there is a file called Employee.gdb. This file represents a database from Borland InterBase.
So, in the Database option, which can be found in the DBExpress Connections window, insert the file name
(Employee.GDB) and the complete path for its driver.
Having properly configured these options, it is time to test the connection. To test the connection, click on the
TSQLConnection component button and set the Connected property on Object Inspector to True.. The name and
password will be requested in a dialog. If any error message occurs, check for the following problems:
• Check whether Borland InterBase is installed and running at the moment.
• Check whether the Database file informed in the DBExpress Connections window exists on the hard disk.
• Check whether the sysdba user and the masterkey password are valid.
Notice that Delphi has automatically filled some of the properties (Figure 7) of the Connection component.

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 250 -
Figure 2 – Connection properties in the Object Inspector
The following table describes some of the more relevant TSQLConnection properties.
Property Description
Connected Specifies whether the connection is active. Set Connected to true to
establish a connection to the database server. Set Connected to false to close
a connection.
ConnectionName Names the connection configuration. Set ConnectionName to use a named
connection configuration. Setting ConnectionName automatically sets the
DriverName and Params properties to reflect the driver and connection
parameters.
DriverName Specifies the database driver associated with the SQL connection.
GetDriverName Specifies the function that returns the top-level DBExpress driver.
LibraryName Specifies the DBExpress driver that TSQLConnection uses to establish a
connection. LibraryName is the DBExpress library associated with the
driver specified by DriverName. Most applications do not need to use
LibraryName directly, because this property is set automatically when the
DriverName property is set.
VendorLib Specifies the client software library (DLL or shared object) supplied by the
database vendor. VendorLib is the library supplied by the database vendor
to support client-side use of the database. Most applications do not need to
use VendorLib directly, because this property is automatically set at the
time the DriverName property is set.
There is also the option to mark the LoginPrompt property as false. Therefore, user and password will not be
requested every time you try to connect to the database server.

Notice that the connections parameters are no more stored in dbxconnection.ini file. Instead, these
parameters are stored inside the TSQLConnection property.

Using DBExpress to Access Data. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 251 -
Delphi 2009 Application Development for Win32
- 252 -
TClientDataSet
This module covers:

• Introduction to TClientDataSets
• Using TClientDataSets with Single-Tier Applications
• Introduction and use of TField Components

Delphi 2009 Application Development for Win32


- 253 -
TClientDataSets
Client datasets are specialized datasets that hold all their data in the memory. The support for manipulating the data
they store in the memory is provided by midaslib.dcu or midas.dll. The format client datasets use for storing data is
self-contained and easily transported, thus allowing client datasets to:
• Read from and write to dedicated files on disk, acting as a file-based dataset
• Cache updates for data from a database server
• Represent the data in the client portion of a multi-tiered application. To work like that, the client dataset
must work with an external provider
• Represent the data from a source other than a dataset. Since a client dataset can use the data from an external
provider, specialized providers can adapt a variety of information sources to work with client datasets. For
example, an XML provider can be used to enable a client dataset to represent the information in an XML
document.
Whether client datasets are used for file-based data, caching updates, for data from an external provider (such as
working with an XML document or in a multi-tiered application), or a combination of these approaches - such as a
"briefcase model" application - you can always take advantage of a broad range of features client datasets support
for working with data.
For this course, the link between TClientDataSet and a provider (TDataSetProvider) will be explored. Therefore,
TClientDataSet will represent the application’s client part.
The course focus will be on the DBExpress data access technology. It was previously seen that DBExpress uses
unidirectional datasets, and that there are some consequential limitations, such as:
• Forward movement only (cannot use Last and Prior methods)
• No filter support
• No cached updates
• No lookup fields
However, it has been seen that using unidirectional datasets is a light and quick way of recovering data from a
database.
That is why TClientDataSet will be used: it provides the ability to modify data, change sort orders and maintain a
change log, support lookups, filtering, and much more.
TClientDataSet is powerful and has several functionalities. To get used with TClientDataSet, a single-tier
application whose data will be read and saved in a binary format file will be created.

Working With Single-Tier Application


A TClientDataSet can work attached to a database, not only through another data set – it allows working with a file-
based table, without using other components. It is called a single-tier application because the data is accessed from a
file-based table located on the same machine as the application. In this case, MyBase tables are being used.
When a TClientDataSet gets its data from a local file using MyBase, the metadata is read from this file. However,
neither mechanism is available when the in-memory dataset is created on-the-fly, at runtime. In these situations, it is
necessary that the structure of TClientDataSet be explicitly defined. Defining this metadata can be done either at
design time or at runtime. Once the metadata is defined, the in-memory dataset is created by calling the
TClientDataSet.CreateDataSet method, or by using the TClientDataSet's component editor in the designer.
There are two ways to define the metadata of a TClientDataSet. Either use the FieldDefs property of the
TClientDataSet, or create TFields (see more about Tfields below) and associate them with the TClientDataSet. The
most common approach to creating the metadata definitions is using FieldDefs. However, FieldDefs do not allow
creating virtual fields, such as calculated or aggregate fields. Similarly, using FieldDefs does not allow the creation
nested datasets. Therefore, create the metadata using TField objects.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 254 -
Defining a TClientDataSet’s Structure
The TFields that represent the structure of a TClientDataSet are defined at design time using the Fields Editor.
Unfortunately, this process is a bit tedious, as one field must be added at a time and its characteristics defined in a
row.
Now, start creating a new application, naming it “MyBaseSample”. Add a TClientDataSet to the form and name it
“cdsSample”. Right-click on it and select “Fields Editor…”. This brings the Fields Editor window up.
In the Fields Editor window, right-click on a blank area and select “New Field…” to create a new TField object.
Add the following fields:

Field Property Property Value

ID Name ID

Type Integer

Size 0

Field Type Data

FirstName Name FirstName

Type String

Size 30

Field Type Data

LastName Name LastName

Type String

Size 30

Field Type Data

BirthDate Name BirthDate

Type DateTime

Size 0

Field Type Data


Table 1 – List of Fields to be added in your TClientDataSet
When finished, the Fields Editor will look like this:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 255 -
Figure 1 – Field Editor.
Close the Fields Editor window. With this, four fields have just been defined for the TClientDataSet. However, this
is not enough, since the data set still has to be created.
Right-click on TClientDataSet to display the context menu again:

Figure 2 – Create DataSet


Select the Create DataSet item. This will create a new, empty client dataset that can then be edited and saved. TField
objects are used to create the fields’ definition.
Right-click on TClientDataSet again to see more options:

Figure 3 – More options on TClientDataSet

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 256 -
The highlighted options are MyBase-related. They allow loading a previously saved file, create a file on disk, or
clear the definitions.

Defining Where the Information Is Saved


TClientDataSet has a property called FileName. Set FileName if the client dataset always reads its data from and
writes its data to a single file. If FileName represents a valid file name, the client dataset automatically writes its
data to that file when it is closed. If FileName specifies the name of an existing file, the client dataset automatically
reads its data from that file when it is opened.
Remember that TClientDataSet has two additional methods it uses to work with files: SaveToFile and
LoadFromFile. These methods can be used to save the data to a different file, or to load and save data at times other
than when the Active property changes.
The SaveToFile method is declared as:

procedure SaveToFile(const FileName: string = '';


Format: TDataPacketFormat = dfBinary);
…and the LoadFromFile is declared as:

procedure LoadFromFile(const FileName: string = '');


The FileName parameter of both methods is optional – if they are omitted, the value of the FileName property is
used.
Right-click on TClientDataSet, select the “Save to binary MyBase file…” item, and enter “mydata.cds”. This creates
the file physically. The binary format renders the best performance and a reduced file size. The two XML formats
give more flexibility.
Now, select the FileName property, click on the ellipsis button, and select the recently created file.
Complete the form with a TDataSource, a TDBNavigator, and a TDBGrid, and then run it. Insert some records,
close the application, and run it once again. Notice that the records are automatically saved.

Working With Fields Components


TField encapsulates the fundamental behavior common to all field components. It introduces the properties, events,
and methods that are used to:
• Change the value of a field in a dataset
• Convert the value of a field from one data type to another
• Validate data that the user enters for a field
• Define how the data in the field appears as it is displayed or edited
• Calculate the value of a field from code written in the OnCalcFields event of the dataset
• Look up the field's value from another dataset
Do not create instances of TField. TField descendants are created automatically each time a dataset is activated.
These descendants can be dynamic (the default) or persistent. Dynamic field components reflect the columns in the
underlying metadata at the time a dataset is opened. Persistent field components are created at design time using the
Fields editor, which specifies the fields in the dataset, along with their properties and their arrangement.
Creating persistent field components ensures that the application will always use and display the same columns, in
the same order, even if the physical structure of the underlying database changes. If a column on which a persistent
field component is based is deleted or changed, the IDE generates an exception rather than opening the dataset
against a nonexistent column or mismatched data. If this happens, remove the field component for the nonexistent
field using the Fields editor.
A field in a dataset is always treated as one of the following TField descendant classes below:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 257 -
TADTField TDateField TReferenceField TAggregateField
TDateTimeField TSmallIntField TArrayField TFloatField
TSQLTimeStampField TautoIncField TFMTBCDField TStringField
TBCDField TGraphicField TTimeField TBinaryField
TGuidField TVarBytesField TBlobField TIDispatchField
TVariantField TBooleanField TIntegerField TWideStringField
TBytesField TInterfaceField TWordField TCurrencyField
TLargeIntField TDataSetField TMemoField
Table 2 - TField descendant classes
In the sample applications, the TClientDataSet dataset will be used.

Field Component Types


As described above, TField Components can be one of the two following types: dynamic or persistent. This two
TField component types are described below.

Dynamic Field Components


Dynamically generated field components are the default. In fact, all field components for any dataset start out as
dynamic fields the first time the developer places a dataset on a data module, specifies how that dataset fetches its
data, and opens it. A field component is dynamic if it is automatically created based on the underlying physical
characteristics of the data represented by a dataset. Datasets generate one field component for each column in the
underlying data. The exact DB.TField descendant created for each column is determined by the field type
information received from the database or (for TClientDataSet) from a provider component.
Dynamic fields are temporary. They exist only for as long as a dataset is open. Each time a dataset that uses dynamic
fields is opened again, it rebuilds a completely new set of dynamic field components based on the current structure
of the data underlying the dataset. If the columns in the underlying data change, then the next time a dataset that uses
dynamic field components is opened, the automatically generated field components are also changed to match.
When to use dynamic fields: Use dynamic fields in applications that must be flexible about data display and editing.
For example, to create a database browsing tool, dynamic fields must be used because every database table has
different numbers and types of columns. There is also the option to use dynamic fields in applications where user
interaction with data takes place mostly inside grid components, and the datasets used by the application change
frequently.
To use dynamic fields in an application
• Place datasets and data sources in a data module
• Associate the datasets with data. This involves using a connection component or provider to connect to the
source of the data, and setting any properties that specify what data the dataset represents
• Associate the data sources with the datasets
• Open the datasets

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 258 -
Figure 4 - Defining the Data Component Settings
Place data-aware controls in the application's forms, add the data module to each uses clause for each form's unit,
and associate each data-aware control with a data source in the module. In addition, associate fields with each data-
aware control that requires one. Note that because dynamic field components are being used, there is no guarantee
that any specified field name will exist when the dataset is opened.

Figure 5 - Linking data-aware controls to Data Fields

Figure 6 - Open Dataset

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 259 -
Aside from their ease of use, dynamic fields can be limiting. Without writing code, the developer cannot
change the display and editing defaults for dynamic fields, he/she cannot safely change the order in which
dynamic fields are displayed, and he/she cannot prevent access to any fields in the dataset. Additional fields
cannot be created for the dataset, such as calculated fields or lookup fields, and dynamic field's default data
type cannot be overridden either. To gain control and flexibility over fields in database applications, the
developer needs to invoke the Fields editor to create persistent field components for datasets.

Persistent Field Components


By default, dataset fields are dynamic. Their properties and availability are automatically set and cannot be changed
in any way (except for their display and editing properties). To gain control over a field's properties and events,
persistent fields must be created for the dataset. Persistent fields allow:
• Setting or change the field's display or edit characteristics at design time or runtime
• Creating new fields, such as lookup fields, calculated fields, and aggregated fields, that base their values on
existing fields in a dataset
• Validating data entry
• Removing field components from the list of persistent components to prevent the application from accessing
particular columns in an underlying database
• Defining new fields to replace existing fields, based on columns in the table or query underlying a dataset
At design time, the developer can – and should – use the Fields editor to create persistent lists of the field
components used by the datasets in the application. Persistent field component lists are stored in the application and
do not change even if the structure of a database underlying a dataset is changed. Once persistent fields are created
with the Fields editor, they can be provided with event handlers that respond to changes in data values, and also
validate data entries.

When persistent fields are created for a dataset, only those fields that were selected are available to the
application at design time and runtime. At design time, the Fields editor can always be used to add or
remove persistent fields for a dataset.

All fields used by a single dataset are either persistent or dynamic. You cannot mix field types in a single dataset. If
you create persistent fields for a dataset, and you then want to revert to dynamic fields, you must remove all
persistent fields from the dataset. For more information about dynamic fields, see Dynamic field components.

One of the primary uses of persistent fields is to gain control over the appearance and display of data.

You can create the following types of persistent fields:


• Data Field: represents a physical field in a database table
• Calculated Field: The value of a calculated field is not stored in or retrieved from the physical tables
underlying a dataset. Instead, calculated fields are calculated for each record in the table by the dataset's
OnCalcFields event handler, which typically uses expressions involving values from other fields in the
record in order to generate a value for each calculated field
• Lookup Field: If the field is a lookup field, its KeyFields property indicates which fields in the dataset must
match the LookupKeyFields in the LookupDataSet in order to identify a record in the LookupDataSet. The
value of the LookupResultField for that record becomes the Value of the field component
• Aggregate Field: An aggregate field displays values from a maintained aggregate in a client dataset. An
aggregate is a calculation that summarizes the data in a set of records

Lookup
In the following example, it is explained how to create a Lookup field in a dataset.
• Create a new VCL Forms Application

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 260 -
• Add a datamodule to the application
• Add a SQLConnection component (from the DBExpress tab) to the datamodule and rename it as
‘cntnPersistentFields’
• Set the DriverName property from the connection to InterBase. Click on the ellipsis button in the Params
property and set the Database param to the employee.gdb database (located in CodeGear’s shared data
directory – e.g. C:\Program Files\CodeGear Shared\Data)
• Add a SQLDataSet component to the datamodule and rename it as ‘sqlEmployee’. Set the CommandType
property to ctQuery and insert the following select statement in the CommandText property: ‘select
FIRST_NAME, LAST_NAME, SALARY, DEPTO_NO from EMPLOYEE’

Figure 7 - The CommandText Editor


• Add a DataSetProvider component to the datamodule, rename it as ‘dspEmployee’ and link its Dataset
property to the ‘sqlEmployee’ component created above
• Add a ClientDataSet component to the datamodule, rename it as ‘cdsEmployee’ and link its ProviderName
property to the ‘dspEmployee’ component created above. Set the property Active to True
• Right-click on cdsEmployee and open its Fields Editor
• Right-click on the fields editor and select the Add all fields option

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 261 -
Figure 8 - The Fields Editor

Figure 9 - Persistent fields added


• Add a DataSource component to the datamodule, rename it as ‘dsEmployee’ and link its DataSet property to
the ‘cdsEmployee’ component created above
• Add a SQLDataSet component to the datamodule and rename it as ‘sqlDepto’. Set the CommandType
property to ctQuery and insert the following select statement in its CommandText property: ‘select
DEPT_NO, DEPARTMENT from DEPARTMENT’
• Add a DataSetProvider component to the datamodule, rename it as ‘dspDepto’ and link its Dataset property
to the ‘sqlDepto’ component created above
• Add a ClientDataSet component to the datamodule, rename it as ‘cdsDepto’ and link its ProviderName
property to the ‘dspDepto’ component created above
• Right-click on cdsDepto and open its Fields Editor
• Right-click on the fields editor and select the Add all fields option
• Add a DataSource component to the datamodule, rename it as ‘dsDepto’ and link its DataSet property to the
‘cdsDepto’ component created above

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 262 -
Figure 10 - Data components
Now, the Lookup field will be created in the cdsEmployee component. These lookup fields will retrieve the Depto
name from the cdsDepto based on the Depto_No field from the cdsEmployee.
• Open the fields editor from the cdsEmployee
• Right-click and select the New field option

Figure 11 - Adding a new field


In the New Field editor, insert the following values:
• Name: lkpDEPTO
• Component: cdsEmployeelkpDepto (Delphi automatically completes this information when a value is
inserted into the name property)
• Type: String
• Size: 25
• Field Type: Lookup
• Key Fields: DEPT_NO
• Dataset: cdsDepto
• Lookup fields: DEPT_NO
• Result Field: DEPARTMENT
Figure 9 shows the resulting configuration.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 263 -
Figure 12 - New Field editor
Now, add a DBGrid component to the form and link its DataSource property to dsEmployee, which is placed in the
datamodule (don’t forget to add the datamodule to the form’s Uses clause).
Run the application.

Figure 13 - Application running


Figure 13 shows the application running. Note that the Department name is retrieved from the dataset and displayed
in the grid (DEPARTMENT column). You can edit the columns to the grid and delete the DEPTO_NO column,
displaying only the Depto’s name.

To modify the display label from the created calculated field, open the fields editor, select the lkpDEPTO
field and modify its DisplayLabel property to a new value.

Aggregate
In the following example, it will be explained how to create an Aggregate field in a dataset.
Create a new project and add a TClientDataSet to the form, then rename it as ‘cdsSales’. Set the FileName property
of the TClientDataSet to the orders.cds file (located in CodeGear’s shared data directory – e.g. C:\Program
Files\CodeGear Shared\Data).
Add a new TField object to TClientDataSet.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 264 -
Figure 14 - Creating an aggregate field
Click on OK and the TField object will appear in the Fields Editor:

Figure 15 - Aggregate field added


Notice that the aggregate field is kept separated from the other fields. Select the aggregate field and configure the
Expression property to SUM (AmountPaid). The Expression property defines the calculation the aggregate will
perform. Set its Currency and Active properties to True. In TClientDataSet, there’s a property called
AggregatesActive. Set it to True, just as well.
Add a TDataSource to the form. Rename it as ‘dsOrders’, and link its DataSet property to the ‘cdsOrders’
component created above.
Complete the form with a TPanel, a TDBNavigator, a TDBGrid, and a TDBText, as seen below:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 265 -
Figure 16 - Application with an aggregate field running

Aggregate Index and Grouping Level


By default, an aggregate field will sum up all the records in the table. If you want the calculation to be made on a
customer basis, set the IndexName and GroupingLevel properties of the aggregate.
First, you must create a persistent index whose initial fields define the group. In this case, create an index on the
CustNo field.
Select TClientDataSet and, in its IndexDefs property, click on the ellipsis button to show the IndexDefs window.
Click on the Add New button to create a new IndexDef object. Set its Name property to CustomerIdx, its Fields to
“CustNo”, and its GroupingLevel to 1.
The Fields property of the IndexDef object can have more than one field to index on. For example, the index can be
created based on CustNo, SaleDate, and PaymentMethod. Setting the GroupingLevel to 0, the calculation will be
made on all records; setting the GroupingLevel to 1, the calculation will be made on the CustNo field; setting it to 2
will perform the calculation for each customer for each sale date.
Link the TBDText DataSource property to the ‘cdsOrders’ component.
Lastly, set the TClientDataSet’s IndexName property to the index just created, CustomerIdx.
Running the application, notice that the calculation now is made on a customer basis – if you change to another
customer, the calculation is performed again.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 266 -
Figure 17 -Index added
There are some things to comment here.
Each TAggregateField has an Active property that can be toggled True or False. It’s useful when there’s the need to
add, remove, or change a number of records at runtime. If it affects the aggregate calculation, these changes will be
much slower if they are active. In this case, deactivate the aggregates and reactive them when finished.
TClientDataSet also has an AggregatesActive property that changes the status of all its aggregates at the same time.
Besides the SUM function, aggregates also have the following functions:

Operator Use

SUM Totals the values for a numeric field or expression.

AVG Computes the average value for a numeric or date-time field or


expression.

COUNT Specifies the number of non-blank values for a field or expression.


Use count (*) to count the number of records in a dataset or
subgroup.

MIN Indicates the minimum value for a string, numeric, or date-time field
or expression.

MAX Indicates the maximum value for a string, numeric, or date-time field
or expression.

Sum(Qty * Price) { legal -- summary of an expression on fields }

Max(Field1) – Max(Field2) { legal -- expression on summaries }

Avg(DiscountRate) * 100 { legal -- expression of summary and constant }

Min(Sum(Field1)) { illegal -- nested summaries }

Count(Field1) – Field2 { illegal -- expression of summary and field }

Calculated
In the following example, it will be explained how to create a Calculated field in a dataset.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 267 -
Create a new project and add a TClientDataSet to the form. Rename TClientDataSet as ‘cdsOrders’. Set the
TClientDataSet FileName property to the orders.cds file (located in CodeGear’s shared data directory – e.g.
C:\Program Files\Common Files\CodeGear Shared\Data).
Add the fields TaxRate and AmountPaid. To make this, right-click on the fields editor and select the Add fields
option. Then select the TaxRate and AmountPaid fields.
Add a new TField object to TClientDataSet.

Figure 18 - Creating a new calculated field


Click on OK and the TField object will appear in the Fields Editor:

Figure 19 - Calculated field added


Select the TClientDataSet that was inserted above and add the following code to its OnCalcFields event:

procedure TForm6.cdsOrdersCalcFields(DataSet: TDataSet);


begin
cdsOrdersCalculatedTotal.AsCurrency := cdsOrdersAmountPaid.AsCurrency +
(cdsOrdersAmountPaid.AsCurrency * cdsOrdersTaxRate.AsCurrency) / 100;
end;
Add a TDataSource into the form, rename it as ‘dsOrders’, and link its DataSet property to the ‘cdsOrders’
component.
Complete the form with a TPanel, a TDBNavigator, and a TDBGrid, as seen below:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 268 -
Figure 20 - Application running
Notice that when the values from the TaxRate and AmountPaid columns are changed, the values from the
CalculatedTotal column are automatically refreshed.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 269 -
DBExpress Example
As seen before, Delphi provides several data access technologies. It would take a long time to describe all of them,
which is why, for this course, the focus will be on the most flexible and fast technology, which also offers the easiest
deployment: DBExpress. All data access components mentioned in the example (except for TClientDataSet and
TDataSetProvider) belongs to DBExpress.

Displaying the Data


Now that you learned how to establish a connection with a database server, let’s show the data of the Employee
database table.
Firstly, you have to keep in mind that the architecture used is that which uses a provider and a client dataset. Three
parts of this architecture have already been seen: the InterBase server, the TSQLConnection component, and the
User Interface (UI – the form).
The next step is to insert a TSQLDataset component in the application. It will be responsible for sending SQL
commands to the database server and storing the query resulting data in table format. In this case specifically,
considering you will bring some database data to display in the form, you will use the SELECT command from the
SQL language (the TSQLDataset component can also execute SQL commands that don’t return a result set).
Remember that TSQLDataset is unidirectional. Therefore, as you will show the query results in table format (in a
TDataGrid component), you need all data that is returned by the query to be in the memory in order to display it.
As previously explained, TSQLDataSet doesn’t load the data in the memory, and you can only navigate through its
first two records. Therefore, 2 other complementing components are needed in this architecture: a provider
(TDataSetProvider) and a client dataset (TClientDataSet). TDataSetProvider will be responsible for fetching the data
that is retrieved (by a query made by SQLDataSet), packing and sending it to TClientDataSet. TClientDataSet will
load this data in the memory, making it possible to show, navigate, insert, edit, and delete records.
However, no record insertion, change, or exclusion will be reflected in the database. Record manipulation will
entirely occur in the client’s computer memory, and TClientDataSet will be responsible for that. When you
eventually want to save the changes in the database, call the TClientDataSet’s ApplyUpdates method.
TClientDataSet has a property called Delta, which stores information about inserted, edited, and removed records.
When you call the ApplyUpdates method, the Delta value is transferred to TDataSetProvider. From this value
(which has all the changes that were completed in the records), TDataSetProvider can apply the updating to the
database, opening a transaction and committing the changes.
Summarizing:
Providing:
• The ClientDataSet component requests data to the DataSetProvider
• DataSetProvider fetches the requested data – in this case, data returned by a SQLDataSet query - and
packages this data into a transportable data packet
• DataSetProvider sends the data to the ClientDataSet
Resolving:
• The ClientDataSet component holds the data in the memory
• ClientDataSet executes updates in the memory until the ApplyUpdates method is called
• DataSetProvider receives the updated data from ClientDataSet
• DataSetProvider applies updates to the database server and logs any updates that cannot be applied
• DataSetProvider returns unresolved updates to the ClientDataSet, for further reconciliation
This working approach is called disconnected, because the record updating is performed in the memory (without
direct connection with a database). It’s only when the ApplyUpdates method is called that the changes are actually
committed in the database.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 270 -
Now, get back to the project. Go to the Tool Palette and include a TpageControl component in the form. It can be
found in the Win32 tab. Change the TPageControl’s Name property to pgcEmployees and its Align property to
alClient. Right-click on TPageControl and click on “New Page”. Change the new tab name to tabDisplayingData
and the Caption property to “Displaying Data”. This tab will be used to show the Employee table content of the
Employee database. Go to the project’s DataModule and add a TSQLDataSet component. Change its name to
sqlEmployees. Expand the CommandType property (Figure 21) and notice that there are three different kinds of
commands.

Figure 21 – TSQLDataSet CommandType property


Each command indicates the type of command that is contained in the CommandText property. The table below
shows a description of each one of them:

CommandType Corresponding CommandText

ctQuery An SQL statement the dataset executes.

ctServerMethod The name of a server method.

ctStoredProc The name of a stored procedure.

ctTable The name of a table on the database server. The SQL dataset automatically generates
a SELECT statement to fetch all the records on all the fields in this table.

As an SQL command will be executed in the database (SELECT, in this case), keep the CommandType property as
ctQuery. Now, change the SQLConnection property of the sqlEmployees component to point to the connection
component (Connection, in this case) with the server. Before executing the next step, change the Connection
component’s Active property to True (this way, the Connection component establishes a connection with the
database server at design time) and change its LoginPrompt to False (name and password will not be requested
every time Connection tries to connect to the server).
Now click on the sqlEmployees’ CommandText property ellipses button. On the left side of the “CommandText
Editor” dialog box, there’s a list of all Employee database tables. Select the Employee table and click on the “Add
Table to SQL” button. The SELECT command should appear in the SQL area as follows:

SELECT * FROM Employee


You now need to add the fields. Right below the table list, there’s a list of all the fields that are contained in this
table. Select all the fields and click on the “Add Field to SQL” button. Now the SQL command is complete (Figure
22).

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 271 -
Figure 22 – Building an SQL statement in the CommandText Editor Dialog Box
Click on the OK button to apply the changes and close the window. Notice that now the sqlEmployees’
CommandType property contains the SQL command.
Now add a TDataSetProvider component (Tool Palette, Data Access tab) and change its name to dspEmployees.
Configure the DataSet property to point to the sqlEmployees (for further information about TDataSetProvider,
consult the DBExpress chapter).
Insert a TClientDataSet component to the dmMain Data Module and rename it as cdsEmployees. As a provider will
be used to feed this client dataset, change the cdsEmployees ProviderName property to dpsEmployees.
Finally, insert a TDataSource component. This component will be responsible for the connection between
cdsEmployees and the data controls that will be in the form. It works as a conduit, in a way that data controls can
show the value of a current record field of the dataset (cdsEmployees in this case). Change the TDataSource Name
property to dsEmployees and point its DataSet property to cdsEnployees.
Now, you are ready to read the Employee table records and show the results in the form. Using several components
to read only a few database records may seem a little complicated in the beginning, but keep in mind that this
separation – each component having a specific function – makes the application much more flexible, not to mention
each of these components’ powerful resources.
So far, the application Data Module should look like Figure 23.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 272 -
Figure 23 – Data Module configuration to access the Employee table
Go back to the form (form designer) and insert two more components: a DBNavigator and a DBGrid. Rename
DBNavigator as navEmployees and change its Align property to alTop. Rename DBGrid as grdEmployees and
change its Align property to alClient.
Now, it is necessary to configure grdEmployees and navEmployees’ DataSource properties to point to the
dsEmployees data source. Before doing it, however, it’s necessary to add the dmMain data module unit to the
untEmployee unit Uses clause. To do so, click on File > Use Unit (or Alt + F11) and select the untDmMain unit
(Figure 24).
Now, select grdEmployees and navEmployees and point their DataSource properties to the dmMain data module’s
dsEmployees, as shown in Figure 25.
Delphi allows seeing the data at design time. In other words, it allows the dataset to be populated and the grid to
show these data. It’s called live data. Go to dmMain, click on the cdsEmployees and change its Active property to
True. Go back to the form. The grdEmployees should display the Employee table data, as shown in Figure 26.
If you execute this application now (selecting Run > Run, or F9) the data will be automatically loaded in the
cdsEmployees. That’s because the cdsEmployees component’s Active property value is True.

Figure 24 – Using the data module dmMain unit


However, if this property is configured as False, open the client dataset at run time doing the following:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 273 -
cdsEmplooyees.Open; // opens the client dataset
To close it, simply type:

cdsEmployees.Close; // closes the client dataset

Figure 25 – Connecting the data control’s DataSource property to dsEmployees


So far, you have learned how to use DBExpress components to query a database and, with the help of a provider,
how to load these data in the memory through a client dataset component. The next step is learning how to
manipulate these data.

Figure 26 – DBGrid displaying data at design time

Manipulating the Data


Now it will be explained how to manipulate data in a client dataset, as well as how to apply the changes to the
database. You will keep using the same application used in the previous example.
Go to the form designer and create a new tab in pgcEmployees. Change the new tab name property to
tabManipulatingData and its caption property to Manipulating Data.
In the dmMain Data Module, right-click on cdsEmployees and choose the “Fields Editor…” option. Right-click on
the Fields Editor window and choose the “Add all fields” option (Figure 27).
The Fields Editor will be filled with all the Employee table fields. For more information on how to work with fields,
refer to “Working with Field Components”.
Keep the Fields Editor window opened and go back to the form designer. Select all fields that are in the Fields
Editor and drag them onto the form, as shown in Figure 28.
Notice that Delphi automatically creates the data controls according to the fields that were dragged through the
Fields Editor. Each data control also has a label. The caption property value of each label is similar to the field name
in the Fields Editor. You can change the DisplayLabel property of a Fields Editor’s field. This way, when you click
and drag this field, the created label will assume as its caption property the same DisplayLabel property value of the
field that is being dragged.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 274 -
Arrange the components inserted in tabManipulatingData in a way that they are well distributed in the screen.
Now, put eight buttons in the bottom part of tabManipulatingData (see Figure 29).

Figure 27 – Fields Editor “Add all fields” option


Before typing the code below, add the DB unit in the Uses clause of the untEmployee unit (because dsEdit and
dsInsert are declared in a type called TDataSetState, inside the DB unit).
Configure each button as follows (according to Figure 29, from the left to the right):
• Name: btnOpen
• Caption: Open
• onClick event:

procedure TfrmEmployees.btnOpenClick(Sender: TObject);


begin
if not dmMain.cdsEmployees.Active = True then
dmMain.cdsEmployees.Open;
end;
• Name: btnClose
• Caption: Close
• onClick event:

procedure TfrmEmployees.btnCloseClick(Sender: TObject);


begin
if dmMain.cdsEmployees.Active = True then
if dmMain.dsEmployees.State in [dsInsert, dsEdit] then
ShowMessage('There are pending changes that should be posted or canceled!')
else
dmMain.cdsEmployees.Close;
end;
• Name: btnInsert
• Caption: Insert
• onClick event:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 275 -
procedure TfrmEmployees.btnInsertClick(Sender: TObject);
begin
if not (dmMain.dsEmployees.State in [dsEdit, dsInsert]) then
dmMain.cdsEmployees.Insert;
end;

Figure 28 – Dragging Fields Editor’s fields to the form


• Name: btnDelete
• Caption: Delete
• onClick event:

procedure TfrmEmployees.btnDeleteClick(Sender: TObject);


begin
if not (dmMain.dsEmployees.State in [dsEdit, dsInsert]) then
dmMain.cdsEmployees.Delete;
end;
• Name: btnEdit
• Caption: Edit
• onClick event:

procedure TfrmEmployees.btnEditClick(Sender: TObject);


begin
if not (dmMain.dsEmployees.State in [dsEdit, dsInsert]) then
dmMain.cdsEmployees.Edit;
end;
• Name: btnPost
• Caption: Post
• onClick event:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 276 -
procedure TfrmEmployees.btnPostClick(Sender: TObject);
begin
if dmMain.dsEmployees.State in [dsEdit, dsInsert] then
dmMain.cdsEmployees.Post;
end;

Figure 29 – Manipulating Data tab


• Name: btnCancel
• Caption: Cancel
• onClick event:

procedure TfrmEmployees.btnCancelClick(Sender: TObject);


begin
if dmMain.dsEmployees.State in [dsEdit, dsInsert] then
dmMain.cdsEmployees.Cancel;
end;
• Name: btnApply
• Caption: Apply
• onClick event:

procedure TfrmEmployees.btnApplyClick(Sender: TObject);


begin
if dmMain.cdsEmployees.ChangeCount > 0 then
dmMain.cdsEmployees.ApplyUpdates(-1);
end;

The parameter passed onto the TClientDataSet cdsEmployees’s ApplyUpdates method indicates the
maximum number of errors that the provider should allow before prematurely stopping the update
operation. Pass –1 to indicate that there is no limit to the number of errors.

UpdateMode and ProviderFlags


The type of optimistic locking that is used in an application is established using the dataset UpdateMode property.
The UpdateMode setting specifies which fields in the dataset will be used to find the original record. Specifically,
this property sets what fields are used in the WHERE clause of the UPDATE and DELETE statement generated by
the provider. If the record is found to have values other than those it had at the time it was first read, the update fails

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 277 -
and an exception is raised. Deciding where to set this property depends solely on which components you use. Using
ClientDataSets, the UpdateMode property is on the DataSetProvider. If you use one of the combination CDS/DSP
components (e.g. SimpleClientDataset), those components have an UpdateMode property because of the internal
DataSetProvider. The table below lists the three possible values for UpdateMode along with their description.

Value Description

upWhereAll Every column in the record is used to find the record. This is the Delphi default.

upWhereChanged Only the columns that were changed (in the record that is being edited) are used to find the
record.

upWhereKeyOnly Only the columns that define the key are used to find the record.

Careful consideration should be given when choosing the value for the UpdateMode. All the settings present
particular advantages and setbacks. The default, upWhereAll, is the most restrictive and has the worst performance.
Using all the fields in the WHERE clause to locate the original record ensures the record has not been changed since
it was originally read. If you get an error message stating the query is too complex when you attempt an update, the
SQL database is indicating it cannot handle a WHERE clause with all the fields in the table being used. The
upWhereKeyOnly is the least restrictive. It allows anyone to change any field (except the primary key) regardless of
what the original values were. In between these two property options, there is the upWhereChanged value. This can
be a problem when the values in multiple fields have a specific meaning when taken together, like in a multi-field
primary key, where users are allowed to change the values. Having a good database design and a well thought set of
business rules assist in determining the correct UpdateMode setting.
Testing this feature can be done by changing the UpdateMode property and then running two instances of an
application, in order to make modifications to each of them.
The ProviderFlags properties for TField components are also worth in determining how an update is to be processed.

Value Description

pfInUpdate Field is included in update

pfInWhere Field is used in finding the original record to be updated

pfInKey Field is used in finding the current record after an update fails

pfHidden Field is included in the data packet to ensure uniqueness of the record. The field
is used to find the original record to update. The field is not visible to the
application.

Each field in the dataset that is used by the provider can set which of the ProviderFlags are applicable. Changing the
ProviderFlags modifies the SQL UPDATE and DELETE statements that were created by the provider to change and
remove records from the database.

Reconciling Errors
In either the briefcase or cached updates model, the database server may potentially return a number of errors when
the updates are applied. Someone may have changed a record before the user got a chance to update his/hers, or
some other database or business rule on the server may have been violated, thus triggering an exception. When that
happens, the provider returns a set of data packets describing the before and after image of each error record and an
error object.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 278 -
Handling these errors is called Reconciling and occurs in the TClientDataSet’s OnReconcileError event handler.
You can write your own OnReconcileError event handler, or you can use the predefined form included with Delphi.
It contains generalized exception handling code.
The Object Repository contains a dialog box that provides the basic reconciliation code already completed.
Click on File | New | Other and, in the New Items dialog box, select “Reconcile Error Dialog” (Figure 27).

Figure 30 – Reconcile Error Dialog item in New Items


Go to the dmMain Data Module, click on the cdsEmployees client dataset and add the following code to the
onReconcileError event (before doing it, add the RecError unit in the Uses clause of the untDMMain unit):

procedure TdmMain.cdsEmployeesReconcileError(DataSet: TCustomClientDataSet;


E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction);
begin
Action := HandleReconcileError(DataSet, UpdateKind, E);
end;
Notice that the event includes the following parameters:
DataSet: A client dataset that contains the updated record which couldn't be applied. This dataset's methods can be
used to get information about the problem record and to edit the record in order to correct any problems.
E: An object that represents the problem that occurred. This exception can be used to extract an error message or to
determine the cause of the update error.
UpdateKind: The type of update that generated the error. UpdateKind can be ukModify (the problem occurred
while updating an existing record that was modified), ukInsert (the problem occurred while inserting a new record),
or ukDelete (the problem occurred while deleting an existing record).
Action: A var parameter that indicates what action should be taken when the event handler exits. In the event
handler, this parameter is set so as to:
• Skip this record, leaving it in the change log (rrSkip or raSkip)
• Stop the entire reconcile operation (rrAbort or raAbort)

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 279 -
• Merge the modification that failed into the corresponding record from the server (rrMerge or raMerge). It
only works if the server record does not include any changes to the fields that were modified in the client
dataset's record
• Replace the current update in the change log with the value of the record in the event handler, which has
presumably been corrected (rrApply or raCorrect)
• Ignore the error completely (rrIgnore). This possibility only exists in the OnUpdateError event handler, and
is intended for those cases where the event handler applies the update back to the database server. The
updated record is removed from the change log and merged into Data, as if the provider had applied the
update
• Back out the changes for this record on the client dataset, reverting to the originally provided values
(raCancel). It’s only possible in the OnReconcileError event handler.
• Update the current record value to match the record on the server (raRefresh). It’s possible only in the
OnReconcileError event handler.
The Reconcile Error Dialog is displayed after a call to ApplyUpdates, provided that one or more error records exist.
Each record that cannot be posted is displayed in the dialog box, one error at a time. Figure 31 shows an example in
which an attempt is made to save a record that was previously changed by another user.

Figure 31 – Reconcile Error dialog box – Update Error


The dialog shows errors for the records you have inserted, modified, and attempted to delete. The Update Type label
indicates which of the three actions was taken on the record. The grid has a maximum of three columns. They are
the value you are attempting to save (Modified Value), the value of the field as it is currently in the database
(Conflicting Value) and the value as it was at the moment the client application originally received the data packet
(Original Value).
Grid changes are displayed based on the type of problem, the update action, and on which of the two check boxes at
the bottom of the dialog are checked. The first checkbox, Show conflicting fields only, limits the records in the grid
to only those fields that have been changed and are in conflict with the current database values. Figure 31 shows an
example of this selection. If the Show changed fields only checkbox is checked, the fields in all records that have
been changed are listed. If there is no conflicting value, the Conflicting Value column contains <Unchanged>. If you
attempt to delete a record that has already been deleted by another user, only the Modified Value column is
displayed. The radio buttons in the upper right provide the actions that can be applied to the error record. Keep
reading for their specific descriptions.

Value Description

Skip Skips updating the record that raised the error condition, and leaves the
unapplied changes in the change log.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 280 -
Value Description

Abort Aborts the entire reconcile operation.

Merge Merges the updated record with the record on the server.

Correct Replaces the current updated record with the value of the record in the event
handler.

Cancel Backs out all changes for this record, reverting to the original field values.

Refresh Backs out all changes for this record, replacing it with the current values from
the server.

The action for each error can be set before clicking on the OK button. Based on the type of error, you can determine
whether you want to leave the record in the change log, cancel it, merge the updated record, or abort the entire
reconcile operation.

Searching data
A very common task when working with database applications is to search for a specific record in the database,
according to some criteria. Delphi has several methods that implement record search. Now, two of these methods
will be presented: Locate and Lookup.

Using Locate
Locate moves the cursor to the first row that matches a specified set of search criteria. In its simplest form, you pass
Locate the name of a column to search, a field value to match, and an options flag that specifies whether the search
is case-insensitive or if it should use partial-key matching instead. (Partial-key matching is when the criterion string
need only be a prefix of the field value).
If Locate finds a match, the first record containing the match becomes the current record. Locate returns True if it
finds a matching record, False if it does not. If a search fails, the current record does not change.
Locate uses the fastest possible method to locate matching records. If the columns to search are indexed and the
index is compatible with the search options specified, Locate uses the index.

Using Lookup
Lookup searches for the first row that matches specific search criteria. If it finds a matching row, it forces the
recalculation of any calculated fields and lookup fields associated with the dataset, and then returns one or more
fields from the matching row. Lookup does not move the cursor to the matching row; it only returns values from it.
Lookup returns values for the specified fields from the first matching record it finds. Values are returned as
Variants. If more than one return value is requested, Lookup returns a Variant array. If there are no matching
records, Lookup returns a Null Variant.
Like Locate, Lookup uses the fastest possible method to locate matching records. If the columns to search are
indexed, Lookup uses the index.
Now, get some practice:
In the example application, create a new tab in pgcEmployees, rename it as tabSearchin and change its caption
property to ‘Searching Data’.
Place two Label components, two Edit components, and a DBGrid component in it. It should look like Figure 32.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 281 -
Figure 32 – Searching Data Tab
Configure the component properties as follows (from the left to the right and from the top down):
Labels:
• Name: lblFirstName
• Caption: First Name
• Name: lblLastName
• Caption: Last Name
Edits:
• Name: edtFirstName
• Text: (empty)
• Name: edtLasName
• Text: (empty)
Buttons:
• Name: btnLocate
• Caption: Locate
• onClick event:

procedure TfrmEmployees.btnLocateClick(Sender: TObject);


begin
dmMain.cdsEmployees.Locate('FIRST_NAME', edtFirstName.Text, [loPartialKey,
loCaseInsensitive]);
end;
• Name: btnLookup
• Caption: Lookup
• onClick event:

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 282 -
procedure TfrmEmployees.btnLookupClick(Sender: TObject);
var
LookupResults: Variant;
begin
LookupResults := dmMain.cdsEmployees.Lookup('LAST_NAME', edtLastName.Text,
'FULL_NAME;SALARY');
if not VarIsNull(LookupResults) then
ShowMessage('Full Name: ' + VarToStr(LookupResults[0]) + #13 + 'Salary: ' +
VarToStr(LookupResults[1]));
end;

Grid:
• Name: grdSearch
• DataSource: dmMain->dsEmployees
Run the application and do some research tests. Don’t forget to open the cdsEmployees dataset before clicking on
the Lookup and Locate buttons; otherwise it will raise an exception.

Master/Detail
Applying cached updates is particularly tricky when you are working with multiple datasets that are linked in a
master/detail relationship, because the order in which updates are applied to each dataset is significant. You usually
have to update master tables before detail tables, except when handling deleted records (when this order must be
reversed). There’s no reason to worry about that: client datasets automatically handle all arrangement issues with
master/detail relationships.
In the next example, you will create a master/detail relationship to show the salaries (detail) by employees (master).
You need to add 4 additional components to the Data Module dmMain: 1 TSQLDataSet, 1 TClientDataSet, and 2
TDataSources.
Add TSQLDataSet, set its name to sqlSalaryHistory and link its SQLConnection property to the Connection
component. Type the following statement in the sqlSalaryHistory’s CommandText property:

select * from SALARY_HISTORY where EMP_NO = :emp_no


Notice that the names of the parameters must match the names of the linking fields in the master table. In this
example, EMP_NO is the field name in both tables.
Add a TDataSource component, set its name to dsEmp and link its DataSet property to the sqlEmployees dataset.
Click on the sqlSalaryHistory dataset and change its DataSource property to link to the TDataSource dsEmp.
Right-click on the cdsEmployees client dataset and click on the “Fields Editor…” option. . Right-click on the newly
opened window and select the “Add all Fields” option. Notice that a new field is added (Figure 33).

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 283 -
Figure 33 - sqlEmployees Fields Editor box
This new field (sqlSalaryHistory) comprises the detail dataset.
Add a TClientDataSet component and set its name to cdsSalaryHistory. Link the cdsSalaryHistory’s DataSetField
property to the new field just created (Figure 34).

Figure 34 - Setting the cdsSalaryHistory DataSetField property


Now, add another TDataSource component, set its name to dsSalaryHistory and link its DataSet property to the
cdsSalaryHistory client dataset.
It’s now time to design the form. Go to the form designer and create a new pgcEmployees tab. Change the new tab’s
caption property to Master/Detail and set its name to tabMasterDetail.
To conclude this process, add 2 TLabels and 2 TDBGrids. The form should look like Figure 35. Change the
following properties for the first TDBGrid.
• Name: grdEmployeesMaster
• DataSource: dmMain->dsEmployees
And set the following properties for the second TDBGrid:
• Name: grdSalaryHistoryDetail
• DataSource: dmMain->dsSalaryHistory

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 284 -
Figure 35 Master / Detail with DBExpress
Run the application and remember: if you make changes to any of the datasets (cdsEmployees or cdsSalaryHistory)
you have to apply the updates from the Master Dataset (“cdsEmployees.ApplyUpdates”) only.

TClientDataSet. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.


Delphi 2009 Application Development for Win32
- 285 -
Delphi 2009 Application Development for Win32
- 286 -
Deploying Application Files
This module discusses the required files for deploying Delphi Win32 applications.

Delphi 2009 Application Development for Win32


- 287 -
Application Files
Besides the executable file, an application may require a number of supporting files, such as DLLs, package files,
and helper applications. In addition, the Windows registry may need to contain entries for an application - from the
location of supporting files to simple program settings. An installation program, such as InstallShield Express, can
automate the process of copying an application’s file to a computer and making any needed registry settings. Nearly
all types of applications include the following issues:
Database and Web applications require additional installation steps.

Using Installation Programs


Simple applications that consist of only an executable file are easy to install on a target computer. Simply copy the
executable file into the computer. However, more complex applications that comprise multiple files require more
extensive installation procedures. These applications require dedicated installation programs.
Setup toolkits automate the process of creating installation programs, often without the need to write any code.
Installation programs created with Setup toolkits perform various tasks inherent to installing Delphi applications,
including: copying the executable and supporting files to the host computer, making Windows registry entries, and
deploying DBExpress applications either as a stand-alone executable file or as an executable file that includes
associated DBExpress driver DLLs.. InstallShield Express is a setup toolkit that is bundled to Delphi.
InstallShield Express is certified for use with Delphi. It is based on the Windows Installer (MSI) technology.
InstallShield Express is not automatically installed when Delphi is installed, so it must be manually installed if it
will be used to create installation programs. Run the installation program from the Delphi CD to install InstallShield
Express. For more information on using InstallShield Express to create installation programs, refer to the
InstallShield Express on-line help.

Application Files, Listed by File Name Extension


The following types of files are likely to be needed for distribution with an application.

Type File Name Extension

Program files .exe and .dll

Package files .bpl and .dcp

Help files .hlp, .cnt, and .toc (if used) or any other Help files the application supports

ActiveX files .ocx (sometimes supported by a DLL)

Local table files .dbf, .mdx, .dbt, .ndx, .db, .px, .y*, .x*, .mb, .val, .qbe, .gd*
Table 1 – Application types list

Deploying Application Files. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 288 -
Deploying Database Applications
This module briefly discusses the various files required when deploying an application that connects to a database.

Delphi 2009 Application Development for Win32


- 289 -
Database Applications
Applications that access databases involve special installation considerations besides copying the application's
executable file onto the host computer. Database access is most often handled by a separate database engine, the
files of which cannot be linked into the application's executable file. The data files, when not created beforehand,
must be made available to the application. Multi-tier database applications require additional handling on
installation, because the files that make up the application are typically located on multiple computers.
Since several different database technologies (ADO, DBExpress, and InterBase Express) are supported, deployment
requirements differ for each of them. Regardless of which is being used, make sure that the client-side software is
installed on the system where the database application will be executed. ADO, DBExpress, and InterBase Express
also require drivers to interact with the client-side software of the database.
Database applications that use client datasets such as TClientDataSet or dataset providers require that midaslib.dcu
be included (for static linking when providing a stand-alone executable); when packaging the application (with the
executable and any needed DLLs), remember to include the Midas.dll file.
When deploying database applications that use ADO, make sure that either MDAC version 2.8 or later is installed
on the system where the application will be executed. MDAC is automatically installed with software such as
Windows 2000 and Internet Explorer version 5 or later. In addition, make sure the drivers for the database server
you want to connect are installed on the client. No other deployment steps are required.
When deploying database applications that use InterBase Express, ensure that the InterBase client is installed on the
system where the application will be executed. InterBase requires gds32.dll and interbase.msg to be located in an
accessible directory. No other deployment steps are required. InterBase Express components communicate directly
with the InterBase Client API and do not require additional drivers. For more information, refer to the Embedded
Installation Guide posted on the Borland Web site.
In addition to the technologies described here, there is the option to use third-party database engines to provide
database access. Consult the documentation or database engine vendor for the regarding redistribution rights,
installation, and configuration.

Deploying DBExpress Database Applications


DBExpress is a set of thin, native drivers that provide fast access to database information.
DBExpress applications can be deployed either as a stand-alone executable file or as an executable file that includes
associated DBExpress driver DLLs.
To deploy DBExpress applications as stand-alone executable files, the DBExpress object files must be statically
linked into the executable. This can be done by including the following DCUs, located in the lib directory:

Database Unit When to Include

dbExpINT Applications connecting to InterBase databases

dbExpORA Applications connecting to Oracle databases

dbExpDB2 Applications connecting to DB2 databases

dbExpMYS Applications connecting to MySQL 4.0.24 databases

MidasLib Required by DBExpress executables that use client datasets such as


TClientDataSet
Table 1 – DBExpress deployment as a stand-alone executable

Deploying Database Applications. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 290 -
Note: For database applications using Informix, a standalone executable cannot be deployed. Instead,
deploy an executable file with the driver DLL (listed in the following table).

Provided that a stand-alone executable is not being deployed, the developer can deploy associated DBExpress
drivers and DataSnap DLLs with the executable. The following table lists the appropriate DLLs and when to include
them:

Database DLL When to Deploy

dbxinf30.dll Applications connecting to Informix databases

dbxint30.dll Applications connecting to InterBase databases

dbxora30.dll Applications connecting to Oracle databases

dbxdb230.dll. Applications connecting to DB2 databases

dbxmss30.dll Applications connecting to MSSQL databases

dbxmys30.dll Applications connecting to MySQL 4.0.24 databases

Midas.dll Required by database applications that use client datasets

Table 2 – DBExpress deployment with driver DLLs

Deploying Database Applications. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 291 -
Delphi 2009 Application Development for Win32
- 292 -
Using P ackages
This module covers:
• Introduction to Runtime and Dynamic Packages
• Why Use Packages
• Structure of Packages
• Creating Packages
• Deploying Packages

Delphi 2009 Application Development for Win32


- 293 -
What is a Package?
A package is a special dynamic-link library used by applications, the IDE, or both. Packages are available in Delphi
3 and higher.
Packages allow placing portions of the application into separate modules that can then be shared across multiple
applications. Packages also provide a means for installing (custom) components into Delphi's VCL palette.
Therefore, two types of packages can be made by Delphi:
• Runtime packages - provide functionality when a user runs an application - they operate much like
standard DLLs.
• Design-time packages - used to install components in the Delphi IDE and to create special property editors
for custom components.
A single package can function at both design and runtime, and design-time packages frequently work by calling
runtime packages. To distinguish them from other DLLs, package libraries are stored in files that end with the .bpl
(Borland package library) extension.
Like other runtime libraries, packages contain code that can be shared among applications. For example, the most
frequently used VCL components reside in a package called vcl. Each time a new default VCL application is
created, it automatically uses vcl. When an application created this way is compiled, the application's executable
image contains only the code and data that are unique to it; the common code is in the vcl90.bpl runtime package. A
computer with several package-enabled applications installed on it needs only a single copy of vcl90.bpl, which is
shared by all the applications and the IDE itself.
Several runtime packages encapsulate VCL components while several design-time packages manipulate components
in the IDE.
The developer can build applications with or without packages. However, if custom components will be added to the
IDE, they must be installed as design-time packages.
Create your own runtime packages to share among applications. If you write Delphi components, they can be
compiled into design-time packages before being installed.

Design-time Packages
Design-time packages are used to install components on the IDE's Tool palette and to create special property editors
for custom components. Design time packages contain those components, property and component editors, experts,
etc., which are necessary for application design in the Delphi IDE. This type of package is used only by Delphi and
is never distributed with applications. Which ones are installed depends on which edition of Delphi is being used
and whether or not it has been customized. A list of which packages are installed on the system can be viewed by
choosing Component |.Install Packages.
The design-time packages work by calling runtime packages, which they reference in their Requires clause. For
example, dclstd references the VCL. dclstd itself contains additional functionality that makes many of the standard
components available on the Tool Palette.
In addition to preinstalled packages, your own component packages, or those from third-party developers, can be
installed in the IDE. The dclusr design-time package is provided as a default container for new components.

Runtime Packages
Runtime packages are deployed with applications. They provide functionality when a user runs the application.
To run an application that uses packages, a computer must have the application's executable file and all the packages
(.bpl files) that the application uses. The .bpl files must be in the system path for an application to use them. When
an application is deployed, the developer must ensure users have the correct versions of any required .bpl.

A false myth: a developer is not required to be a component developer to take advantage of packages.
Beginner Delphi programmers should try working with packages - they will get a better understanding of
how packages and Delphi work.

Using Packages. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 294 -
Why Use Packages?
Design-time packages simplify the tasks of distributing and installing custom components. Runtime packages, which
are optional, offer several advantages over conventional programming. By compiling reused code into a runtime
library, it can be shared among applications. For example, all applications – including Delphi itself – can access
standard components through packages. Since applications have no separate copies of the component library bound
into their executables, the executables are much smaller, saving both system and hard disk storage resources.
Moreover, packages allow faster compilation because only the code that is unique to the application is compiled
with each build.

Package Structure
Packages include the following parts:
• Package name
• Requires clause
• Contains clause

Naming packages
Package names must be unique within a project. If a package is named Stats, Delphi 2009 generates a source file for
it called Stats.dpk; the compiler generates an executable and a binary image called Stats.bpl and Stats.dcp,
respectively. Use Stats to refer to the package in the Requires clause of another package, or when using the package
in an application.

Requires clause
The Requires clause specifies other external packages that are used by the current package. At compile time, an
external package included in the Requires clause is automatically linked into any application that uses both the
current package and one of the units contained in the external package.
If the unit files contained in the package make references to other packaged units, the other packages should appear
in the package's Requires clause; if they do not appear there, add them. If the other packages are omitted from the
Requires clause, the compiler will import them into the package’s 'implicitly contained units'.

Note: Most packages created use the require clause. When using VCL components, the vcl package also
has to be included.

Contains clause
The Contains clause identifies the unit files that should be bound into the package. If you are writing your own
package, put the source code in pas files and include them in its Contains clause.

Avoiding circular package references


Packages cannot contain circular references in their Requires clause. This means that:
• A package cannot reference itself in its own Requires clause.
• A chain of references must end without a second reference being made to any of the packages previously
included in the chain. If package A requires package B, then package B cannot require package A; if
package A requires package B and package B requires package C, then package C cannot require package
A.

Handling Duplicate Package References


Duplicate references in a package's Requires clause – or the Runtime Packages edit box – are ignored by the
compiler. For programming clarity and readability, however, track and remove duplicate package references.

Avoiding Redundant Source Code Uses


A package cannot appear in the Contains clause of another package.

Using Packages. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 295 -
All units that are either directly included in a package's Contains clause, or indirectly included in any of those units,
are bound into the package at compile time.
A unit cannot be contained (directly or indirectly) in more than one of the packages that are used by the same
application, including the IDE. This means that if a package that contains one of the units in vcl (VCL) is created, it
can’t be installed in the IDE. To use an already-packaged unit file in another package, put the first package in the
second package's Requires clause.

Packages and Standard DLLs


Create a package when you want to make a custom component that is available through the IDE. Create a standard
DLL when you want to build a library that can be called from any application, regardless of the development tool
used to build the application. The following table lists the file types associated with packages:

File extension Contents

.dcp This binary image file consists in the actual compiled package. Symbol
information and additional header information required by the IDE are all
contained within the .dcp file. The IDE must have access to this file in order to
build a project.

.dll Code for dynamic link library. A dynamic-link library (DLL) is a collection of
routines that can be called by applications and by other DLLs. Like units, DLLs
contain sharable code or resources. However, a DLL is a separately compiled
executable that is linked at runtime to the programs that use it. Do not delete a
.dll file unless you’ve written it yourself.

.bpl or .dpl This is the actual design-time or runtime package. This file is a Windows DLL
with Delphi-specific features integrated into it. This file is essential for
deploying an application that uses a package. In version 4 and above, this is
'Borland package library'; in version 3, it's 'Delphi package library'.

.dpk This file contains the source code for a package, which is most often a collection
of multiple units. Package source files are similar to project files, but they are
used to construct special dynamic-link libraries called packages.

.pas In Delphi, PAS files are always the source code to either a unit or a form. Unit
source files contain most of the code in an application. The unit contains the
source code for any event handlers that are attached to the events of the form or
the components it contains. Edit .pas files using Delphi's code editor.
Table 1 – Package Files
At this point, you are probably curious about what could make a runtime package be chosen over of a DLL, or the
other way around.
Packages differ from DLLs in that they are specific to Delphi, that is, applications written in other languages can't
use packages created by Delphi. Packages provide more functionality to a Delphi developer than to the application
itself.
In general, DLLs are built in Delphi to store collections of custom procedures and functions that can be used by
many applications and/or different environments. Packages can contain units with code, components, forms, and
Delphi classes - this enables us to write object oriented code. What Packages can do and DLLs cannot is store the
complete custom Delphi components inside them.
Both DLLs and BPLs play a great role in code reduction. A primary reason for using packages or DLLs is to reduce
the size of applications. Beware not to assume it as a perennial true, because the packages and DLLs needed to ship
with the application can get to a quite large size. Note that the packages still have to be distributed; the primary VCL
package, vcl50.BPL, is almost 2MB in size alone.
However, if several Delphi applications that share the same package are distributed, it may result in less code being
distributed. Consider the benefit of giving users the option of downloading smaller versions of the application when

Using Packages. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 296 -
pieces of the application might already exist on their system (like standard Delphi BPLs). This should certainly urge
you to start deploying projects over the Internet.
Packages save memory. As a result of dynamic linking, only one copy of the VCL resides in the memory for all
Delphi applications that use runtime packages.

Static Linking and Dynamic Linking


When you write (and compile) a Delphi application, typically an executable file is generated - a standalone
Windows application. Unlike other developer tools, Delphi produces applications wrapped in compact exe files,
with no need for bulky runtime libraries (DLLs). This is known as static linking.
Static linking means that when a Delphi project is compiled, all the code that the application requires is directly
linked into the application's executable file. The resulting exe file contains all the code from all the units that are
involved in a project. Too much code, one might say. By default, the uses clause for a new form unit lists more than
5 units (Windows, Messages, SysUtils...). However, the Delphi linker is smart enough to link only the minimum of
code in the units actually used by a project.

Try this: start Delphi 2009, choose File | New | VCL Forms Application, and compile the created project;
this will produce an executable file of about 382 KB. Now go to Project | Options > Packages and check
the 'Build with runtime packages' check box. Compile and run. Now the exe size is around 16 KB. When
the ‘Build with runtime packages’ option is selected, Delphi dynamically links the libraries required by the
application.

Dynamic linking is like working with standard DLLs, that is, dynamic linking provides functionality to multiple
applications without binding the code directly to each application - any required packages are loaded at runtime. The
greatest thing about dynamic linking is that the loading of packages by the application is automatic. Neither you
have to write code to load the packages nor change the code.
By default, the 'Build with runtime packages' option is unchecked and every time a Delphi application is created, the
compiler links all the code the application requires to run directly into the application's executable file. The
application is a standalone program and doesn't require any supporting files (like DLLs) - that's why Delphi’s exe
files are so big.
One way of creating smaller Delphi programs is to take advantage of 'Borland package libraries', or BPLs (Delphi
Runtime packages) in short.

Creating a Package
To create a package:
• Choose File | New | Other, select the Package icon under Delphi Projects, and click on OK. The generated
package appears in the Project Manager. The Project Manager displays a Requires node and a Contains node
for the new package.
• To add a unit to the Contains clause, right-click on the Contains node in the Project Manager and select
Add. In the Add Unit page, type a .pas file name in the Unit file name edit box, or click on Browse to
browse for the file, and then click on OK. The selected unit appears under the Contains node in the Project
Manager. Additional units can be added by repeating this step.
• To add a package to the Requires clause, right-click on the Requires node in the Project Manager and select
Add Reference. In the Requires page, type a .dcp file name in the Package name edit box, or click on
Browse to browse for the file, and then click on OK. The selected package appears under the Requires node
in the Project Manager. Additional packages can be added by repeating this step.

In the Project Manager, right-click on the package and select Compile.

Compiling Packages
A package can be compiled from the IDE or from the command line. To recompile a package by itself from the IDE:
• Choose File | Open and select a package (.dpk or .dpkw).

Using Packages. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 297 -
• Click on Open.
• When the package opens:
• In the Project Manager, right-click on the package and choose Compile.
• In the IDE, choose Project | Build.

Note: Right-click on the package project nodes for options to compile, install, or build.
Insert compiler directives into the package source code. If the package is compiled from the command line, several
package-specific switches can be used.

Deploying Packages
Packages are deployed the same way as other applications. The files distributed with a deployed package may vary.
The bpl and any packages or dlls required by the bpl must be distributed. Files deployed with a package:

File Description

.pas Allows end users to access the class interfaces and source code of
package functions.

.dpk Allows end users to access the package structure.

.bpl Allows end users to link applications.


Table 2 – Files deployed with a package

Deploying applications that use packages


When distributing an application that uses runtime packages, make sure that users have the application's .exe file as
well as all the library (.bpl or .dll) files that the application calls. If the library files are in a different directory from
the .exe file, they must be accessible through the user's Path. You may want to follow the convention of putting
library files in the Windows\System directory. If InstallShield Express is used, the installation script can check the
user's system for any packages it requires before blindly reinstalling them.

Distributing packages to other developers


If you distribute runtime or design-time packages to other Delphi developers, be sure to supply both .dcp and .bpl
files. It is good to include .dcu files as well.

Using Packages. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 298 -
M odel-View -Controller P attern
This module covers:
• Introduction to the Model-View-Controller Pattern (MVC)
• Benefits and Disadvantages of the MVC
• Introduction to MVC components and their relationships to each other

Delphi 2009 Application Development for Win32


- 299 -
Introduction
Application development - particularly business application development - is becoming increasingly more complex
these days. Developers, architects, project managers, and users are forced to face complex data structures,
modifications to the business rules, user’s requirement changes, and new technologies. Modern enterprise
applications usually keep at least three layers: data access code, business logic code, and presentation code.
The separation between business logic and graphical interface is something that happens more in an abstract level
than in a technological level. This separation became simpler after the advent of object-oriented programming.
For large projects, such as business projects where code re-use and frequent maintenance are demanded, the logical
separation of these parts is highly recommended.
Several problems can arise when applications contain a mixture of data access code, business logic code, and
presentation code. Such applications are difficult to maintain, because the interdependencies between all of the
components cause strong ripple effects whenever a change is made. High coupling makes classes difficult or
impossible to re-use, because they depend on many other classes. Adding new data views often requires re-
implementing or cutting and pasting business logic code, which then requires maintenance in multiple places. Data
access code suffers from the same problem, being cut and pasted among business logic methods.
There are many possible ways of treating such complexity, but all of them can be grouped into two main principles:
using functional approaches and anticipating future necessities. Both approaches provide good techniques that can
be used in virtually any application development. These techniques are known as design patterns.
For this purpose, there is a design pattern that will optimize the application: it is called Model-View-Controller.
The primary goal of Model-View-Controller (MVC) is to isolate User Interface (UI) changes and prevent them from
requiring changes to the domain logic of the application. By making this separation, the UI can be changed without
making changes to the domain logic and vice versa.
MVC divides an application into three concerns:
• Model: encapsulates core application data and functionality domain logic
• View: obtains data from the model and presents them to the user
• Controller: receives and translates input to requests on the model or the view

Figure 1 - The MVC pattern


The separation into three concerns is inspired by an information processing model, where the controller represents
the system input, the model represents the processing and the view represents the output of the system.
It is important to note that both view and controller depend on the model. However, the model depends on neither
the view nor the controller. This is one of the key benefits of the separation. This separation allows the model to be
built and tested separately from the visual presentation. The separation between view and controller is secondary in
many rich-client applications and, in fact, many user interface frameworks implement the roles as an object. In Web

Model-View-Controller Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 300 -
applications, on the other hand, the separation between view (the browser) and controller (the server-side
components handling the HTTP request) is very well defined.
Model-View-Controller is a fundamental design pattern for the separation of user interface logic from business
logic. Unfortunately, the popularity of this pattern has resulted in a number of faulty descriptions. In particular, the
term "controller" has been used to mean different things in different contexts. Fortunately, the advent of Web
applications has helped resolve some of the ambiguity, because the separation between the view and the controller is
very apparent.

MVC Components
Model
The model encapsulates the functional core of an application: its domain logic. The goal of MVC is to make the
model independent from the view and controller, which together form the user interface of the application. An object
may act as the model for more than one MVC triad at a time.
Since the model must be independent, it cannot refer to either the view or controller portions of the application. The
model may not hold direct instance variables that refer to the view or the controller. It passively supplies its services
and data to the other layers of the application. There are some features about Model that will be described next.
Passive Model
• With a passive model, the objects used in the model are completely unaware of being used in the MVC
triad. The controller notifies the view when it executes an operation on the model that will require the view
to be updated.
• The passive model is commonly used in web MVC. The strict request/response cycle of HTTP does not
require the immediacy of an active model. The view is always completely re-rendered on every cycle,
regardless of changes.
Active Model
• In the active model, model classes define a change notification mechanism, typically using the Observer
pattern. This allows unrelated view and controller components to be notified when the model has changed.
Because these components register themselves with the model and the model has no knowledge of any
specific view or controller, this does not break the independence of the model.
This notification mechanism is what provides the immediate updating, which is the hallmark of a GUI application
using MVC.

View
The view obtains data from the model and presents it to the user. The view represents the output of the application.
The view generally has free access to the model, but should not change the state of the model. Views are read-only
representations of the state of the model. The view reads data from the model using query methods provided by the
model.
With an Active model, the view can register itself to receive notifications when the model changes, being then able
to present a more up to date version of the model.
Sometimes generic reusable view components can be arbitrarily connected to the model in a process known as
binding.

Controller
The controller receives and translates input to requests on the model or view.
Controllers are typically responsible for calling methods on the model that change the state of the model. In an
active model, this state change is then reflected in the view via the change propagation mechanism. In the passive
model, the controller is responsible for telling the view when to update.
In MVC, the controller is not a mediator between the view and the model. The controller does not sit in between the
model and the view. Both the controller and the view have equal opportunity to access the model. The controller

Model-View-Controller Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 301 -
does not copy data values from the model to the view; although it may place values in the model and tell the view
that the model has been changed.

Relationships between Components


View-Controller Relationship
In traditional MVC, views and controllers are tightly coupled. Each view instance is associated with a single unique
controller instance and vice versa. The controller is considered a Strategy the view uses for input. The view is also
responsible for creating new views and controllers.
It is logical that views and controllers are strongly related, as well as the input and output of an application are
strongly related. In most GUI MVC frameworks, the view and controller are simply merged into one object. This is
called Document View. The view and controller are combined as the view. The model is then known as a document.
A passive model shifts more responsibility into the controller, as it must notify the views when they should update.
The modern web usage of MVC shifts even more the traditional responsibilities of the view to the controller. The
controller becomes responsible for creating and selecting views and the view tends to lose responsibility for its
controller.
Sometimes the responsibility for creating and selecting views is delegated to a specific object; this is known as the
Application Controller pattern for web MVC and as the View Handler for GUI MVC.
The rigid request/response cycle of HTTP may make the Document View variant less popular in web applications
than in GUI applications, though the controller is still strongly related to the view. The HTTP request is handled by
the controller, the processing by the model, and the response is handled by the view.

Model-View Relationship
The view depends on the model. Changes to the model interface require parallel changes in the view.
It is very difficult to achieve a strict separation between model and view. For example, consider the requirement
“Show negative balances in red.” At first glance, this appears to be strictly an output requirement and a test might be
placed into the view in roughly this form:

if balance < 0 then red


This would violate the separation of concerns in MVC. Upon further analysis, it turns out that the real requirement is
“show overdrawn balances in red” and the definition of overdrawn =balance < 0= should be placed in the model, as
that is domain-specific. It is very easy for domain logic to migrate out of the model and into the view. Template
View contains further discussion on this issue.

Model-Controller Relationship
The Controller depends on the model. Changes to the model interface may require parallel changes to the controller.

Benefits of MVC
Substitutable User Interface
Different views and controllers can be substituted to provide alternate user interfaces for the same model. For
example, the same model data can be displayed as a bar graph, or a pie chart, or a spreadsheet.
Some examples:
• Read-only UI Expert, and novice-specific UI Different human languages
• Alternate input mechanisms: User specific themes
• Alternate output formats XML, HTML, etc
• Alternate form mechanisms HTML forms, XForms, PDF forms

Model-View-Controller Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 302 -
User interface components
Because MVC demands that the user interface of an application be structured into a hierarchy of objects and defines
a standard relationship between these objects, generic versions of these objects are possible. They are usually called
user interface components and no modern GUI environment is without a full complement of them, usually
combining view and controller into a single object.

Multiple Simultaneous Views of the Same Model


Multiple different views can be active at the same time. Each view simultaneously and independently presents the
same information from the model. This applies more to GUI MVC than to web MVC.

Synchronized Views
The change propagation mechanism insures that all views simultaneously reflect the current state of the model.

Easier User Interface Changes


Changes affecting just the user interface of the application logic become easier to make.

Easier Testing
With MVC, it can be easier to test the core of the application, as encapsulated by the model.

Drawbacks to MVC
Complexity
The MVC pattern introduces new levels of indirection and therefore slightly increases the complexity of the
solution. It also increases the event-driven nature of the user-interface code, which can become more difficult to
debug.

Cost of Frequent Updates


Decoupling the model from the view does not mean that developers of the model can ignore the nature of the views.
For example, if the model undergoes frequent changes, it could flood the views with update requests. Some views,
such as graphical displays, may take some time to render. As a result, the view may fall behind update requests.
Therefore, it is important to keep the view in mind when coding the model. For example, the model could batch
multiple updates into a single notification to the view.

Close Coupling Views and Controllers to Model


Changes to the model interface require parallel changes in the view and may require additional changes to the
controller. Certain code changes become more difficult.

Potential for Excessive Updates


The change propagation mechanism can be inefficient when frequent model changes require many change
notifications. This is generally not much of a problem if a passive model is used.

Close Coupling between View and Controller


Strict separation is difficult, if not impossible.

Model-View-Controller Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 303 -
Delphi 2009 Application Development for Win32
- 304 -
I ntroduction to TeeChart
TeeChart Standard was incorporated in Delphi 3 as the default charting solution. Since then, it has been a powerful
and flexible tool to create and manage charts with the Delphi environment.

Delphi 2009 Application Development for Win32


- 305 -
TeeChart Feature Matrix

Feature TeeChart TeeChart


Standard v8 Pro v8

Chart control with headers, footers, 4 axes, legend, walls, 3D, paging √ √

2D, 3D orthogonal, pseudo-3D √ √

Printing and Print Preview window √ √

Export to picture (Bitmap and Metafile) √ √

Basic functions (Add, Multiply, etc) √ √

Database DBChart √ √

Standard Series styles (Line, Bar, Area, Pie, Fast-Line, Point, Horiz.Bar) √ √

Gantt series style √ √

Bubble, Arrow, Shape series styles √ √

Basic gradients √ √

Decision Cube compatible √ √

Win32 VCL Support √ √

C++ Builder Support √ √

.NET VCL Support √ √


(Borland Developer Studio 2006, 2007, Delphi 8 and 2005)

C# and Delphi WinForms √ √

True 3D with perspective, 360 degrees √ √

Median, Mode and y=f(x) functions √ √

TDraw3D general purpose 3D canvas √ √

Cross-tab Series datasource √ √

Hundreds of small improvements and properties in all Chart and Series styles sub- √ √
components

Horizontal Area and Horizontal Line series styles √ √

Extended gradients and transparency √ √

Legend Symbols and Check-Boxes √ √

Right chart wall √ √

QuickReport compatible √ √

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 306 -
Feature TeeChart TeeChart
Standard v8 Pro v8

Intraweb compatible √ √

Treeview enabled editor √ √

ButtonColor, ButtonPen, ComboFlat controls √ √

ChartGrid for data editing √ √

Depth axis at left-top √ √

Export native template (*.tee) √ √

Import from template (*.tee) √ √

Series Datasources √
(Dataset, SingleRecord, Text, XML)

TSeriesDataSet component √

Multi-language translations (English and 28 other idioms) √

Extended Series styles: Arrow, Bezier, Donut, Gauge, Map, Polar, Polar Bar, √
Pyramid, Radar, Smith

3D Series styles: Bubble 3D, ColorGrid, Contour, Point 3D, Water Fall, Surface, √
Tower, Triangle Surface, Vector 3D, Waterfall

Financial Series styles: Candle, Point & Figure, Volume √

Statistical Series styles: BoxPlot, Error, Error Bar, Funnel, Horizontal BoxPlot, √
High-Low, Histogram, Horizontal Histogram, VolumePipe

Other Series styles: Bar 3D, Bar Join, Big Candle, Calendar, Clock, DeltaPoint, √
ImageBar, ImagePoint, Line Point, Wind Rose

Export to picture (PNG, GIF, JPEG, TIFF, SVG, PDF, VML) √

Extended Functions: Correlation, Count, Cross Points, Cumulative, Curve Fitting, √


Downsampling, Exponential Average, Exponential Trend, Logarithmic Trend,
Performance, Perimeter, Root Mean Square, Smoothing, Standard Deviation,
Trend, Variance

Custom unlimited multiple Axes √

Financial Functions: ADX, Bollinger bands, Close Location Value, Commodity √


Channel Index, Compression, Exp.Moving Average, MACD, Momentum,
Momentum Division, Moving Average, On Balance Volume, R.S.I., Stochastic,
Volume Oscillator

TeeCommander toolbar √

Visual Themes √

ChartListBox control √

TeeTree components √

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 307 -
Feature TeeChart TeeChart
Standard v8 Pro v8

OpenGL 3D rendering √

Export data (text csv, xml, html) √

Chart Tools (Cursors, Annotations...) √

TeeChartOffice with source code √

Full 100% Delphi Source Code optional available √

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 308 -
TeeChart Components
TeeChart gives developers a way to include charts within their Delphi executables. See below the Delphi Tool
Palette with the TeeChart Standard tab selected (Figure 1):

Figure 1 – TeeChart Components

Component Description

Charts are central components in the TeeChart library. TChart is a VCL


TPanel derived component hugely expanded to include capabilities
specific to charting and graphing purposes. TChart is the ParentChart
component for Series. All Series have a ParentChart property that must
refer to a TChart or TDBChart.

TDBChart derives from TChart/TCustomChart and inherits all TChart


functionalities. When a Chart Series is connected to a TDBChart
component, TDBChart checks the Series DataSource property.

This component enables the connection of a DBGrid to


TChart/TDBChart's data. The TSeriesDataset component may be
activated to connect to any TeeChart Series on a Form. Use a
TDatasource component to connect to the TSeriesDataset in the same
way as if it were a TTable or TQuery. A DBGrid may then be connected
to the TDatasource to access the Series data.

VCL TDataSet derived component that contains all series data in Chart.

The DBCrossTabSource component is a non-visual component used to


automatically create charts with multiple series from database data. To
customize a DBCrossTabSource, use either the Object Inspector or the
custom editor dialog. To access the dialog, set the Series property to the
desired series and go to the series datasource tab in the normal Chart
editor dialog.

This Button control is internally used in Chart Editor dialogs. It is similar


to any other TButton control, except that it includes a property to "link"
the button to a Color property. The Color is used to display a graphical
filled rectangle at the right side of the button. Clicking on the button will
automatically show the Color editor dialog used to change the color.

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 309 -
Component Description

This component is internally used in Chart Editor dialogs. It’s similar to


any other VCL TButton control, except that it includes a property to
"link" the button to a TChartPen property. The Pen is used to display a
graphical line at the right side of the button. Clicking on the button will
automatically show the Chart Pen editor dialog (also found in the
TeePenDlg unit).

A special button that automatically shows the Gradient editor dialog.


Before using this button, its Gradient property must be set to an existing
gradient, using the LinkGradient method.

TDraw3D is a panel that offers 3D Canvas drawing capabilities.


TDraw3D's ancestor, TCustomTeePanel, is also TChart’s ancestor. Many
of the TDraw3D properties are also common to TChart. For example,
this component has a sub-set of the Chart features, such as printing,
printing properties, copying to clipboard, margins, 3D Canvas, export to
bitmap, metafile, jpeg, etc. The TDraw3D Canvas is provided with 3D
capabilities, such as Zoom, Rotation, Elevation, Scrolling, etc. It can be
rendered using 3D OpenGL, and provides the same Printing,
CopyToClipboard, "SaveTo..." exporting methods, and the same
DoubleBuffered display, Panel Margins, etc, as the Chart components.

A derived ComboBox class that displays the combo border when the
cursor is hovered over it.

TImage derived visual control that provides an additional property called


Filters, which the developer can use at design and runtime to add effects
to the image.
Table 1 – List of TeeChart Components

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 310 -
The TeeChart Editor
Many of the steps necessary for defining a TChart or TDBChart are the same. This section describes the common
steps needed to start defining either of the two types of Chart.
Create a new application and place a TChart component on it (drag it over from the component palette). Move the
corners of the new TChart component as far apart as desired. This will help better visualize the content of the new
Chart while it is being defined, yet you’re still able to set the Chart back to a more appropriate size afterwards. Move
the cursor over the new Chart and right-click on it. A menu including the ‘Edit Chart’ option is shown.

Figure 2 - TeeChart Menu


Select the Edit Chart option to edit the Chart, and define and populate its data Series. The Chart page (1st page) of
the Chart editor contains definition information for the Chart. It includes sections to define general and other more
specific Chart parameters. Some parameters won’t take effect until some data Series defined is defined in the Chart.
Try modifying a parameter - the Title, for example - and see that it is updated in real time on the Chart.
To see some data Charted, create a data Series. Press the Add button in the Series tab section of the Chart page.
TeeChart will display a gallery of Series types. Select one of them to add into the Chart. Remember that its type can
be changed later if you decide you’d rather visualize data in a different way.

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 311 -
Figure 3 - TeeChart Gallery
Use the mouse or the arrow keys from the keyboard to select a Series type and then press ‘OK’. Double-clicking a
series achieves the same result. The Series type is then automatically added to the Chart, and the Chart editor
displays the new configuration tab that was added for the new Series.
Suppose a Pie Series has been selected. The editor then looks like the following figure:

Figure 4 - Series Definition


TeeChart has added the Series to the Chart. It has also added some random values. Although not valid at runtime,
such values allow visualizing the appearance of the Series in the Chart at design-time. They’ll make it easier to
follow the changes being performed. Press the F9 key to compile the project. The project should compile to display

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 312 -
an empty Chart. Remember that the random values created by TeeChart are disregarded at runtime, which is why
you now have to go back to the Chart Editor and write your own code to add data values.
Selecting the Series tab allows editing Series. After that, add data to the Series. There is a TChart on the form and a
Series has been added to it. You are ready to populate the Series. Now type some Delphi code to add point values
programmatically.
Suppose the added Series was a Pie Series. You could populate the Series as shown below, remembering, for the
following code to work, to leave the default Series name – Series1 – untouched:
Place a TButton on the Form and go to its OnClick event. Copy the following code to the Button1.OnClick event:

With Series1 do
Begin
Add( 40, 'Pencil' , clRed ) ;
Add( 60, 'Paper', clBlue ) ;
Add( 30, 'Ribbon', clGreen ) ;
end;
Return to the code and press F9 again to run the project. Press Button1 to see the Pie Chart appear, meaning that the
code works.

Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 313 -
Delphi 2009 Application Development for Win32
- 314 -
I ntroduction to XM L

Delphi 2009 Application Development for Win32


- 315 -
XML Overview
What it is (and what it’s not)
The Extensible Markup Language (XML) is a simple, very flexible text format derived from SGML (ISO 8879).
Originally designed to meet the challenges of large-scale electronic publishing, XML is also playing an increasingly
important role in the exchange of a wide variety of data on the Web and elsewhere.
It is a set of guidelines that can be used to put structured data into text files that are easily generated and read by a
computer. Files structured according to these guidelines are also called XML. Because XML is text, you can create
and edit it without special interfaces. The official guidelines ensure that the text is unambiguous, extensible,
supports internationalization / localization, and is platform-independent.

<memo>
<to>Terry</to>
<from>Noah</from>
<topic>Reminder</topic>
<body>Do not forget to write courseware this week.</body>
</memo>
The number of applications that are currently being developed based on or making use of XML documents is truly
amazing (particularly when considering that XML is not that old)! For such purposes, the word "document" refers
not only to traditional documents, like this one, but also to the myriad of other XML "data formats". These include
vector graphics, e-commerce transactions, mathematical equations, object meta-data, server APIs, and a thousand
other kinds of structured information

Figure 1 – How Applications work with XML


An XML document does not perform any action. It is pure information wrapped in user-defined XML tags. The
developer must write or employ a piece of software to send it, receive it, and display it. Even though XML files are
text files, they really aren’t meant to be read by humans. They are text files because text is a cross-platform format
that allows debugging applications more easily.
Tags can also contain attributes - additional information included as part of the tag itself - within the tag's angle
brackets. The following example shows an email message structure that uses attributes for the "to", "from", and
"subject" fields:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 316 -
<message to="[email protected]" from="[email protected]"
subject="XML Is Really Cool">
<text>
XML is cool or not?
</text>
</message>
As in HTML, the attribute name is followed by an equal sign and the attribute value, while multiple attributes are
separated by spaces. Unlike HTML, however, commas between attributes are not ignored in XML (they generate an
error, instead).
There is the option to use a simple text editor to create an XML file and write code to interpret it. That’s analogous
to using Notepad to type a spreadsheet doc with all the meta tags for column and row placement, and then creating a
custom program to read the file into a grid. Today, there are tools (editors) for creating XML and components for
reading it. This allows creating the content of the XML, rather than being concerned about the format.

Does Size Matter?


Because XML is a text format, XML files are nearly always larger than comparable binary formats. That was a
conscious decision by the developers of the XML standard. Disk space isn’t expensive anymore, and zip programs
can compress files quickly. Programs that compress nearly all platforms can be found everywhere, and they are
usually free. Additionally, web communication protocols can compress data on the fly, thus saving bandwidth as
effectively as a compressed binary format.

XML vs. HTML


Development of XML began in 1996. There has been a W3C standard (http://www.w3.org) since February 1998.
Thus, it is rather immature technology, but the concept isn’t very new. Before XML there was SGML, widely used
for large documentation projects and HTML, the development of which started in 1990. The designers of XML took
the best parts of SGML, guided by their experiences with HTML, and produced XML.
XML is designed to describe data and to focus on what data is, while HTML is designed to display data and to focus
on how data looks. Thus, HTML is about displaying information, XML is about describing information. Like
HTML, XML makes use of bracketed tags and attributes. However, while HTML specifies what each tag and
attributes means and how the text between them will look in a browser, XML only uses tags to delimit pieces of
data.
The tags used to markup HTML documents, and the structure of HTML documents, are predefined . HTML
documents can only use tags that are defined in the HTML standard (like <p> and <h1> ….). XML grants the ability
to define your own tags and document structure. The tags in the example above (as <memo> and <form> ), are not
defined in any XML standard. These tags were created by the author of the XML document. The display of the data
is completely up to the application that reads it. So, if there is a “<p>” in an XML file, it is not an HTML paragraph
mark. Depending on the context, it might be a price, a pedigree, a pilot, or any other piece of data.
Lastly, you’ll find that the rules for XML files are much stricter than those for HTML. A forgotten tag or attribute
without quotes make the files unusable. In HTML, such practice is often tolerated. The official XML states that
applications are not allowed to try second-guessing the author of a broken XML file; if the file is invalid, an
application has to immediately stop and issue an error.

Where’s the Data?


When HTML is used to display data, it is stored within the HTML text. With XML, data can be stored in separate
files in disparate locations. This way, the developer can concentrate on using HTML for data layout and display, and
be confident that changes to the underlying data will not require any changes to the HTML. Additionally, for simple
web pages, XML data can be stored inside HTML pages as “data islands”. The developer still concentrates on using
HTML only for formatting and displaying the data.
Moreover, XML can be used to store data in files or in databases, thus employing a generic format that other
programmers can access. Word processors, spreadsheet applications, and databases can already save their data in
XML; in the future these types of applications may be able to read each other’s data in a pure text format, without

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 317 -
any conversion utilities in between. Other clients and applications could access XML files as data source, just like
they access databases.

XML is the Basis of a Family of Technologies


Surrounding XML 1.0 there is a growing set of extensions that provide sets of tags & attributes, or guidelines for
specific tasks. For example, MathXML is a low-level specification for describing mathematics as a basis for
machine to machine communication. XQuery, another example, is a query language for XML, which provide
flexible query facilities to extract data from real and virtual documents on the Web. There are a host of others. The
Wireless Markup Language (WML) is used to markup Internet applications for handheld devices like mobile
phones. And, as will be seen, XSL is the advanced language for expressing style sheets for displaying XML data.

XML: License-free, Platform-independent and Well-


supported
Choosing to employ XML is a bit like using SQL for databases: you still have to build your own databases and your
own programs / procedures to manipulate it. Like SQL, by choosing XML as the basis for your project, you buy into
a large and growing community of users and tools. In fact, there may be one already doing exactly what you need.
And because XML is a W3C technology, it is license-free. You can build your own software around it without
paying anybody anything. The large and growing support means that you are also not tied to a single vendor.
The World Wide Web Consortium (W3C) develops interoperable technologies, (specifications, guidelines, software,
and tools), to lead the Web to its full potential as a forum for information, commerce, communication, and collective
understanding.

How Can You Use XML?


There are several basic ways to make use of XML:
• Traditional data processing, where XML encodes the data for a program to process.
• Document-driven programming, where XML documents are containers that build interfaces and applications
from existing components.
• Archiving -- the foundation for document-driven programming, where the customized version of a
component is saved (archived) so it can be used later.
• Binding, where the DTD or schema that defines an XML data structure is used to automatically generate a
significant portion of the application that will eventually process that data Basic XML.

Traditional Data Processing


XML is fast becoming the data representation of choice for the Web. It's terrific when used in conjunction with
network-centric Java-platform programs that send and retrieve information. So, a client/server application, for
example, could transmit XML-encoded data back and forth between the client and the server.
In the future, XML is potentially the answer for data interchange in all sorts of transactions, as long as both sides
agree on the markup to use (for example, should an email program expect to see tags named <FIRST> and <LAST>,
or <FIRSTNAME> and <LASTNAME>?). The need for common standards will generate a lot of industry-specific
standardization efforts in the years ahead. In the meantime, mechanisms that let you "translate" the tags in an XML
document will be important. Such mechanisms include projects like the RDF initiative, which defines "meta tags",
and the XSL specification, which lets you translate XML tags into other XML tags.

Document-Driven Programming (DDP)


The newest approach to using XML is to construct a document that describes how an application page should look.
The document, rather than simply being displayed, consists of references to user interface components and business-
logic components that are "hooked together" to create an application on the fly.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 318 -
Binding
Once you have defined the structure of XML data using either a DTD or one of the schema standards, a large part of
the processing you still have to do needs to have been previously defined. For example, if the schema says that the
text data in a <date> element must follow one of the recognized date formats, then one aspect of the validation
criteria for the data has been defined - it only remains to write the code. Although a DTD specification cannot follow
the same level of detail, a DTD (like a schema) provides a grammar that tells which data structures can occur, and in
what sequences. That specification tells how to write the high-level code that processes the data elements.
But when the data structure (and possibly format) is fully specified, the code needed to process it can just as easily
be automatically generated. That process is known as binding - creating classes that recognize and process different
data elements by processing the specification that defines those elements. Later on, you’ll probably find yourself
using the data specification to generate significant chunks of code, thus freeing you to focus on programming the
unique aspects of the application.

Archiving
The Holy Grail of programming is the construction of reusable, modular components. Ideally, they'd be taken off the
shelf, customized, and plugged together to construct an application, with a bare minimum additional coding and
additional compilation.
The basic mechanism for saving information is called archiving. A component is archived by writing it to an output
stream in a way that you can re-use it later. You can then read it in and instantiate it using its saved parameters. (For
example, if you saved a table component, its parameters might be the number of rows and columns to display).
Archived components can also be shuffled around the Web and used in a variety of ways.
When components are archived in binary form, however, there are some limits to the kinds of changes that can be
made to the underlying classes if you want to retain compatibility with previously saved versions. Could you modify
the archived version to reflect the change, the problem would be solved. But that's hard to do with a binary object.
Such considerations have prompted a number of investigations into using XML for archiving. But if an object's state
were archived in text form using XML, then anything and everything in it could be changed as easily as you can say,
"search and replace".
XML's text-based format could also make it easier to transfer objects between applications written in different
languages. For all of these reasons, XML-based archiving is likely to become an important force in the not-too-
distant future.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 319 -
Basic XML
XML Syntax
Unlike HTML, the XML tags identify the data, rather than specifying how to display it. Where an HTML tag says
something like display the data in bold font (<b>...</b>), an XML tag acts like a field name in the program.
In the same way that you define the field names for a data structure, you are free to use any XML tags that make
sense for a given application. Naturally, though, for multiple applications to use the same XML data, they have to
agree on the tag names they intend to use.

<?xml version=”1.0”?>
<message>
<to>[email protected]</to>
<from>instructor@instructor’sAddress.com</from>
<subject>Delphi AppDev for Win32 </subject>
<text>
You are in the Delphi AppDev for Win32´s course.
</text>
</message>
XML documents use a self-describing and simple syntax. The first line in our document – the XML declaration –
defines the XML version being used. In this case the document conforms to the 1.0 specification of XML. The next
line describes the root element of the document, as if it were saying, “It is a messaging application”. The next lines
describes the children elements of the root (to, from, subject, text).
When using XML, the closing tag cannot be omitted. In HTML, some elements do not have a closing tag. The
following is valid HTML code:

<p> This is a paragraph


<P> This is another paragraph
In XML, all elements must have a closing tag and, unlike HTML, those tags are case sensitive:

<P>This is a incorrect!</p>
<p>This is correct</p>
In HTML, some elements can be improperly nested within each other:

<b><i>This text is bold and italic</b></i>


Not in XML, though. All XML elements must be properly nested within each other:

<b><i>This text is bold and italic</i></b>

Notice in the original example that the XML declaration did not have a closing tag.
This is not a mistake. The declaration is not a part of the XML document itself. It is not an XML element,
and it should not have a closing tag.

In XML language, the white space in the document is not truncated. This is different from HTML, which reads a
sentence like this:

Hello my name is Jefferson


and displays it like this:

Hello my name is Jefferson,


It’s so because HTML reduces multiple, consecutive white space characters to a single white space.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 320 -
The first tag in an XML document is the root tag. All XML documents must contain a single tag pair to define the
root element. All other elements must be nested within the root element. Elements can have sub-elements (children).
Sub-elements must be correctly nested within their parent element:

<root>
<child>
<subchild>……</subchild>
</child>
</root>
Expanding our original example, XML elements can have attributes in name/value pairs just like in HTML. In
XML, the attributes’ value must always be quoted:

<?xml version=”1.0”?>
<message>
<to>[email protected]</to>
<date sent=”12/09/2008”></date>
<from>instructor@instructor’sAddress.com</from>
<subject>Delphi AppDev for Win32 </subject>
<text>
You are in the Delphi AppDev for Win32´s course
</text>
</message>
Also note that in XML the white space in the document is not truncated.

You are in the Delphi AppDev for Win32´s course


This is different from HTML, which displays the sentence as seen below:

You are in the Delphi AppDev for Win32´s course


Lastly, in Windows applications a new line in the text is normally stored as a pair of CR/LF (carriage return, line
feed) characters. In UNIX applications, a new line is normally stored as a LF character. Some applications use only
a CR character to store a new line. In XML, a new line is always stored as LF.

Comments
The syntax for writing comments in XML is similar to that of HTML.
<! This is a comment >
There is another type of comment in XML:
/// this is a comment

Extensibility
All XML documents can be extended to carry more information without impacting programs that read them.

<?xml version=”1.0”?>
<message>
<to>[email protected]</to>
<from>instructor@instructor’sAddress.com</from>
<subject>Delphi AppDev for Win32 </subject>
<text>
You are in the Delphi AppDev for Win32´s course.
</text>
</message>
You could create a program that reads the message and procedures output such as:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 321 -
Message:
To: [email protected]
From: instructor@instructor’sAddress.com
You are in the Delphi AppDev for Win32’s course.
The application should still be able to find the <to>, <from> and <text> elements in the document and create the
same output.

Relationships
Imagine this fragment of a schedule of class offerings:
Product: Delphi
Class: Delphi AppDev for Win32’s course
01/02/2009
02/15/2009
01/30/2009
This XML document describes the schedule fragment:

<?xml version=”1.0”?>
<schedule>
<product> Delphi
<specifics version =”2009” OS=”Windows Vista” </specifics>
<title> Delphi AppDev for Win32’s course
<offering>01/02/2009</offering>
<offering>02/15/2009</offering>
<offering>01/30/2009</offering>
</title>
</product>
</schedule>

Elements – Lineage and Content


An XML element is everything from the element’s start tag (including it) to the element’s end tag (including it).
Here, schedule is the root element, product is a child element of schedule, title and specifics are child
elements of product, offerings are child elements of a title (their parent). Specifics and title are
sibling elements with the same parent, product.
An element can have:
• element content
• mixed content
• simple content
• or empty content
An element can also have attributes. Attributes are additional information included as part of the tag itself, within
the tag's angle brackets.
In the example above, schedule has element content, because it contains other elements. Title has mixed
content because it contains both text and other elements. Offering has a simple content (or text content) because it
contains only text. Specific has empty content, because the element itself carries no information. It does,
however, have 2 attributes: version and OS, with the values “2009” and “Windows Vista”, respectively.
A really big difference between XML and HTML is that an XML document is always constrained to be well
formed. There are several rules that determine when a document is well-formed, but one of the most important is

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 322 -
that every tag has a closing tag. So, in XML, the </to> tag is not optional. The <to> element is never terminated by
any tag other than </to>.
Sometimes, though, it makes sense to have a tag that stands by itself. For example, you might want to add a "flag"
tag that marks a message as important. A tag like that doesn't enclose any content, so it's known as an "empty tag".
You can create an empty tag by ending it with /> instead of the regular ending tag.

Naming XML elements


XML elements must follow these naming rules:
• Names can contain letters, numbers, and other characters.
• Names must not start with a number or other punctuation characters.
• Names must not start with the sequence of letters XML (or xml or Xml …).
• Names cannot contain spaces.
Carefully choose the element names and follow these simple guidelines:
Make names descriptive. Names with an underscore separator are nice.
Examples: <first_name>, <last_name>.
Avoid using “-“ and “.” in names. This could backfire if the software tries to subtract name from first (first-name) or
thinks that “name” is a property of the object “first” (first.name).
• Names should be short and simple: <book_title> NOT <the_title_of _the_book>.
• XML documents are often based on a corresponding database, in which fields exist corresponding to
elements in the XML document. Use the naming rules of the fields in the database for the elements in the
XML documents.
• Non-English letters like é, á, and ó are perfectly legal in XML element names.
• The “:” should be used in element names because it is generally reserved for namespaces, (more on that
topic later).

XML Attributes
As previously seen, XML elements can have attributes. In HTML you might see:

<IMG SRC=”Athena.jpg”>
The SRC attributes provide additional information about the IMG element. Similarly, in XML you simply attribute
to provide additional information about elements that are part of the data. In the example, the file type is irrelevant
to the data, but important to the software that wants to manipulate the XML element:

<file type=”jpg”>Athena</file>
The values of the attributes must always be enclosed in either single or double quotes:

<person weight=”160”> OR <person weight=’160’>


Double quotes are the most common, but sometimes, (when the values of such attributes themselves contain quotes),
it is necessary to nest quotes:

<person name=’Johnny’ “Quest” ‘Jones’>

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 323 -
Elements vs. Attributes
Data can be stored in child elements or in the attributes.

<product name=”Delphi” version=”2009”>


<title> Delphi AppDev for Win32’s course </title>
</product>
<product> Delphi
<version>2009</version>
<title> Delphi AppDev for Win32’s course </title>
</product>
In the first part of the example above, name and version are attributes; in the second part, version is a child element.
Both examples provide the same information. There are no rules about when to use attributes, or about when to use
child elements. Here are some of the problems faced when using attributes:
• attributes cannot contain multiple values (child elements can).
• attributes are not easily expandable (for future changes).
• attributes cannot describe structures (child elements can).
• attributes are more difficult to manipulate by program code.
• The values of the attributes are not easy to test against a DTD (document type definition)
Attributes are handy in HTML, but try to avoid them in XML. If the attributes are used as containers for data, you’ll
end up with documents that are difficult to read and maintain. Use a child element if the information you’re dealing
with is data, especially when it reflects fields in an underlying database.

Well Formed and Valid XML documents


A “Well Formed” XML document holds correct XML syntax; it is a document that conforms to the XML syntax
rules.

<?xml version="1.0" encoding="UTF-8"?>


<note>
<to>CodeGear</to>
<from>Student</from>
<heading>Enjoy</heading>
<body>Thank you and enjoy this course.</body>
</note>
A “Valid” XML document is a “Well Formed” XML document, which also conforms to the rules of a Document
Type Definition (DTD):

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE note SYSTEM "InternalNote.dtd">
<note>
<to>CodeGear</to>
<from>Student</from>
<heading>Enjoy</heading>
<body> Thank you and enjoy this course.</body>
</note>
The purpose of a Document Type Definition is to define the legal building blocks of an XML document. It defines
the document’s structure with a list of legal elements. Unfortunately, DTDs are beyond the scope of our discussion.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 324 -
XSL
Because HTML uses predefined tags, the meanings of tags are understood. The <p> element defines a paragraph
and the <h1> element defines a heading, and the browser knows how to display these elements. You can add display
style characteristics to HTML elements using CSS (Cascade Style Sheets). With CSS you can tell the browser to
display each element using a special font or color.
Because XML does not have predefined tags (any desired tags can be used), the meaning of tags is not understood
by the browser. The <table> tag could mean an HTML table or maybe a piece of furniture. Because of the nature of
XML, the browser does not know how to display an XML document.

XSL – The Style Sheet of XML


In order to display XML documents, you need a mechanism to describe how the document should be displayed. CSS
could be used, but XSL is the preferred style sheet language of XML, and it is far more sophisticated than CSS.
The XSL (Extensible Stylesheet Language) standard has three parts: XSLT (the transformation standard, described
next), XSL-FO (the part that covers formatting objects, also known as flow objects), and XPATH (for navigating in
XML documents). XSL-FO grants the ability to define multiple areas on a page and then link them together. When a
text stream is directed at the collection, it fills the first area and then "flows" into the second when the first area is
filled. Such objects are used by newsletters, catalogs, and periodical publications.
Think of XSL as a language that can be used to transform XML into HTML, to filter and sort XML data, to address
parts of an XML document, to format XML data based on the data value (like displaying negative numbers in red),
and to output XML data to different media - screen, paper or sound, for instance.
XSL is a standard recommended by the World Wide Web Consortium. The first two parts of the language became a
W3C Recommendation in November 1999. The full XSL Recommendation, including XSL formatting, became a
W3C Candidate Recommendation in November 2000.

Using the XSLT – XSL Transformation


The XSLT Transformation standard is essentially a translation mechanism that allows specifying what to convert an
XML tag into, so that it can be displayed (for example, in HTML). Different XSL formats can then be used to
display the same data in different ways, for different uses.
You can use XSLT to add completely new elements into the output file, or remove elements. Moreover, it can be
used to rearrange and sort the elements, and test and make decisions about which elements to display.
This transformation process is commonly described as XSL using XSLT to transform an XML source tree into an
XML result tree (or an XML source document into an XML result document). In the transformation process, XSLT
uses XPATH to define parts of the source document that match one or more predefined templates. When a match is
found, XSLT transforms the matching part of the source document into the result document. The parts of the source
document that do not match a template will generally end up unmodified in the result.

XML, XSL and Browsers


In order to process an XML document using XSL, you need an XML parser with an XSL Engine. Internet Explorer
5.0 was the first available browser with this capability. MSXML 2.0 is the name of the XML parser that originally
shipped with IE 5.0. But, XSLT in Internet Explorer 5 is NOT compatible with the official W3C XSL
Recommendation.
From its version 6 on, Microsoft Internet Explorer is the web browser which has full XML support, including
Namespaces, Style sheets in CSS, and XSLT 1.0. XML Parser 3.0 in Internet Explorer 6.0 and Windows XP is
based on both W3C XSLT 1.0 and W3C XPath 1.0 Recommendations. If you are serious about learning XSLT, you
should upgrade at least to Internet Explorer 6.0.
Firefox 1.0.2 supports XML and XSLT (and CSS). Firefox 3.0, the new version improves basic parsing,
XML,DOM, XSLT, SVG, and more.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 325 -
Transforming an XML Document
To understand the transforming XML Document, an XML document will be used (“cdcatalog.xml”):

<?xml version="1.0" encoding="UTF-8"?>


<?xml:stylesheet type=”text/xsl” href=”my_catalog.xsl”?>
<catalog>
<cd>
<title>Big Ones</title>
<artist>Aerosmith</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1986</year>
</cd>
<cd>
<title>The Number of The Beast</title>
<artist>Iron Maiden</artist>
<country>U.K.</country>
<company>Sony</company>
<price>10.90</price>
<year>1987</year>
</cd>
</catalog>
XSL uses one or more templates to define how to output XML elements. A match attribute is used to associate the
template with an XML element. The match attribute can also be used to define a template for a whole branch of the
XML document.
Look at the following XSL Style Sheet. It contains a template to output our cdcatalog.xml (Figure 2):

<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border=’1’>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<tr>
<td>.</td>
<td>.</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

To generate the example XML above, use NOTEPAD or the text editor of your preference. Save the file as
cdcatalog.xml

Because the style sheet itself is an XML document, it begins with an XML declaration:

<?xml version=”1.0” ?>


Use an xsl:stylesheet tag in the second line to define the beginning of the template. The template attribute
match=’/’ associates, (matches), the template to the root (/) of the XML source document.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 326 -
The rest of the document comprises the template itself, except for the last two lines, which tag the end of the
template and the end of the style sheet.
So far, the result of our transformation will be a little disappointing because no data was copied from the XML
document to the output:

Figure 2 – The transformation of an XML


Use the XSL <xsl:value-of> element to select XML elements into the output stream of the XSL
transformation:

<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border=’1’>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<tr>
<td><xsl:value-of select=”catalog/cd/title”/></td>
<td><xsl:value-of select=”catalog/cd/artist”/></td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Note the syntax for the select attribute’s value. It is called an XSL Pattern (Figure 3). It works like navigating a file
system where a forward slash (/) selects subdirectories. The result of our accomplished transformation will look like
this:

Figure 3 – XSL Pattern


Don’t be disappointed because only one line of data was copied from the XML document to the output. This is
easily improved with the XSL <xsl:for-each> element. Use it to select every XML element into the output stream of
the XSL transformation:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 327 -
<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border=’1’>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select=”catalog/cd”>
<tr>
<td><xsl:value-of select=”title”/></td>
<td><xsl:value-of select=”artist”/></td>
</tr>
<xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Note that an XSL style sheet is basically an XML document with embedded HTML tagged values. The result of the
for-each(Figure 4) transformation will look like this:

Figure 4 - Result of <xsl:for-each>

An XSL Stylesheet for Every Occasion


In the previous section, it was shown how XSL can be used to transform a document from XML to HTML by
embedding a reference to an XSL style sheet in the XML file, and letting the browser do the transformation.
However, by embedding the XSL reference in the XML document, you are limiting the output of the data to a single
(current) format.
A more versatile solution would be using a component to perform the XML to HTML transformation. One of the
design goals for XSL was to make it possible to transform data from one format to another.
Here’s the CD catalog without the embedded reference:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 328 -
<?xml version =”1.0”?>
<CATALOG>
<CD>
<TITLE>Big Ones</TITLE>
<ARTIST>Aerosmith</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1986</YEAR>
</CD>
<CD>
<TITLE> The Number of The Beast</TITLE>
<ARTIST>Iron Maiden</ARTIST>
<COUNTRY> U.K.</COUNTRY>
<COMPANY> Sony </COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1987</YEAR>
</CD>
</CATALOG>
Note that the reference to the XSL Style Sheet has been removed. You are now able to apply various formats to the
data using Delphi’s TXSLPageProcedure component.

XSL Controls Appearance


Change the HTML code to variegate the appearance of the output (CDCatalog_GreenTable.xsl):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/TR/WD-xsl'>
<xsl:template match='/'>
<html>
<body>
<table border='4' bgcolor="green">
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select='catalog/cd'>
<tr>
<td><xsl:value-of select='title'/></td>
<td><xsl:value-of select='artist'/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 329 -
XSL Sorting
To sort the output, simply add an <xsl:sort> element inside the <xsl:for-each> element in he XSL file, as seen in our
example:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border="3" bgcolor=”blue”>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select="catalog/cd">
<xsl:sort select="artist"/>
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
There is another way of sorting, which is by using the XSL order-by clause (CDCatalog_OrderBy.xsl):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border="3" bgcolor=”yellow”>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select="catalog/cd" order-by=”+ artist”>
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 330 -
XSL Filtering
Using a filter clause, the XML output can be limited (CDCatalog_FilterBy.xsl):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border="3" bgcolor=”yellow”>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select=”catalog/cd[artist=’Aerosmith’]”>
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 331 -
XSL Conditional IF
The same results are accomplished using a ‘conditional if’ statement. Put the conditional if test against the content of
the XML file. Add an <xsl:if> element to the XSL document (CDCatalog_CondIf.xsl):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border="3" bgcolor=”yellow”>
<tr>
<th>Title</th>
<th>Performer</th>
<th>Price</th>
</tr>
<xsl:for-each select=”catalog/cd”>
<xsl:if match=”.[Artist=’Aerosmith’]”>
<tr>
<td><xsl:value-of select=”title”/></td>
<td><xsl:value-of select=”performer”/></td>
<td><xsl:value-of select=”price”/></td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Other choices of XSL


To express a multiple conditional test against the content of the XML file, add an <xsl:choose> element to the XSL
document. Besides the <xsl:choose> element used to insert a conditional choose test against the content of the XML
file, add the <xsl:when>, and the <xsl:otherwise> elements to the XSL file:

<xsl:choose>
<xsl:when test=”.[artist=’Iron Maiden’]”>
…… some code ……
</xsl:when>
<xsl:otherwise>
…… some code ……
</xsl:otherwise>
</xsl:choose>
Now take a look at the new XSL Style Sheet (CDCatalog_WhenChoose.xsl):

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 332 -
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=’http://www.w3.org/TR/WD-xsl’>
<xsl:template match=’/’>
<html>
<body>
<table border="3" bgcolor=”yellow”>
<tr>
<th>Title</th>
<th>Performer</th>
</tr>
<xsl:for-each select=”catalog/cd”>
<tr>
<td><xsl:value-of select=”title”/></td>
<xsl:choose>
<xsl:when test=”.[Artist=’Aerosmith’]”>
<td bgcolor=”#FF0000”>
<xsl:value-of select=”artist”/>
</td>
</xsl:when>
<xsl:otherwise>
<td><xsl:value-of select=”artist”/></td>
</xsl:otherwise>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Choose your personal style


The following table shows all XSLT Elements that can be used.

Elements supported in IE 5.x may have NON-standard behavior, because IE 5.x was released before XSLT
became an official W3C Recommendation. However, IE 6.x offers full XML / XSLT 1.0 / XPath 1.0
support.

Element Description IE FF
apply-imports Applies a template rule from an imported style sheet 6.0 1.0
apply-templates Applies a template rule to the current element or to the current 5.0 1.0
element's child nodes
attribute Adds an attribute 5.0 1.0
attribute-set Defines a named set of attributes 6.0 1.0
call-template Calls a named template 6.0 1.0
choose Used in conjunction with <when> and <otherwise> to express 5.0 1.0
multiple conditional tests
comment Creates a comment node in the result tree 5.0 1.0
copy Creates a copy of the current node (without child nodes or 5.0 1.0
attributes)
copy-of Creates a copy of the current node (with child nodes and 6.0 1.0
attributes)
decimal-format Defines the characters and symbols to be used when converting 6.0 1.0

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 333 -
Element Description IE FF
numbers into strings, with the format-number() function
element Creates an element node in the output document 5.0 1.0
fallback Specifies an alternate code to run if the processor does not 6.0
support an XSLT element
for-each Loops through each node in a specified node set 5.0 1.0
If Contains a template that will be applied only if a specified 5.0 1.0
condition is true
import Imports the contents of one style sheet into another. Note: an 6.0 1.0
imported style sheet has lower precedence than the importing
style sheet
include Includes the contents of one style sheet into another. Note: an 6.0 1.0
included style sheet has the same precedence as the including
style sheet
key Declares a named key that can be used in the style sheet with the 6.0 1.0
key() function
message Writes a message to the output (used to report errors) 6.0 1.0
namespace-alias Replaces a namespace in the style sheet to a different 6.0 1.0
namespace in the output
number Determines the integer position of the current node and formats 6.0 1.0
a number
otherwise Specifies a default action for the <choose> element 5.0 1.0
output Defines the format of the output document 6.0 1.0
param Declares a local or global parameter 6.0 1.0
preserve-space Defines the elements for which the white space should be 6.0 1.0
preserved
processing- Writes a processing instruction to the output 5.0 1.0
instruction
sort Sorts the output 6.0 1.0
strip-space Defines the elements for which the white space should be 6.0 1.0
removed
stylesheet Defines the root element of a style sheet 5.0 1.0
template Rules to apply when a specified node is matched 5.0 1.0
text Writes literal text to the output 5.0 1.0
transform Defines the root element of a style sheet 6.0 1.0
value-of Extracts the value of a selected node 5.0 1.0
variable Declares a local or global variable 6.0 1.0
when Specifies an action for the <choose> element 5.0 1.0
with-param Defines the value of a parameter to be passed into a template 6.0 1.0

• FF: indicates the earliest version of Firefox that supports the tag
• IE: indicates the earliest version of Internet Explorer that supports the tag

Useful information on each of the following XSLT elements can be found at: http://www.w3school.com

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 334 -
XML Parsers
DOM (Document Object Model)
As seen in the previous module, XML (Extensible Markup Language) is a markup language for describing
structured data. It is similar to HTML, except that the tags describe the structure of information rather than its
display characteristics. XML documents provide a simple, text-based way to store information in a way that it is
easily searched and edited. They are often used as a standard, transportable format for data in Web applications,
business-to-business communication, and so on.
Although it is possible to work directly with the text in an XML document, applications typically use additional
tools for parsing and editing the data. W3C (World Wide Web Consortium) defines a set of standard interfaces -
called Document Object Model (DOM) - for representing a parsed XML document
The Document Object Model (DOM) presents an easily standardized interpretation of an XML document to
applications and scripts. The DOM implementation in Microsoft® XML Core Services (MSXML), for example,
allows loading or creating a document; gathering errors, if any, accessing and manipulating the information and
structures contained within the document and saving the document back out to an XML file, if necessary.
The following diagram (Figure 5) shows the tasks involved in parsing an XML document and presenting the
information to an application or script.

Figure 5 - DOM implementation in MSXML


The DOM approach creates an object tree that is managed by the parser. This allows developers to take advantage of
the logic built into MSXML for managing XML content, rather than having to create their own.

For detailed descriptions of the DOM interfaces, refer to the declarations in the XMLDOM unit, the
documentation supplied by the DOM Vendor, or the specifications provided on the W3C web site
(http://www.w3.org).

BDS provides a number of additional tools for working with XML documents. These tools use a DOM parser that is
provided by a third-party vendor, and make it even easier to work with XML documents. This section describes
those tools.

SAX (Simple API for XML)


The memory requirement demanded by a DOM XML parser led to the creation of another popular XML API known
as the Simple API for XML (SAX). SAX took a different approach in parsing the XML data. Instead of a tree-model
like DOM, it employs a streaming, event-driven model. A SAX parser would not load the whole XML document
into the memory before it had started parsing the document. It would stream the document and start parsing it. A

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 335 -
client application would receive specific XML data based on the type of event the SAX parser would send to it.
However, an XML parser that implements SAX is a forward-only, read-only parser, since it would only buffer what
it needs as it traverses the document. One would then not be able to go backwards to the top of the document. But
this made SAX extremely fast and resource sensitive.

SAX vs. DOM


Both SAX and DOM have their respective strengths and weaknesses, as shown by the following table:

SAX DOM

Advantages

• Speed • Arbitrary navigability

• Ability to process arbitrarily large • Makes XSL-style transformations much


documents due to its low memory easier
requirements

Disadvantages

• No navigability • Uses lots of memory, not well suited for


large documents

• Event based programming that is hard for • Slow, as it needs to build an in-memory
some programmers representation

• Need to maintain state externally (makes • Tight coupling between XML document
XSL-style transformations hard to code) and object model

The SAX document handlers you write are elements to object mapping. So, if the information is structured in a way
that makes it easy to create this mapping, use the SAX API. On the other hand, if data are much better represented as
a tree, then use DOM.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 336 -
XML and VCL Components
TXMLDocument
The starting point to work with an XML document is the TXMLDocument component (Figure 6).

Figure 6 - TXMLDocument component


Located on the Internet page of the Component Palette, TXMLDocument is used to represent an XML document.
TXMLDocument can read an existing XML document from a file, it can optionally be associated with an in-
memory string that is the content of an XML document, or it can create a new, empty XML document.
You can use TXMLDocument to load an XML document, read it, edit it, and save any changes. You can also use
TXMLDocument to access the objects generated by the XML Data Binding wizard.
TXMLDocument can be used to parse that file according to a specified DOM implementation. Once parsed, the
contained data is represented in nodes, where each node corresponds to a tagged element in the document.
Combined with other new XML-specific components, creating programs that utilize XML files should be no
problem for any Delphi developer with moderate experience.

How to use the TXMLDocument


As you use the TXMLDocument component, you start understanding how it works. Before doing it, you must take
the following steps.
Start Delphi and create a VCL Form application selecting File | New | VCL Forms Application.
Then, add the following Tool Palette components to the form:
• TEdit
• TLabel
• TButton
• TTreeView
• TOpenDialog
• TXMLDocument
After organizing these components, the application should look like this:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 337 -
Figure 7 - TXMLDocument Sample in Design Mode
In this example, the application will search for an XML file if you click on the Ellipsis button. The path for the
XML file will be shown in the text entry box; then, when you click on the XML Tree button, the application shows
the XML document selected in the Tree View.
To make the Ellipsis button search for an XML document and show it in the text box, add the following procedure:

procedure TfrmXMLTree.btnFindClick(Sender: TObject);


begin
if OpenDialog.Execute then
edtPath.Text := OpenDialog.FileName;
end;
The code behind the XML Tree button looks like this:

procedure TfrmXMLTree.btnXMLClick(Sender: TObject);


var
TreeFile: TextFile;
begin
XMLDocument.FileName := edtPath.Text;
XMLDocument.Active := True;

AssignFile(TreeFile,'XMLTree.log');
Rewrite(TreeFile);
write(TreeFile,Treeview(XMLDocument.DocumentElement,0));
CloseFile(TreeFile);
trVwData.LoadFromFile('XMLTree.log');
end;
You still need to insert a function to organize the structure of the XML document into the Tree View. To do so, add
the following code to the button:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 338 -
function TfrmXMLTree.TreeView(XmlNode: IXMLNode; node: Integer): AnsiString;
var
i: Integer;
Attr: IXMLNode;
begin
Result := '';
if XmlNode.NodeType = ntElement then
begin
if XmlNode.IsTextElement then
if XmlNode.NodeValue <> Null then
Result := Tab[node] + XmlNode.NodeName +
' = ' + XmlNode.NodeValue + CRLF
else
Result := Tab[node] + XmlNode.NodeName + CRLF
else
if XmlNode.HasChildNodes then
Result := Tab[node] + XmlNode.NodeName + CRLF;
for i := 0 to Pred(XmlNode.AttributeNodes.Count) do
begin
Attr := XmlNode.AttributeNodes[0];
Result := Result + Tab[node + 1] + '[' + Attr.NodeName +
' = ' + Attr.NodeValue + ']' + CRLF;
end;
if XmlNode.HasChildNodes then
for i := 0 to Pred(XmlNode.ChildNodes.Count) do
Result := Result + TreeView(XmlNode.ChildNodes[i], node + 1)
end;
end;
After it’s done, compile and execute this application. The source code above will generate the following result:

Figure 8 - TXMLDocument example

XML Data Binding


Although it is possible to use only the TXMLDocument component and the interfaces it supports to manipulate
XML documents at design mode, it is also possible to use a wizard tool provided by Delphi, which can be used by
developers to simplify the code needed to produce those manipulations. The XML Data Binding Wizard examines

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 339 -
XML schema or data documents and maps their structure and the hierarchy of their nodes. The wizard uses this data
to provide the developer with objects whose properties represent the document’s elements, attributes, and nodes.
This wizard generates the interface and class definitions that correspond to the structure of an XML document or
schema. And it generates a global function that returns the interface to the root element of the document. In the
process, developers are provided with the opportunity to redefine the nodes with more appropriate names, and to
fine tune the represented data types. Once the specifications are set, and interfaces and implementations are
generated, the wizard wraps the final products in a single .pas file. You can then simply save it and add it to the
project.
All interfaces generated by the Data Binding wizard descend from IXMLNode. This means you can still add and
delete child nodes in the same way as when you do not use the Data Binding wizard. In addition, when child nodes
represent repeated elements (when all children of a node are of the same type), the parent node is given two methods
- Add, and Insert - for adding additional repeats. These methods are simpler than using AddChild, because you do
not need to specify the type of node to create.
While many of the methods that will be used are the same, here is an example of how the code may look when XML
data is accessed through a TXMLDocument component:

XMLDocument1.ChildNodes.Nodes.ChildNode[1].Value;
On the other hand, this is how it would read if you use the classes created by the XML Data Binding Wizard:

GetEmployee.Salary;
In the next section, it is explained how to create an application using the XML Data Binding Wizard.

Using the XML Data Binding


To start the XML Data Binding Wizard, select File | New | Other | Delphi Projects | XML (Figure 9). Click the
icon on the right pane and then click OK.

Figure 9 - Starting the XML Data Binding Wizard


In the first part of the wizard (Figure 10), insert the source that will generate interfaces and classes. You should
insert the Schema or XML Data File. You can use any of the types that are listed here: .xml, .dtd, .xdr, .xsd. In the
example below, you will use a simple XML document. This XML file is the same one used in the example above
(books.xml).

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 340 -
Figure 10 - Selecting a valid XML file
Selecting the Options button will allow specifying and configuring the default prefix for Code Generation as well
as for the Data Type Map between XML and native data types:

Figure 11 - Configuration settings


Clicking on the Next button, you activate the wizard that will help parse through the selected XML file. The
following screen shows the Schema Preview of the XML file (Figure 12):

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 341 -
Figure 12 - Schema Preview
The Schema Components displays a hierarchy of elements for which the wizard can generate interfaces and
classes. This hierarchy is divided into complex elements (nodes that correspond to tags that have child nodes) and
simple elements (simple data types that the schema defines for elements in the XML document). Nodes for complex
types can be expanded to display nodes for the child elements.
When a node in the Schema Components hierarchy is selected, the right side of the dialog displays detailed
information on this node and lets you indicate what code, if any, the wizard should generate for that node. The
components that detail the information of the nodes are listed below:
Source Name displays the name of the type or tag in the XML schema.
Source Datatype displays the type of the selected node, as defined in the XML schema.
Documentation displays any comments from the XML schema to describe the type or node.

Using these components it’s possible to edit their values – whenever it’s necessary for the wizard to create
or modify the schema file.

The checkbox called Generate Binding creates an interface and implementation class for a selected complex type,
or a property on the parent interface and class for simple elements that are children of a complex type. This
checkbox defaults to checked, and will automatically produce an implementation class for the interface.
Identifier Name, Document Element Type, and Element Name components correspond to the Binding Options.
The Identifier Name specifies the name of the interface to generate for a top-level complex type. For the children of
a complex type, it specifies the name of the property created for a specific child in the parent element’s interface.
The Document Element Type is the checkbox of Binding options. This option is available only for top-level
complex types and indicates the type of the document element (the root of the data hierarchy).
And, finally, the Element Name specifies the tag name of the document element.

The Binding Options only becomes available if the Generate Binding checkbox is checked.

The next screen in the wizard summarizes all selections up to now. In the Data Binding Settings (Figure 13)
section, you can choose to save the settings in either a new schema file or as annotations to the schema file that is
associated with the current document. Of course, you can also choose not to save the settings at all.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 342 -
Figure 13 - Code generation confirmation screen
After making the desired selections, click on the Finish button to generate a new unit containing the defined
interfaces and implementations for the nodes of the XML document. The properties and methods of these interfaces
can then be used in the Delphi project to access the document’s data.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 343 -
{********************************************************************}
{ }
{ XML Data Binding }
{ }
{ Generated on: dd/mm/yyyy hh:mm:ss }
{ Generated from: books.xml }
{ Settings stored in: books.xdb }
{ }
{********************************************************************}
unit books;

interface

uses xmldom, XMLDoc, XMLIntf, msxmldom, classes;

type

{ Forward Decls }

IXMLBookstoreType = interface;
IXMLBookType = interface;
IXMLAuthorType = interface;

{ IXMLBookstoreType }

IXMLBookstoreType = interface(IXMLNodeCollection)
['{549A50FC-BAFB-494B-89CA-5931189ED766}']
{ Property Accessors }
function Get_Book(Index: Integer): IXMLBookType;
{ Methods & Properties }
function Add: IXMLBookType;
function Insert(const Index: Integer): IXMLBookType;
function FindBook(sField, sText: String): IXMLBookType;
procedure Save(sFileName: String);
property Book[Index: Integer]: IXMLBookType read Get_Book; default;
end;
As part of the code generation, the XML Data Binding Wizard has also generated a number of global utility
functions (defined in the interface section) to make it easier to work with the XML file:

{ Global Functions }

function Getbookstore(Doc: IXMLDocument): IXMLBookstoreType;


function Loadbookstore(const FileName: WideString): IXMLBookstoreType;
function Newbookstore: IXMLBookstoreType;
To access these interfaces in the project, reference this new unit on the uses clause only. The next example shows
how to access the XML elements from within BDS. The application shows the list of available books after clicking
on the button.
Create a VCL Forms Application – Delphi for Win32, and insert the following components:
• TButton
• TStringGrid
The application looks like the following figure:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 344 -
Figure 14 - Application in Design mode
In its OnClick event, insert the following code:

procedure TfrmShowData.btnShowClick(Sender: TObject);


var
i: Integer;
begin
stgrdData.Cells[0, 0] := 'Genre';
stgrdData.Cells[1, 0] := 'Title';
stgrdData.Cells[2, 0] := 'Author';
stgrdData.Cells[3, 0] := 'Price';

for i := 0 to bs.Count - 1 do
begin
stgrdData.Cells[0, i + 1] := bs.Book[i].Genre;
stgrdData.Cells[1, i + 1] := bs.Book[i].Title;

if bs.Book[i].Author.Name <> '' then


stgrdData.Cells[2, i + 1] := bs.Book[i].Author.Name
else
stgrdData.Cells[2, i + 1] := bs.Book[i].Author.Firstname +
' ' + bs.Book[i].Author.Lastname;

stgrdData.Cells[3, i + 1] := bs.Book[i].Price;
end;
end;
Insert the following code for the FormCreate event. This code makes the Loadbookstored method load the xml file
in a form:

procedure TfrmShowData.FormCreate(Sender: TObject);


begin
bs := Loadbookstore('books.xml');
end;

The bs attribute is declared on the TfrmShowData class structure.

The source code above will generate the following dialog:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 345 -
Figure 15 - Client application consuming XML data
As can be seen, Static Binding is extremely useful when dealing with XML data whose structure is known. This is
especially common as a replacement to existing EDI, Electronic Data InterExchange applications.

XML Mapper
The XML Mapper utility is used to create XML transform files. These files are used to instruct the various XML
transformation components on how to create dataset from XML encoded data or vice versa. For the purpose of the
discussion, you will illustrate the steps to convert the following XML data file to a dataset:

<?xml version="1.0"?>
<!-- This file represents a fragment of a book store inventory database -->
<bookstore>
<book genre="autobiography">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Benjamin</first-name>
<last-name>Franklin</last-name>
</author>
<price>8.99</price>
</book>
<book genre="novel">
<title>The Confidence Man</title>
<author>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
<book genre="philosophy">
<title>The Gorgias</title>
<author>
<name>Plato</name>
</author>
<price>9.99</price>
</book>
</bookstore>
To start the XML Mapper utility, open the CodeGear RAD Studio 2009 Folder | Select XML Mapper. This
brings the XML Mapping Tool (Figure 16) up:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 346 -
Figure 16 - XML Mapping Tool
From now on, the Transform Direction will determine from which pane you will start. To succeed in your creation,
transform an XML file to a dataset (XML to Datapacket). Select the File | Open menu to select any valid XML file.
The XML Mapping Tool automatically parses the XML file content into a tree view in the left pane. This function
is very similar to the one seen in the XML Data Binding Wizard.
Next, select all the applicable nodes from the XML file that you would like to include as part of the transformation.
This is done via a popup context menu or by right-clicking on the left pane and then choosing, for example, “Select
All”.
Notice the XML Mapping Tool acknowledges the selection by listing all the selected nodes in the center panel.
Next, you will have to create the transformation mapping from XML nodes to dataset fields. Once you have
specified the way nodes are to be mapped, choose Create | Datapacket from XML. The corresponding data packet
is automatically generated and displayed in the Datapacket pane (Figure 17).

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 347 -
Figure 17 - XML nodes to dataset fields mapping
At this point, keep on creating the transformation mapping file. In fact, the XML Mapping Tool can even help
create a test dataset to verify the transformation. To do this, just click on the “Create and Test Transformation”
button.

Figure 18 - Test transformation result


Now that you know your transformation is working as planned, you can keep on storing your Transformation file
(.XTR). This is done through the File | Save | Transformation menu from within the XML Mapping Tool.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 348 -
Figure 19 - Saving the Transformation file
Since this transformation file represents the conversion from XML to datasetpackets, name it XMLToDP.XTR. The
content of the XTR file looks like this:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 349 -
<XmlTransformation Version="1.0"><Transform Direction="ToCds"><SelectEach
dest="DATAPACKET\ROWDATA\ROW" from="\bookstore\book"><Select dest="@last-name"
from="\author\last-name"/><Select dest="@first-name" from="\author\first-
name"/><Select dest="@name" from="\author\name"/><Select dest="@price"
from="\price"/><Select dest="@title" from="\title"/><Select dest="@genre"
from="@genre"/></SelectEach></Transform><XmlSchema
RootName="bookstore"><![CDATA[<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bookstore" type="bookstoreType"/>
<xs:complexType name="bookstoreType">
<xs:sequence>
<xs:element name="book" type="bookType" minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="titleType"/>
<xs:element name="author" type="authorType"/>
<xs:element name="price" type="priceType"/>
</xs:sequence>
<xs:attribute name="genre" type="xs:string"/>
</xs:complexType>
<xs:element name="title" type="titleType"/>
<xs:simpleType name="titleType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:element name="author" type="authorType"/>
<xs:complexType name="authorType">
<xs:sequence>
<xs:element name="first-name" type="first-nameType"/>
<xs:element name="last-name" type="last-nameType"/>
<xs:element name="name" type="nameType"/>
</xs:sequence>
</xs:complexType>
<xs:element name="first-name" type="first-nameType"/>
<xs:simpleType name="first-nameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:element name="last-name" type="last-nameType"/>
<xs:simpleType name="last-nameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:element name="name" type="nameType"/>
<xs:simpleType name="nameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:element name="price" type="priceType"/>
<xs:simpleType name="priceType">
<xs:restriction base="xs:string"/>
</xs:simpleType>

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 350 -
</xs:schema>]]></XmlSchema><CdsSkeleton/><XslTransform/><Skeleton><![CDATA[<?xml
version="1.0"?><DATAPACKET Version="2.0"><METADATA><FIELDS><FIELD attrname="last-
name" fieldtype="string" WIDTH="8"/><FIELD attrname="first-name"
fieldtype="string" WIDTH="8"/><FIELD attrname="name" fieldtype="string"
WIDTH="32"/><FIELD attrname="price" fieldtype="string" WIDTH="5"/><FIELD
attrname="title" fieldtype="string" WIDTH="38"/><FIELD attrname="genre"
fieldtype="string"
WIDTH="13"/></FIELDS><PARAMS/></METADATA><ROWDATA/><METADATA><FIELDS><FIELD
attrname="last-name" fieldtype="string" WIDTH="8"/><FIELD attrname="first-name"
fieldtype="string" WIDTH="8"/><FIELD attrname="name" fieldtype="string"
WIDTH="32"/><FIELD attrname="price" fieldtype="string" WIDTH="5"/><FIELD
attrname="title" fieldtype="string" WIDTH="38"/><FIELD attrname="genre"
fieldtype="string"
WIDTH="13"/></FIELDS><PARAMS/></METADATA><ROWDATA/></DATAPACKET>
]]></Skeleton></XmlTransformation>
You can also create an XTR file for the reverse direction (datasetpacket to XML), by changing the Transformation
Direction:

Figure 20 - Transform Direction


An XTR for the reverse direction will allow propagating the changes made in the dataset back to the XML.

TXMLTransform
Now that you have successfully generated the necessary XTR file, you can keep on creating an application in Delphi
(read and write) as if it were a dataset. In this example, use the same XML document used in other examples
(books.xml).
Start Delphi and create a new VCL Form application by selecting File | New | VCL Form Application.
From the Tool Palette, add the following components to the form:
• TEdit

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 351 -
• TLabel
• TDBGrid
• TButton
• TClientDataSet
• TDataSource
• TOpenDialog

For quick access to these components, use the shortcut keys Ctrl+Alt+P to set focus to the Tool Palette and
then start typing the name of the component.

Connect the data-aware components in the same way as with normal dataset applications. Add the following
component to the form:

Figure 21 – TXMLTransformProvider component


The purpose of the TXMLTransformProvider is to provide data from an XML document to a client dataset, and to
resolve updates from that client dataset back to the source XML document. TXMLTransformProvider can be a part
of the same application as the client dataset, or it can be placed in the application server of a multi-tiered application.
It serves as a translator between an XML document and a client dataset.
This sample is a dynamic application, where you the attributes of TXMLTransformProvider are inserted in runtime
mode. You will insert the values for the XML File’s Transform Read and Transform Write. After this, load the
XML file on DBGrid. After that, you can modify its content, insert new records, and apply these actions.
You need to connect your TClientDataSet component’s ProviderName property to TXMLTransformProvider.
Your form should look like this:

Figure 22 - Design of the Sample


Notice that three buttons captioned “…” were added. In these buttons, insert the following code:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 352 -
procedure TfrmTXMLTransformProv.btnOpenReadClick(Sender: TObject);
begin
if opdFiles.Execute then
edtRead.Text := opdFiles.FileName;
end;

procedure TfrmTXMLTransformProv.btnOpenWriteClick(Sender: TObject);


begin
if opdFiles.Execute then
edtWrite.Text := opdFiles.FileName;
end;

procedure TfrmTXMLTransformProv.btnOpenXmlClick(Sender: TObject);


begin
if opdFiles.Execute then
edtXml.Text := opdFiles.FileName;
end;
The code to be used by the button whose caption equals “Load” should look like this:

procedure TfrmTXMLTransformProv.btnLoadClick(Sender: TObject);


begin
XMLTransformProvider.XMLDataFile := edtXml.Text;
XMLTransformProvider.TransformRead.TransformationFile := edtRead.Text;
XMLTransformProvider.TransformWrite.TransformationFile := edtWrite.Text;

ClientDataSet.Open;
end;
And finally, the “Apply” code of the button:

procedure TfrmTXMLTransformProv.btnApplyClick(Sender: TObject);


begin
ClientDataSet.ApplyUpdates(-1);
ShowMessage('Applied Data');
end;
At the end it will generate the following result:

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 353 -
Figure 23 - Example of TXMLTransformProvider
The TClientDataSet.ApplyUpdates( ) method will trigger TXMLTransformProvider to resolve the changes made in
TClientDataSet back to the XML data file. Once again, the appropriate XTR files are responsible for reading the
XML data from the XML (XMLToDP.xtr) and writing the changes from the datapacket back to the XML file
(DPToXML.xtr).
As can be seen, with the help of XML Mapper and TXMLTransformProvider, you can now easily work with XML
data files in a familiar TDataSet format in Delphi applications.

Introduction to XML. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 354 -
I ntroduction to VCL for the W EB
This module covers:
• The History of Web Development with Delphi
• What IntraWeb brings to Delphi
• IntraWeb Components
• Application Module vs. Page Module
• Server Controller for IntraWeb Applications
• Database development on the Web
• Frame usage in Web Applications
• AJAX programming in Web Applications

Delphi 2009 Application Development for Win32


- 355 -
The History of Web Development with
Delphi
The history of Web Development with Delphi began when PageProducers were introduced in Delphi 3.
PageProducers allowed developers to build ISAPI, NSAPI, CGI, and WINCGI applications using an event-driven
framework.
PageProducers implement a design that allows actions to be defined. These actions are then used by WebModules to
manage requests and responses from a server.
After WebModules, Delphi introduced the concept of a WebBroker: by building upon the PageProducer technology,
WebBrokers supported communication between IDE Delphi technologies and the then unique Delphi’s component-
centric solution for web development.
By the time Delphi 6 arrived, developers needed enterprise-strength Intranet and Internet support that were scalable
to larger and smaller platforms. Moreover, as projects moved from research to production environments, developers
needed solutions that were extensible, or easier to evolve and maintain over a product’s life cycle. Delphi 6’s answer
was WebSnap: an advanced, complex system that better supports both scalability and extensibility of industrial-
strength web applications.
As a balanced response to both the requirements of the enterprise and the needs of the RAD developer, since its
version 7, Delphi hosts IntraWeb on the component palette. IntraWeb represents a framework that better supports a
RAD-style development environment.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 356 -
What IntraWeb brings to Delphi
IntraWeb is a framework and component set for building web applications in RAD style. As a framework of inter-
related components, IntraWeb allows rapidly creating, debugging, and maintaining web applications as quickly and
easily as Delphi developers have come to expect.
At a high level, IntraWeb is a component architecture that encapsulates the HTTP and HTML layers. While
IntraWeb is an interconnected set of components and frameworks, it does not lock you into its exclusive use. Just
like VCL, you can “reach around” IntraWeb components and framework to interact with the HTML layer at any
time.

IntraWeb Features:
• Supports the Professional or Enterprise versions of Delphi 5 or later.
• Creates a pure Delphi software solution – no JavaScript to write!
• Optionally integrates with WebBroker and WebSnap.
• Includes a built-in HTTP server to assist debugging.
• Includes an integrated WYSIWYG HTML editor.
• Supports popular browser standards such as Mozilla Firefox and IE versions 4-7.
• No Java or ActiveX to download or install.
• Supports Frame views and expected behaviors.
• Support for TChart is available.
• Many third-party IntraWeb components available.
• Transparent state management.
• Easy deployment.
• Over 50 visual components.
• Supports both “Application Mode” for rapid applications development and “Page Mode” for greater control
over individual pages.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 357 -
IntraWeb Components
IntraWeb components occupy several tabs on the tool palette: IW Standard, IW Control, IW Data, IW Client Side,
IW Standard 3.2, IW Data 3.2, IW WAP, and IW Data WAP. As RAD developers have come to expect, each tab
classifies its components by their related features.

The tabs and components displayed in Delphi’s Tool Palette may vary depending on the Main Form type
selected during project creation.

Standard Controls
The Standard Control collection represents a web-enabled set of GUI controls. Support for these controls are defined
within the realm of modern Internet and Intranet operating environments. Table 1 shows the IW Standard
Components grouped on the IW Standard tab from Delphi’s Tool Palette.

Component Description

Host the control inside of Intraweb.

Host the Silverlight video player.

Allows embedding Java applets within the application. Parameters can be


passed to the Java Applet to control its behavior.

Implements a button the user can click to manage the fields of submit forms.

Allows a user to select or deselect a checkable option.

Implements a scrollable drop-down list. Users can select an item from the list.

Retrieves text entered by the user. TIWEdit controls can also display text to the
user.

Allows uploading files directly from the browser to the server.

Allows supporting Flash multimedia presentations.

Renders a horizontal rule.

Displays a graphical image to the user. The image is compiled into the IntraWeb
application. TIWImage dynamically converts the image to JPEG. TIWImage
can be used to present dynamic charts, graphs, or other dynamic and static
graphic types.

Displays static images that are stored as external files.

Implements bulleted or numbered HTML lists.

Used to implement bulleted or numbered HTML lists. This is a read-only


control on which the elements of the list are set using the Items TStrings
property.

Displays text to a user.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 358 -
Component Description

Displays classical list boxes. TIWListBox represents a scrollable list of items


from which users can select and/or view data.

Presents a hyperlink to the user. TIWLink also provides an OnClick event.


TIWLink can be used to navigate within the application, or to implement any
other type of navigational functionality.

Renders a multi-line edit control. TIWMemo allows the user to enter one or
more lines of text.

Allows the construction of drop down menus in the application.

Displays a progress bar. The progress bar is always horizontal. It is filled from
the left to the right. It can display the progress value and it is colored.

Presents a list of exclusive option buttons from which the user can only choose
one.

Displays rectangular shapes with different text and fill colors.

Allows constraining certain controls into a specific region, acting as a pixel-


level container.

Displays read-only multi-line text. TIWText can also be used to output raw
HTML without the need to create a custom component.

Fires a server-side event on a specified interval.

Allows defining a custom grid to display information in a grid view.

Produces a tree/outline control in the application.

Provides hyperlinks to external URLs.

A control that is similar to a DHTML. Use the TIWURLWindow to create


windows within the main window.

A control that embeds ActiveX controls in the IntraWeb application.

A class that embeds MPEG movies in the IntraWeb application. The ability to
play MPEG files depends on the browser. When embedding MPEG files into
the IntraWeb application, keep in mind that they may require the client to
download plugins, new browser versions, external players, etc.

An IntraWeb control used to embed QuickTime movies into the IntraWeb


application. In order to display QuickTime movies, it is necessary to download
and install Apple’s QuickTime to the computer on which the application runs.
This is done automatically by some browsers; others might require manual
installation.

An IntraWeb class designed to render a calendar. The calendar can be


customized, and each individual cell in the calendar can display user defined
text.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 359 -
Component Description

Allows displaying a list of values to which the user can define an order.

Allows adding a control with multiple tab pages to a form.

A control that is used to show a timer to the user in an edit control.

Presents a set of mutually exclusive options to the user – that is, only one radio
button in a set can be selected at a time.
Table 1 – List of IW Standard components

Server Control Components


Server Control Components are the set of controls that a server requires in order to provide access to server-hosted
data. Table 2 shows the IW Control Components grouped on the IW Control tab from Delphi’s Tool Palette.

Component Description

Merges HTML templates and IntraWeb forms. HTML templates allow


separating the presentation from the implementation. This separation
allows leveraging the talents of the web artist or an HTML editor to
control the application’s presentation and layout, without having to
worry about repeatedly re-deploying the application to update its
content.

A template processor for HTML templates based on the HTML 3.2


version.

A layout manager that renders greater control over the visual objects on
the form.

Allows using the functionality of PageProducers in Page Mode.

A helper class used by IntraWeb, while in page mode, to allow the


connection of IntraWeb components with WebBroker components.

Acts as the Server Controller for Page Mode.

Allows displaying data that are sent to the client (browser) and kept on
the client-side.

Allows controlling the look and feel of stand-alone applications.

Associates a normal dataset (server side) with a client side dataset.

Must be used when creating dynamic IntraWeb Pages (IWP). Each


dynamic page must contain a TIWPageController component to handle
all tasks necessary to finding its corresponding IWP page, parsing its
contents, generating the form and returning the result to the browser.

Must be used when creating dynamic IntraWeb Pages (IWP) that render
in HTML 3.2. Each dynamic page must contain a TIWPageController32
component to handle all tasks necessary to finding its corresponding
IWP page, parsing the contents, generating the form, and returning the
result to the browser.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 360 -
Component Description

Allows editing the form as an HTML page with direct interaction with
the object inspector.

Allows designing PDA forms using their HTML editor.

Provides methods that allow managing a collection of Image objects

Pools DataModules in an IntraWeb application.

Merges WAP HTML templates with IntraWeb forms.

Non-visual control that is linked up to the form. Every asynchronous


callback will now use this component to provide status information
during a callback.

Non-visual control that has two important properties: ContentFiles and


OnReady. JQuery basically allows to define behaviors for components
by initializing some parameters once the document (HTML) has been
loaded.
Table 2 – List of IW Control components

Data-Aware Controls
Similarly to their traditional VCL and IW namesakes, the components on the IW Data tab have been designed to
manage data in a web-based application. Table 3 shows the IW Data Components grouped on the IW Data tab from
Delphi’s Tool Palette.

Component Description

A data-aware TIWCheckbox.

A data-aware TIWCombobox.

A data-aware TIWEdit.

A data-aware TIWGrid.

A data-aware TIWImage.

A data-aware TIWLabel.

A data-aware TIWListbox.

Implements a drop-down list of lookup items for filling in those fields that
require data from another dataset.

Implements a list of lookup items for filling in those fields that require
data from another dataset.

A data-aware version of TIWFile, in which you can associate a database


stream field for the uploaded file.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 361 -
Component Description

A data-aware TIWMemo.

Implements a group of buttons used to navigate through a record set.

A data-aware TIWText.

A data-aware TIWRadioGroup.

Table 3 – List of IW Data components

Client Side Controls


As their name implies, client-side controls support data and navigational needs on the client-side of a web
application. Table 4 shows the IW Client Components grouped on the IW Client Side tab from Delphi’s Tool
Palette.

Component Description

Displays information on the client side.

A client-side version of the standard navigator bar.

Allows displaying information in chart format, and allows the user to


change settings dynamically on the client-side.

Used in combination with TIWDynamicChart to provide legend


information.

A dynamic grid that allows changing information and dynamically


displaying it to the user.
Table 4 – List of IW Client components

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 362 -
Standard HTML 3.2 Controls
The Standard HTML 3.2 Controls represent a set of standard controls used to develop PDA applications (rendered in
HTML 3.2). Figure 1 shows the IW Standard HTML 3.2 Components grouped on the IW Standard 3.2 tab from
Delphi’s Tool Palette.

Figure 1 - IW Standard HTML 3.2 Components


The components on this tab are identical to the components located on the IW Standard tab (Table 1), except that, at
runtime, they are rendered in HTML 3.2.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 363 -
HTML 3.2 Data Controls
The HTML 3.2 Data Controls represent a set of standard controls used to develop PDA applications (rendered in
HTML 3.2) that access a database. Figure 2 shows the IW Standard HTML 3.2 Components grouped on the IW
Data 3.2 tab from Delphi’s Tool Palette.

Figure 2 - IW HTML 3.2 Data Components


The components on this tab are identical to the components located on the IW Data tab (Table 3), except that, at
runtime, they are rendered in HTML 3.2.

IW WAP
The WAP components represent a set of standard controls used to develop WAP applications. Figure 3 shows the
IW WAP components grouped on the IW WAP tab from Delphi’s Tool Palette.

Figure 3 - IW WAP components

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 364 -
The components on this tab are identical to the components located on the IW Standard tab (Table 1), except that, at
runtime, they are rendered to be displayed in WAP devices (WML).
Figure 4 shows an IntraWeb WAP application running on a WAP device emulator.

Figure 4 - IW WAP Application running

IW Data WAP
The Data WAP components represent a set of components used to develop WAP applications that access a database.
Figure 5 shows the IW Data WAP components grouped on the IW Data WAP tab from Delphi’s Tool Palette.

Figure 5 - IW Data WAP components


The components on this tab are identical to the components located on the IW Data tab (Table 3), except that, at
runtime, they are rendered to be displayed in WAP devices.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 365 -
Application Mode or Page Mode?
Because support for web development has been incrementally implemented, Delphi provides support for several
different Internet and Intranet technologies. Likewise, IntraWeb also offers two distinct modes in which it can be
used: Page Mode and Application Mode. When creating your next web application, which module type should you
use?

Application Mode
Application Mode provides a RAD development environment that enables you to develop a web application much
like any other Delphi application.
In Application Mode, IntraWeb handles the web-centric technical details for you. Application Mode allows you to
disregard the client-server link and session management. With a little practice, Application Mode lets you easily
develop an application while demanding little to no prior web development experience.
Application Mode is built on top of Page Mode. As such, Application Mode is the ideal solution when you need to
create complete, multi-page web applications. Application Mode encapsulates most of the complexities of web
development.
Note that limiting the programming options does not mean that Application Mode is not flexible. For example, when
using Application Mode, you can still customize the application to generate raw HTML, use style-sheets, code
templates, or even create your own reusable custom components in a page-oriented manner.

Templates allow developers and web artists to control the look and feel of an application. When properly
used, templates can also separate content presentation activities from implementation details. By separating
content from implementation, templates also allow that customizations be applied across many
applications.

When to Use Application Mode: Consider using Application Mode when you need to quickly develop a complete
web application.

Page Mode
Page Mode is used for developing websites or applications that are made up of individual pages. Page Mode is more
flexible than Application Mode. Page Mode is essentially the same type of web development paradigm that you
might be accustomed to, in case you’ve used technologies such as WebBroker or WebSnap before.
Page Mode is available by using PageProducers that can be used with WebBroker or WebSnap. As a lower-level
interface, IntraWeb’s Page Mode is very useful for creating stand-alone, interactive pages. As such, Page Mode is
ideal for creating highly interactive and/or complicated reports, forms, charts, and more.
With Page Mode, IntraWeb will be no longer the framework. Instead, the IntraWeb components become an add-on
to other frameworks available within the Borland IDE. For example, Page Mode allows for full integration with
WebBroker, WebSnap, and other technologies. If you have already developed web apps using WebBroker,
WebSnap, etc., then you will feel right at home when using IntraWeb’s Page Mode.
Because of its usage (which resembles an add-on), Page Mode is more flexible than Application Mode, but it usually
requires more effort in order to achieve the same result.
When to use Page Mode: If the main goal of a specific page on your site is to become highly interactive or dynamic,
but this page is not otherwise an integral part of the web application, the Page Mode is a good candidate for the task.
In spite of being a strong page-authoring environment, Page Mode is not suited for all web site tasks. In some cases,
ASP, PHP, and other page-creation technologies will be much better suited for managing dynamic page content.
These and other dynamic content creation technologies can be used in conjunction with Page Mode.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 366 -
IntraWeb Server Controller
IntraWeb allows the building of the following types of applications:
• Standalone Application.
• Service Application.
• ISAPI Extension.

Figure 6 - IntraWeb Application Wizard


When an IntraWeb project is created, developers make a decision between three different project types.
The sample that will be discussed here is based on the StandAlone Application type.
The wizard begins by asking the developer to commit to an Application Type and to a physical directory where the
project will be created, as shown in Figure 6.
After specifying a location to store the project, IntraWeb places the project, along with all the code needed to start it
with a MainForm and an IWServerController into that location.

All IntraWeb projects start with two units – a blank application and a server controller. As its name implies,
the server controller has properties for configuring session timeouts, ports, and other server-specific
information.

The source code for a simple IntraWeb Standalone Application is presented below:

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 367 -
program Example01;

uses
Forms,
IWMain,
Unit1 in 'Unit1.pas' {IWForm1: TIWAppForm},
ServerController in 'ServerController.pas' {IWServerController:
TIWServerControllerBase},
UserSessionUnit in 'UserSessionUnit.pas' {IWUserSession: TIWUserSessionBase};

{$R *.res}

begin
Application.Initialize;
Application.CreateForm(TformIWMain, formIWMain);
Application.Run;
end.

IWServerController
The IWServerController is an important non-visual interface that controls the web application. The features that
ServerController hosts include:
Feature Description
ComInitialization Needed whenever COM calls are made from web applications, or when
ADO is used.
SessionTrackingMethod Specifies the way session tracking should be done. It has three options – the
use of URLs, cookies, or hidden fields to track the sessions of a web
application.
DestinationDevice Represents the application destination, which can be a PC, a Palm, a
PocketPC, a Wireless Phone, etc.
ExceptionDisplayMode Defines how exceptions are displayed to the user. It can be a MessageBox,
in its own page, on the same page, or in a related frame.
Port Defines a port for communication. The default is port 0, which makes
IntraWeb randomly choose a port every time it is run.
RestrictIPs Prevents specific IP addresses from accessing the web application.
SessionTimeout Defines the session timeout period for a web application.
SSLPort Allows communication over HTTPS. It is only a bridge; all the work for
establishing the SSL port has to be done separately.
SupportedBrowsers Defines which browsers the generated code needs to support and be
optimized to.
CharSet Allows specifying a content encoding other than the standard character sets
used.
Table 5 – List of ServerController Features

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 368 -
Modifying the MainForm
Once the web application has been generated, start customizing the look and feel of the main form:

Figure 7 - Design Progress


From the IWStandard component page, drop an IWImageFile component, an IWEdit, and an IWButton onto the
MainForm.
Point the ImageFile1.Picture property to the IWImageFile component, and choose any image file you have.

It is usually a good idea to create a subdirectory named “Files” immediately below the project level, in
order to store images, Flash files, multimedia, etc. Creating this folder will allow the project to find them
automatically.

Double-click on the IWButton to implement its OnClick event:

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 369 -
unit Unit1;

interface

uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompButton,
IWCompEdit, Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl,
IWControl, IWExtCtrls;

type
TIWForm1 = class(TIWAppForm)
IWImage1: TIWImage;
IWEdit1: TIWEdit;
IWButton1: TIWButton;
procedure IWButton1Click(Sender: TObject);
public
end;

implementation

uses IWBaseForm;

{$R *.dfm}

procedure TIWForm1.IWButton1Click(Sender: TObject);


begin
WebApplication.ShowMessage('Welcome to IntraWeb ' + IWEdit1.Text);
end;

initialization
TIWForm1.SetAsMainForm;

end.
Compile and run the web application.

Figure 8 - The Server Controller


If the Port property is not specified on the ServerController, the IntraWeb web server begins at a random port.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 370 -
Figure 9 - Running the web application
Executing the server, the main browser is automatically launched. Once activated, the IntraWeb web application will
be displayed:

Figure 10 - Message Displayed

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 371 -
Database development on the Web
Developing a database using IntraWeb is as simple as developing a more traditional Delphi database application:
BDE, IBExpress, ADO, DBExpress, and other TDataset-based technologies are all available. You can even use your
own data access routines to support IntraWeb database development activities.

Figure 11 - Standard Database Components Available


Create a new application and drop an IWDBGrid and an IWDBNavigator on its default form:

Figure 12 - A Simple DB application


Generate a new DataModule containing an SQLConnection that connects to an InterBase driver and database, such
as the example employee.gdb.
Place an SQLTable into the DataModule and set it to connect to the Employee table.
Place a ClientDataSet and a DataSetProvider. They will connect to the SQLTable and circumvent the problem of
having unidirectional data showed in grids.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 372 -
unit untEmployee;

interface

uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompButton,
IWCompEdit, Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl,
IWControl, IWExtCtrls, jpeg, IWDBStdCtrls, IWGrids, IWDBGrids, DB;

type
TIWForm1 = class(TIWAppForm)
IWDBGrid1: TIWDBGrid;
IWDBNavigator1: TIWDBNavigator;
IWDBEdit1: TIWDBEdit;
procedure IWAppFormCreate(Sender: TObject);
procedure IWAppFormRender(Sender: TObject);
public
end;

implementation

uses IWBaseForm, dmEmployee;

{$R *.dfm}
procedure TIWForm1.IWAppFormRender(Sender: TObject);
begin
DataModule1.cdsEmployee.Open;
end;

procedure TIWForm1.IWAPpFormCreate(Sender: TObject);


begin
DataModule2 := TDataModule2.Create(webApplication);
end;

initialization
TIWForm1.SetAsMainForm;

end.
Run the web application.

Figure 13 - Running the Database Application

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 373 -
Frame usage in Web Applications
One of the best features of IntraWeb is that it allows the use of Delphi’s regular TFrame concept, in order to reuse
UI designs on different web forms.
To see how this is done, create a new IWFrame in Delphi
• Drop an IWMenu on top of the Frame and a standard TMainMenu component.

Figure 14 - Design Mode


• Add a few menu items to the TMainMenu and attach the menu to IWMenu, using its AttachedMenu
property.
• Align the IWMenu to alClient and set its orientation to vertical.
• Place an IWFrame component at the top of the MainForm of the IntraWeb application.
• Align the frame to alLeft.
• Run the resulting web application.

Figure 15 - Managing the View

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 374 -
Figure 16 - Sub-Menu Selection

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 375 -
AJAX programming in Web Applications
One of the new features of IntraWeb is that it allows the use of the AJAX concept. AJAX, which stands for
Asynchronous JavaScript and XML, is a web development technique for creating interactive web applications. Its
intent is to make web pages feel more responsive by exchanging small amounts of data with the server behind the
scenes, so that the entire web page does not have to be reloaded each time the user requests a change. This is meant
to increase the web page's interactivity, speed, and usability.
The Ajax technique uses a combination of:
• XHTML (or HTML) and CSS, for marking up and styling information.
• The DOM accessed with a client-side scripting language, especially ECMAScript implementations such as
JavaScript and JScript, to dynamically display and interact with the information presented.
• The XMLHttpRequest object is used to exchange data asynchronously with the web server. In some Ajax
frameworks and in certain situations, an IFrame object is used instead of the XMLHttpRequest object to
exchange data with the web server; while in other implementations, dynamically added <script> tags may be
used instead.
• XML is sometimes used as the format for transferring data between server and client, although any format
will work, including preformatted HTML, plain text, JSON (JavaScript Object Notation) and even EBML
(Extensible Binary Meta Language). These files may be created dynamically by some form of server-side
scripting.
To see how this is done, create a new VCL for the Web Application. Add the controls below, as shown in Figure 17.

Control Property Value

TIWEdit Text

StatusText Text to Add Item

TIWListBox StatusText Added Items

TIWButton Caption Add

StatusText Add Text into the ListBox Items

TIWCheckBox Caption Sorted

StatusText Turns On/Off Sorted list

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 376 -
Figure 17 – A basic AJAX Application Example

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 377 -
Implement the onClick event of IWButton1 and IWCheckBox1 controls as follows:

unit Unit1;

interface

uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompLabel,
Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl, IWControl,
IWCompEdit, IWCompListbox, IWCompButton, IWCompProgressBar, IWCompCheckbox;

type
TIWForm1 = class(TIWAppForm)
IWEdit1: TIWEdit;
IWButton1: TIWButton;
IWListbox1: TIWListbox;
IWCheckBox1: TIWCheckBox;
procedure IWButton1Click(Sender: TObject);
procedure IWCheckBox1Click(Sender: TObject);
public
end;

implementation

{$R *.dfm}

procedure TIWForm1.IWButton1Click(Sender: TObject);


begin
sleep(2000);
IWListbox1.Items.Add(iwedit1.Text);
end;

procedure TIWForm1.IWCheckBox1Click(Sender: TObject);


begin
IWListbox1.Items.Sorted := IWCheckBox1.Checked;
end;

initialization
TIWForm1.SetAsMainForm;

end.
Run the application.
With the application’s page open, move the cursor over the controls and note that the status bar of the browser
shows the control description that was added into its StatusText property.
Now, write a text in IWEdit1 and click on the Add button. Observe the progress bar of the browser. In this case, the
entire page is reloaded. Repeat this process a few more times and notice the page is always entirely loaded.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 378 -
Go back to the application source code and move the code on the OnClick events of the controls to their respective
OnAsyncClick events, as seen below:

procedure TIWForm1.IWButton1AsyncClick(Sender: TObject;


EventParams: TStringList);
begin
sleep(2000);
IWListbox1.Items.Add(iwedit1.Text);
end;

procedure TIWForm1.IWCheckBox1AsyncClick(Sender: TObject;


EventParams: TStringList);
begin
IWListbox1.Items.Sorted := IWCheckBox1.Checked;
end;
Run the application again.
Write a text in IWEdit1 and click the Add button. Observe the progress bar of the browser. The progress bar is not
executed, because the page does not need to be reloaded.

Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 379 -
Delphi 2009 Application Development for Win32
- 380 -
I ntroduction to Rave Reports
In this chapter, the Rave Reports Components will be introduced, as well as the necessary proceedings to create
reports with these components.
Rave Reports 5.0 was incorporated to Delphi 7 as the default reporting solution, replacing Quick Reports. Since
then, it has been a powerful and flexible tool to create and manage reports in the Delphi environment.

Delphi 2009 Application Development for Win32


- 381 -
Rave Reports Components
The Rave Reports System gives developers a way to include reports within their Delphi executables. With Rave
Reports objects and their corresponding interface components, developers can create viewable and printable reports
for VCL in Win32 and VCL for .NET in Delphi. It is a direct replacement for Quick Reports, which was included in
previous versions of Delphi. Quick Reports is still available, but it has been deprecated from the component set.
Below you see the Delphi Tool Palette with the Rave tab selected (Figure 1):

Figure 1 – Rave Reports Components

Component Description

The TRvProject component is the key to providing access to the visual


reports created with Rave. Normally, there is a single TRvProject
component in the application, but there can be more, if necessary.
TRvProject also has several properties and methods to make it easy to
create an efficient interface for users.

The TRvSystem component is a very powerful component that integrates


the functionality of three components, TRvRenderPreview,
TRvRenderPrinter, and TRvNDRWriter in one easy-to-use application.
TRvSystem can send a report to the printer, or a preview screen, and can
display a setup and status screen as well.

The TRvNDRWriter component is used in conjunction with


TRvRenderPrinter and TRvRenderPreview to store a report in a special
binary format until it is ready to be printed or previewed.

Through the events in the data connection components, you can


customize how the data is sent to Rave reports. For non-database data
using the TRvCustomConnection component, such as memory arrays or
binary record files, you will need to provide all access to data through
these events.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 382 -
Component Description

TRvDataSetConnection can be used to provide access to TDataSet


descendent components including third party dataset components.

TRvTableConnection is to be used specifically with TTable components


or their descendents.

TRvQueryConnection is to be used specifically with TQuery components


or their descendents.

The TRvRenderPreview component takes a file generated by a


TRvNDRWriter component and sends it to the screen for previewing.
TRvRenderPreview has many methods and events that allow the
programmer to create a completely customized user interface.

The TRvRenderPrinter component takes a file generated by a


TRvNDRWriter component and sends it to the current printer.
TRvRenderPrinter is often used to do a print from the preview screen.
TRvRenderPrinter is a simple component but it has methods and
properties to customize the selection of what gets printed.

TRvRenderPDF will convert an NDR stream or file to PDF format. Text,


graphic, line, and shape (rectangle, ellipse) objects are supported.

TRvRenderHTML will convert an NDR stream or file to HTML format.


Each page in the report will be generated to an HTML 4.0 formatted file.
Text, graphic, line, and rectangle objects are supported.

TRvRenderRTF will convert an NDR stream or file to an RTF


document. Text, graphic, line, and rectangle objects are supported.

TRvRenderText will convert an NDR stream or file to a text document.


Only text objects are supported in the output; all other objects will be
ignored.
Table 1 – List of Rave Reports Components

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 383 -
The Rave Visual Designer
Rave allows you the creation of graphic reports by using de Rave Design Tool. Better known as the Report
Authoring Visual Environment, the controls placed on the Delphi form serve to graphically link out data to the Rave
Designer.
With Rave Designer, you will be able to create reports easily and quickly, allowing you to deploy independent
report files without recompiling the application.
The best way to learn how Rave works is to jump right in; so, start by creating a simple report:
From Delphi IDE, choose File | New | VCL Forms Application.
Now, select the Data Access tab in the Tool Palette and drop a TClientDataSet onto the form. After that, alter the
following properties, as seen in Table 2.

Property Value

Name cdsCustomer

FileName C:\Program Files\Common Files\CodeGear Shared\Data\Customer.xml (This path can


vary , depending on where the Delphi files are installed)

Active True
Table 2 – Properties of TClientDataSet
From the Rave tab in Tool Palette, add a TRvDataSetConnection component to the form, change its Name property
to rdsCustomer and its DataSet property to the TClientDataSet cdsCustomer.
Lastly, you need to be able to activate the visual designer and specify the name of the report definition file. To do so,
you will need a TRvProject component from the Rave tab. Set the name property of TrvProject to rvpReports.
The form should look like this:

Figure 2 – Required Components for Simple Report


Right-click on the TrvProject component and choose Rave Visual Designer…

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 384 -
Figure 3 – Starting the Rave Visual Designer
The Visual Designer will be loaded as follows:

Figure 4 – Rave Reports Designer

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 385 -
Since Rave is a stand-alone application, you do not need a TrvProject to activate it: You can also activate
Rave by choosing Tools | Rave Reports Designer from the Delphi IDE, or by starting Rave.exe from
outside the Delphi environment.

If you are running Windows XP with the latest Services Pack, you might also see the Windows Security Alert dialog
for unblocking the application access.
Delphi communicates with Rave via TCP/IP. Click on the unblock button to continue only if this is a safe
application running locally on your own workstation.
Rave might seem a little intimidating at first. However, since you have specified the data sets before the Rave
Designer was activated, creating a report is now very easy. Once Rave is loaded, the first thing that has to be done is
creating a new data view for the report.
Choose File | New Data Object from the main Rave menu.

Figure 5 – Data Connections Wizard


Since Rave supports many complicated data relationships, you will have to specify which type of data view you
want to create.
Choose Direct Data View and press Next.
Once you have told Rave what type of connection you want to view, Rave can inspect the form and display a list of
available options (see Figure 6):

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 386 -
Figure 6 – Listing of available Data Connections
If all was performed accordingly up to this point, the name of Rave’s Data Set Connection should appear in the list
box. Since you want to view the report at runtime and design-time, leave the corresponding boxes checked. Click on
Finish to proceed.
Once Rave has located the data, it will default to add the names of all known fields to the Data View. To see a list of
found fields, click on the tree view of the project to expand the Data Dictionary definitions (see Figure 7):

Figure 7 – Viewing the Data Dictionary


Once you have defined a set of fields that Rave can put on the report, you can design the report itself. Use Tools |
Report Wizards | Simple Table to generate the first report.
Rave will ask you to choose a Data View for your report, as shown in Figure 8:

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 387 -
Figure 8 – Selecting a Data View
Since there is only one defined view, the choice is easy. Choose DataView1 and click on the Next button.
Now, specify the fields that will appear on the report. Select the CustNo, Company, Country, and Contact fields as
shown in Figure 9:

Figure 9 – Field selection


The wizard will ask you to arrange the fields in the order you want them to be placed on the report. Change the order
as shown in Figure 10 and press the Next button.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 388 -
Figure 10 – Arranging field selections
After the content has been defined, you will have to define the layout (title and margins) of your report.

Figure 11 – Report layout options


Change the layout options as you like. Proceed by clicking on the Next button.
You’ll now get to the last step before the Wizard generates the reports. Font selections allow overriding any default
font that will be used in your report.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 389 -
Figure 12 – Font selection
Alter fonts as you like and press Generate. You have created the first report definition!

Figure 13 – Your first report


Once the report definition is created, feel free to scroll about the Rave real-state. Use Rave’s property panel to
explore the properties of the objects that the Rave Wizard has automatically placed on this first report.
Lastly, save this report as CustomerReport.rav.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 390 -
Changing Report Fields Manually
The wizard facilitates and speeds up the simple report designing, but these reports do not always have all the
necessary information, or maybe it’s the case that changes have occurred and new fields now need to be included in
the report. To deal with situations like those, it will now be explained how to manually include fields to reports,
making the tool even more flexible.
It will be necessary to add the City field to the Customer Simple Report.
Go to Rave Visual Designer and open the CustomerReport.rav report definitions file. In the Data View Dictionary,
select the DataView1City field (see Figure 14):

Figure 14 – Selecting the DataView1City field


Now, pressing the CTRL key, click on the selected field and drag it to the report area.
Change the position of the other fields so that all fields fit in the report, then add a Text Component from the Rave
Designer Standard tab to the column label and alter its Text property to City.
The report should look like Figure 15.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 391 -
Figure 15 – Report changed – City field included
After having concluded all alterations, save the report.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 392 -
Viewing the Results
Once a report is created, you should test it to be sure that the report looks and generates itself correctly. You will use
the Rave designer’s print preview function to examine your first report.
To activate the print preview feature, you have to execute the report. To execute the default report, choose File |
Execute Report from the main Rave designer menu or press F9.
When executing a report, Rave will prompt you for Output Options. From this dialog, you can choose between an
external file, printer, or print preview (screen) rendering device as your output target.

Figure 16 – Report output options


Choose Preview as your report destination and press OK.

Figure 17 – The final report (Print Preview)

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 393 -
Generating the Report under Delphi
Once you have designed your report and saved its definition, using it is relatively easy.
Assuming you have an instance of TRvProject named rvpReports, you will use this component to help interface the
form with your report. You will need to specify the .rav report definition file name using the ProjectFile property,
and execute the report:

procedure TfrmReports.btnExecuteReportClick(Sender: TObject);


begin
rvpReports.ProjectFile := ‘CustomerReport.rav’;
rvpReports.Execute;
end;
By assigning the contents of the ProjectFile at runtime, a single TRvProject component can host several reports.
When the report is executed, the user will be able to use the TRvProject interface to manage the printing of the
report definition (in this case, CustomerReport.rav).

The ProjectFile property can also be assigned at design-time using the Object Inspector.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 394 -
Beyond Rave Designer

Code Based Reports


With Code Based reports, you write reports using plain Delphi code. That provides a very flexible way to display
any kind of data, allowing any kind of complex layouts.
To write a code based report, just drop a TRvSystem component on the form and write the report on its OnPrint
event handler. Sender is the report you are creating, and it can be typecasted to TBaseReport. It contains all the
methods needed to output information to that particular report.

Simple Code Base Report


Here's a simple report using the code based mechanism:

procedure TfrmReports.RvSystemPrint(Sender: TObject);


begin
with Sender as TBaseReport do
begin
GotoXY(1, 1);
SetFont('Times New Roman', 16);
Bold := True;
Print('This is a Rave Code Based Report');
end;
end;
To execute this report, call the RvSystem.Execute method.
In the code above, you defined the position to print the message. Set font family and font size as Times New Roman
and 16, respectively. Set font bold as True, and finally call the Print method to output the message. See this report
running in Figure 18:

Figure 18 – A Simple Rave Code Based Report

Note: More about Rave Code Based reports will be covered in the “Rave Reports” training course.

Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 395 -
Delphi 2009 Application Development for Win32
- 396 -
I ntroduction to DataSnap
This module covers:
• Introduction to DataSnap
• DataSnap Components
○ DataSnap Client Components
○ DataSnap Server Components
• Brief Introduction to Using DataSnap Components

Delphi 2009 Application Development for Win32


- 397 -
Introduction
DataSnap is a multi-tier database application solution. DataSnap was previously called MIDAS (Multi-Tier
Distributed Application Services Suite). It allows Delphi and C++Builder developers to split the traditional 2-tier
model (client/server) into a 3-tier model (client/application server/database server). The term “multi-tier” is also
referred to as “N-tier” (where “N” is the number of tiers). In practice, however, the number of tiers is usually 3;
which is why this technology is also referred to as 3-tier.
In the DBExpress module, TClientDataSet was explained, showing how powerful it can be as a database tool in its
own right. DataSnap uses TClientDataSet on the client tier of the application, so it will be used heavily in this
module.
DataSnap was first introduced in Delphi 3, where it was called MIDAS. Each subsequent release of DataSnap has
taken great leaps forward in the n-tier technology, making DataSnap one of the most powerful n-tier solutions
available today. Since each new release of DataSnap is a leap forward from previous releases, there are some
incompatibilities between DataSnap releases; thus, it is always recommended to use the most recent DataSnap
release (not to mention that the most recent also means the most powerful and full-featured).
For those who are interested, Midas was the mythical Greek king who turned to gold everything he would touch.
CodeGear’s MIDAS was named after him, arguably because of its own ‘golden’ touch. Fortunately, CodeGear’s
technology deserves better fate than King Midas’.

Key Concepts
DataSnap supports server-based data access. This means that business rules and data constraints are aggregated at
the server prior to being passed on to the client. The software that resides on the server is typically referred to as the
application server.
There are several advantages to creating and using a DataSnap application server to “serve up” distinct views of
your enterprise data:
• Since creating your own application server means that the client software no longer requires direct access to
the back-end database(s), the cost of licensing can be greatly decreased for your enterprise applications.
• By creating an application server, you are free to buffer often-read and / or seldom changed tables. This
tuning can greatly improve application performance.
• Since the application server is the only interface the client application needs to be concerned with, your
server can effectively “encapsulate” all data access operations. This encapsulation means that even if your
“back-end” database technologies are dramatically changed, your client applications won’t ever need to be
changed: When changes occur to the data-access routines, only the application server needs to be re-
deployed.
• DataSnap’s thin client, zero-configuration deployment is perfect for mobile applications. When client
applications are physically disconnected from the server, changes can be saved locally until the client can
reconnect to the server. Known as the "briefcase model", when detached database operations are completed,
the client application reconnects to the server and sends updates all at once to the application server. The
server then applies the changes to the master database.
• Using the briefcase model means that all off-line data changes and additions can be tracked and applied
without having to write any code.

IAppServer
In this case, when client datasets need an interface for an application server, the interface is encapsulated by an
instance of IAppServer. The IAppServer interface provides the basis for all communication between a client dataset
and a (usually, but not necessarily, remote) content provider. IAppServer allows client datasets and other interfaces
to conduct their operations with provider components in a uniform manner. In other words, just as DBExpress
encapsulated the operations when working with databases, DataSnap represented a similar leap forward in
abstraction.

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 398 -
DataSnap Client Components on the Component Palette
DataSnap components work with ClientDataSet to allow connecting to a server.

Figure 1 – DataSnap Client tab on the Component Palette


These components represent interfaces that can make it a “snap” to connect to data hosted under DCOM, sockets,
web connections, and even CORBA.
There are three types of functionality provided by these DataSnap components: Protocol Centric, Brokered, and
Connection Oriented.

Protocol Centric
The first category of DataSnap components is protocol specific. It is used to negotiate with an application and create
a connection. Once the connection with the remote application server is established, client datasets on the client
application use an IAppServer interface (provided to your application by the component) to communicate with
remote content providers.
• TDCOMConnection handles a client application's DCOM connection to the remote server in a multi-tiered
application.
• TSOCKETConnection allows the client to interface with an application server using the socket interface 1.
• TWEBConnection uses HTTP to manage the connection to an application server 2.
• TDSProviderConnection: the TDBXAppServerConnection component was renamed
TDSProviderConnection.
Note that other connection types exist or can be created (for example TSOAPConnection, which is part of Delphi’s
Web Services).

1 To use this component between a Windows client and server, the application server must be running SocktSrve.exe.

2 The server system must have IIS version 4, or better


.

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 399 -
Brokers
As their name implies, these types of components maintain a list of servers that are ready to satisfy client requests.
Their job is to “broker”, or supply them to the client application. They can be used to provide a simple form of load
balancing.
• TSimpleObjectBroker allows client applications to locate application servers dynamically during runtime.
• TConnectionBroker is useful when the application has several client datasets, all of which use the same
connection component to manage a connection to an application server.

Connection Management
Unlike the other components on the DataSnap tool pallet, connection management tools are geared toward, helping
the client application manage the load and location of your application servers.
• TSharedConnection allows the client application to use a single connection to each remote data module on
an application server. This sharing is accomplished by allowing this component to serialize and send the
main application server messages that allow it to dispatch calls to the IAppServer interfaces of other
modules.
• TLocalConnection lets the application communicate directly with local providers. This component is used
to manage the connection between one or more XML brokers, client datasets, and similar providers that are
used within the local application. TLocalConnection only links clients to providers that are instances of
TDataSetProvider.

DataSnap Server Components on the Component Palette

Figure 2 – DataSnap Server tab on the Component Palette


DataSnap has a new class called TDSServer, which manages the creation and lifetime of transports and server
method classes. The developer can use TDSServer to create the middle tier server. The new component
TDSServerClass can be used to specify an application server-side class with published methods that can be called
from a remote client using dynamic method invocation. Methods in this class can be called from remote clients, such
as the DBX Client Provider or the ADO.NET provider.
Once connected to a DataSnap server, the developer can call methods in an application server similarly to stored
procedures. For instance, the new class TSqlServerMethod can be used to call server methods by specifying a text
class, a method name for command text, and parameters for the method. Server methods can use a TDBXReader to
receive and return datasets to and from the client.

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.

- 400 -
DataSnap in the Object Repository

Figure 3 – DataSnap in the Object Repository


The developer can still write application server side classes that extend TRemoteDataModule, so that the providers
they contain can be accessed by the new client-side TDBXAppServerConnection client component.
TDBXAppServerConnection provides connectivity to a DataSnap server using DBExpress, which keeps existing
applications compatible, also taking advantage of new DataSnap features. New applications do not need to use
TRemoteDataModule or TDBXAppServerConnection with DataSnap.
Remote Data Modules can contain any non-visual components. Typically, these are data access components, such as
datasets and the provider components that handle communication between these datasets and client applications.
Remote data modules can also contain other non-visual components, such as TTimer or TImageList.
Remote data modules are dual-interface servers. Like the XML Components and others, remote data modules
implement the IAppServer interface. Connection components on client applications are designed to look for this
interface.
At design time, a TRemoteDataModule object provides a visual container into which a developer can place non-
visual components, set their properties, and write event handlers for them. In the unit file for the remote data
module, a developer can also code any business rules that are to be centralized in the middle tier of a multi-tiered
application.

Creating a Three-Tier Application


So far, only one and two-tier applications have been created. Now you will create a three-tier application. The third
tier is defined as the Database Server. This is the database engine itself, and once it is created, it does not have to be
created again. You will use a Local InterBase for this purpose, but any database engine will do.
Both the client tier and the application server tier are Delphi programs. Start by creating the application server tier
because the client tier requires it to exist.
First of all, create a new folder named “DataSnap” with two sub-folders named “Server” (to save the server
application) and “Client” (to save the client portion of our application), respectively.
Create a new application. This application server will be running at the same time as the client. These two
applications would usually be running on separate machines, but for your purposes, you will use the same machine.

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 401 -
As both programs will be running at the same time, moving the form to a different location will make the job easier.
Resize it and give it a meaningful caption, e.g., “Application Server”. Save the project as AppServer.bdsproj and the
unit as uMain.pas, inside the “Server” folder.
The application server ‘serves’ data to clients. The data comes from TDataSet descendants, such as TSQLQuery.
The data from these components are exposed by a combination of a Remote Data Module and a TDataSetProvider.
First, create a Remote Data Module. Select File | New | Other | Delphi Projects | Multitier | Remote Data Module
and the following dialog will be displayed:

Figure 4 - Remote Data Module Wizard


This dialog is similar to the ones used by the Automation Object and Transactional Object wizards. The
“Instancing” and “Threading Model” options are covered in depth on the specific DataSnap training Course, but you
can accept the defaults. Enter “DataSnapAppServer” as the name of the CoClass and click on OK.
You will see what appears to be a regular data module. In fact, if you look at the code, you will see that it inherits
from TRemoteDataModule, not from TDataModule, and a certain amount of code has been written into the unit.
Add a TSQLConnection to the remote data module and set a valid database connection to the Employee database (a
test database that comes with InterBase).
Then, add a TSQLQuery, and set the SQLConnection property to “SQLConnection1” and SQL to “SELECT *
FROM CUSTOMER”. In what concerns to functionality, it doesn’t matter whether Active is set to True at design
time or not. However, when it comes to efficiency, you’d rather leave the dataset closed.
Now add a TDataSetProvider (from the Data Access page) to the remote data module and set DataSet to
“SQLQuery1”.
The application server is now complete. Save the remote data module as uRDM.pas.
The application server is a COM server. In order for COM servers to work, they must be registered in the registry.
The simplest way to register an out of process server, such as this one, is to run it. Delphi out of process servers
register themselves in the registry every time they are run. Run the application server and close it down.
Now you need to create the client application. Select Project | Add New Project | Delphi Projects | VCL Forms
Application and click on OK. Save the project as AppClient.dproj and the unit as uMain.pas, both inside the Client
folder, and the project group as “DataSnapGroup.dgroup” inside the DataSnap folder.
Add a TDCOMConnection (from the DataSnap page) to the form. In the Object Inspector, drop down the
ServerName. If this is your first DataSnap application on this machine, you will see a single item. Select
“AppServer.DataSnapAppServer”. This name is formed from the name of the server, “AppServer”, added to the

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 402 -
name given to the remote data module, “DataSnapAppServer”. As a result, this TDCOMConnection component
makes all of the datasets (which are exposed in the server) available to the client.
Now add a TClientDataSet to the form and set RemoteServer to DCOMConnection1. TClientDataSet has to specify
which of the datasets exposed by the remote data module it may use to retrieve data from. Select the
ClientDataSet1’s ProviderName in the Object Inspector and watch closely as you drop down the combo box. Notice
that the application server is now running. Select DataSetProvider1 from the combo box and set its Active property
to True.
Add a TDataSource and a TDBGrid to the client application and connect them to the ClientDataSet1. Save the
application. With this, a DataSnap application has just been created. As you can see, the data were transferred from
server to client side. Run the application.

Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 403 -
Delphi 2009 Application Development for Win32
- 404 -
Building the M eeting Organizer
Your customer, XYZ Corporation, has given you a set of requirements for a Meeting Organizer application they
want you to create.

Delphi 2009 Application Development for Win32


- 405 -
Requirements
Requirements management is the first step in software development, as seen in the “Introduction - Requirements”
chapter.
Now, you will apply all the knowledge previously acquired, creating the Meeting Organizer application.

System Needs
• The Meeting Organizer shall allow employees to manage their meetings, i.e., to schedule new meetings,
cancel meetings, update meetings, and retrieve meeting schedules. The system will also allow users to
receive asynchronous notifications about their meeting calendar updates and meeting reminders.
• For this release of the system, the meeting is defined as a face-to-face meeting that requires a physical
location (a meeting room). In future releases, the definition of meeting may be expanded to include
conference calls or virtual meetings using Internet or other communication channels.
• System administrators fall under a special category of users. They manage a database of system users and a
database of meeting rooms.
• The system will provide a web-based interface supporting operations, such as managing meetings
(scheduling a new meeting, canceling and updating currently scheduled meetings), managing users (adding
a new user, removing a user, updating user data), and managing meeting rooms (adding a new meeting
room, removing a meeting room from the system, updating meeting room data).
• The system must support privacy and ensure that only authorized users can access it.
• The system must support audits of the user authorization process (login operations).

The Web Interface and Send E-Mail functionalities will not be seen now.

System Requirements
Items to be implemented:
• Log In
• Manage Meeting Rooms
• Manage Meetings
• Manage Users
• Purge Meetings
• Send Meeting Reminders (will not be seen now)
To develop this application according to the specifications above, you should create the folders structure to contain
the project.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 406 -
Configuring the Project
In this section, you will create a new project and define its folder structure.
Create the following folder structure:

Figure 1 – Project Folder Structure

Now, create a new project. Choose File | New | VCL Forms Application (Figure 2)

Figure 2 - Creating a new VCL Form Application – Delphi for Win32 application

Save the project (choose File | Save All, or type CTRL + SHIFT + S). The unit and project files should be saved in
the “Forms” folder of the folder structure created above.
• Save the unit as “uMainForm.pas”
• Save the project as “MeetingOrganizer.dproj”

Defining the project configuration


Select Project | Options. The Project Options dialog will be opened.

Directories/Conditionals
Configure where the executable file will be generated. Set the Output Directory option to “..\”. This way, the
MeetingOrganizer.exe file will be created into the Delphi2009 folder. Set the Unit output directory option to
“..\Temp”. This option defines the folder where the .dcu files will be generated. See Figure 3.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 407 -
Figure 3 – Configuring directories/conditionals options

Application
Change the Title option on Application settings to “Meeting Organizer”, and, if desired, change the project’s icon
(Figure 4).

Figure 4 – Configuring the Application options


The other options will not be changed.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 408 -
Creating the Main Form

Figure 5 – Main Form


To configure the behavior and design of the Main Form, change its properties as follows:

Properties Value

Caption Meeting Organizer

BorderStyle bsSingle

Color clSilver

FormStyle fsMDIForm

Name MainForm

WindowState wsMaximized
Table 1 – Main Form Properties List
Add the following components to the MainForm form.

Component Name Description

TMainMenu MainMenu Encapsulates a menu bar and its accompanying


drop-down menus for the form.

TImageList ImageList Represents a collection of same-sized images,


each of which can be referred to by its index.

TActionManager ActionManager Manages and displays all actions contained in


the application.

TCustomizeDlg CustomizeDlg A customization dialog for action bands.

TTimer Timer Encapsulates the Windows API timer


functions. TTimer will be used to refresh the
Date and Time in the StatusBar.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 409 -
Component Name Description

TStatusBar StatusBar Represents a status bar which will be used to


display User, Date, and Time information.
Table 2 – Main Form Components List
Now, add the MainMenu Items, as seen in Figure 6.

Figure 6 – MainMenu Components


Double-click on MainMenu to open the Menu Editor. Add the following items:

Name Caption

mnuFile File

mnuControl Control

mnuTools Tools

mnuWindow Window

mnuHelp Help
Table 3 – Menu Items List
Add some images to the ImageList component according to the MainMenu items created above.
While keeping the keyboard Shift key pressed, select the MainMenu and ActionManager components and point their
Images property to the ImageList component.
Now, add a TActionToolBar component to the MainForm form. TActionToolBar will have shortcuts to the most
used items.
Double-click on ActionManager to open the Action Manager Editor. Click on the Toolbars tab, add a new tool bar,
and set its Name property to “ActionToolBar”.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 410 -
Figure 7 – Action Manager – Adding a ToolBar
Go back to the MainForm form. The MainMenu Window item will control the forms presentation mode. This menu
item will have the following sub-items (Figure 8):

Figure 8 – Window Menu Sub-Items


To add functionality to each of the items created above, add actions using the ActionManager. TActionManager
provides pre-defined actions for this purpose. Go to the Action Manager Editor and choose New Standard Actions,
as shown in Figure 9.

Figure 9 – Adding Standard pre-defined Actions


Select all action items of the Window category (as seen in Figure 10), and click on the OK button. The actions will
be automatically added into ActionManager.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 411 -
Figure 10 – Standard Actions – Window Actions
Now, associate the menu items by following the steps described below:
• Double-click on MainMenu to open its editor.
• Select the Window menu item, create a new child item, and link the Action property to its respective action
(WindowCascade1 in this case), as seen in Figure 11.

Figure 11 – Menu Item Action – Window Cascade


Repeat the steps above with the other Window menu sub-items.
• WindowTileHorizontal1
• WindowTileVertical1
• WindowMinimizeAll1
• WindowArrange1
• WindowClose1
• Change the MainForm’s WindowMenu property to mnuWindow.
After that, add a new action to the CustomizeDlg component. This action will allow users to add shortcut menu
items in order to speed up their work.
Go to the ActionManager editor and click on the New Standard Action option. Select Tools |
TCustomizeActionBars and click on the OK button (Figure 12).

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 412 -
Figure 12 – Adding the Customize Dialog Action
In the MainMenu Editor, create a new child item for the Tools menu item and link its Action property to the action
created above. See Figure 13.

Figure 13 – Linking the Menu Item with the CustomizeAction


The next step is to add the StatusBar items. They will be comprised of four panels (Figure 14):
• Date
• Time
• User Name
• User Type (Admin, User, etc…)
The last two items will be seen later.
Change the Width property of the four panels to 100, 100, 200, and 300, respectively.

Figure 14 – StatusBar panels


Having properly configured the StatusBar, change the Timer’s Interval property to 1000 and add the code below to
its OnTimer event handler, in order to show the current Date and Time.
Timer’s OnTimer event:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 413 -
procedure TMainForm.TimerTimer(Sender: TObject);
begin
StatusBar.Panels[0].Text := 'Date: ' + DateToStr(Date);
StatusBar.Panels[1].Text := 'Time: ' + TimeToStr(Time);
end;
Save the project and run it.

Figure 15 – Main Form


Now, add the other menu items and associate them to their respective images, as shown in Figure 16.

Figure 16 – Menu Items to complete Menu


Create a new action in the ActionManager (see Table 4) component, to close the application. After that, change the
Close Menu sub-item Action property to acClose:

Property Value

Caption Close

ImageIndex The appropriate image

ShortCut CTRL+Q

Category File (Set the value manually)

Name acClose
Table 4 – Close Action

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 414 -
Figure 17 – Close action properties
Finally, add the code below to the OnExecute event handler of the Close action:

procedure TMainForm.acCloseExecute(Sender: TObject);


begin
Application.Terminate;
end;

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 415 -
Visual Form Inheritance
Now, you will configure the forms structure. In this application, all forms will inherit from a unique form. This is
useful for code reuse and good maintenance.

Application Structure
The first form, named AbstractForm, will be the first at the inheritance’s hierarchy. See Figure 18:

Figure 18 – Inheritance Form Structure

Creating the AbstractForm


Create a new form (choose File | New | Form), and save it as “uAbstractForm.pas”.
Change the new form properties as follows:

Properties Value

Name AbstractForm

Caption Abstract Form

KeyPreview True
Table 5 – AbstractForm properties
Add a TXPManifest component to the AbstractForm form and name it “XPManifest”.
Now, code the following AbstractForm’s events:
onClose event:

procedure TAbstractForm.FormClose(Sender: TObject; var Action: TCloseAction);


begin
if FormStyle = fsMDIChild then
Action := caFree;
end;
onKeyPress event:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 416 -
procedure TAbstractForm.FormKeyPress(Sender: TObject; var Key: Char);
begin
if key = #27 then
Close;
end;
onShow event:

procedure TAbstractForm.FormShow(Sender: TObject);


begin
if FormStyle = fsMDIChild then
begin
Top := 2;
Left := 2;
end;
end;
Save the project.
The initial form is finished. This form will be used by other forms to re-use the above codes.

Figure 19 – AbstractForm

Creating the AbstractDataForm


Add a new inherited form to the project. To do so, choose File | New | Other | Inheritable Items, select the
AbstractForm icon, and then click on OK. Save the new form as uAbstractDataForm.pas and configure it as follows:

Properties Value

Name AbstractDataForm

Caption Abstract Data Form

FormStyle fsMDIChild
Table 6 – Form properties
Add the following components to AbstractDataForm:

Component Name Description

TImageList ImageList Image list for the buttons.

TActionList ActionList Actions for the data buttons.

TDataSorce datControl Links the Data-Aware controls to a DataSet.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 417 -
Component Name Description

TToolBar ToolBarData Buttons container.


Table 7 – AbstractDataForm Components List
Add some images to the ImageList component, as well as actions to the ActionList component that will manipulate
the data, as shown in Figure 20.
On the ActionList Editor (double-click on the ActionList component), add a New Standard Action and select all
actions under the Dataset category.

Figure 20 – Adding Dataset Actions


After that, add a new action. Name it “acClose” and change its Caption property to ‘&Close’ (Figure 21).

Figure 21 – Close Action


Code acClose’s onExecute event handler as follows:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 418 -
procedure TAbstractDataForm.acCloseExecute(Sender: TObject);
begin
inherited;
Close;
end;
The next step is to add some buttons to the ToolBar component. Right-click on ToolBarData, add ten buttons and
three separators, as shown in Figure 22. Connect each button to its respective action.

Figure 22 – ToolBarData appearance


Change the following properties of the ToolBarData component:

Property Value

DrawingStyle dsGradient

Images ImageList

ShowCaptions True

GradientEndColor clSilver

Align alBottom
Table 8 – ToolBarData properties
Add the following code to the form’s OnCloseQuery event.

procedure TAbstractDataForm.FormCloseQuery(Sender: TObject;


var CanClose: Boolean);
begin
inherited;
CanClose := not (datControl.State in [dsInsert, dsEdit]);
end;
Finally, save the project.

Figure 23 – AbstractDataForm form

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 419 -
Creating the AbstractDataTabForm
Add a new inherited form to the project (choose File | New | Other | Inheritable Items), select the
AbstractDataForm icon and click on OK. Save the new form as uAbstractDataTabForm.pas and configure it as
follows:

Properties Value

Name AbstractDataTabForm

Caption Abstract Data Tab Form


Table 9 – Form properties
Add a TPageControl component to the AbstractDataTabForm and change the following properties:

Properties Value

Name PageControl

Style tsFlatButtons

Align alClient
Table 10 - TPageControl properties
Add two new TabSheets to PageControl. To do this, right-click on PageControl and select New Page, as seen in
Figure 24.

Figure 24 – Adding New TabSheets


Change the Tabs’ Name property to ‘tabEdit’ and ‘tabSearch’, and their Caption property to ‘Edit’ and ‘Search’,
respectively.
Add a TPanel to the tabSearch tab and set its Align property to alBottom.
Add the following components to the new TPanel component:

Component Name Caption/Text

TLabel lblSearch Search

TLabel lblField Field

TLabel lblValue Value

TComboBox cmbField <empty>

TEdit edtValue <empty>

TButton btnSearch Search


Table 11 – Search Panel components’ properties

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 420 -
Figure 25 – Search Panel
Now, add a TDBGrid component to the tabSearch tab, name it DBGridSearch and set its Align property to alClient.
The tabSearch tab should look like Figure 26.

Figure 26 – tabSearch tab with a TDBGrid component and a TPanel component


Go to the form implementation to type some code.
Create an Array of String called aFieldNames in the Private section of the AbstractDataTabForm’s class. This array
will store the names of the dataset fields in the same order they will appear in the cmbField component.

private
{ Private declarations }
aFieldNames: Array Of String;
public
Add the following code to the AbstractDataTabForm’s onCreate event. It fills cmbField with the fields of a dataset:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 421 -
procedure TAbstractDataTabForm.FormCreate(Sender: TObject);
var
i: integer;
begin
inherited;
for i := 0 to datControl.DataSet.Fields.Count - 1 do
if (datControl.DataSet.Fields[i].FieldKind = fkData) and
(datControl.DataSet.Fields[i].Tag <> -2) then
begin
cmbField.Items.Add(datControl.DataSet.Fields[i].DisplayLabel);
SetLength(aFieldNames, Length(aFieldNames) + 1);
aFieldNames[Length(aFieldNames) - 1] :=
datControl.DataSet.Fields[i].FieldName;
end;
cmbField.ItemIndex := 0;
end;
Add the following code to the btnSearch’s onClick event. This code will be responsible for locating the occurrence
of edtValue’s Text property value in the dataset, according to the field selected in the cmbField component.

procedure TAbstractDataTabForm.btnSearchClick(Sender: TObject);


begin
inherited;
Screen.Cursor := crHourGlass;
datControl.DataSet.DisableControls;
try
if (cmbField.ItemIndex >= 0) and (edtValue.Text <> '') then
datControl.DataSet.Locate(aFieldNames[cmbField.ItemIndex], edtValue.Text,
[loCaseInsensitive, loPartialKey]);
finally
datControl.DataSet.EnableControls;
Screen.Cursor := crDefault;
end;
end;
When PageControl’s ActivePage property is changed, the Insert, Delete and Edit buttons are enabled only if the
ActivePage property is tabEdit. To do so, add the following code to PageControl’s onChage event handler:

procedure TAbstractDataTabForm.PageControlChange(Sender: TObject);


begin
inherited;
DataSetInsert1.Visible := PageControl.ActivePageIndex = 0;
DataSetDelete1.Visible := PageControl.ActivePageIndex = 0;
DataSetEdit1.Visible := PageControl.ActivePageIndex = 0;
end;
If the DataSet is in edit or insert mode, then the user can’t change between the tabs. To do so, add the following
code to PageControl’s OnChanging event:

procedure TAbstractDataTabForm.PageControlChanging(Sender: TObject;


var AllowChange: Boolean);
begin
inherited;
AllowChange := not (datControl.DataSet.State in [dsEdit, dsInsert]);
end;

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 422 -
Effective Project Development
Now that you have already created some abstract forms, it’s time to configure the database server connection.
The next steps are:
• Create an abstract Data Module to work as the ancestor for the other Data Modules in the application
• Create a Data Module to store the database server connection components
• Create a Data Module for each entity (User, Room, Meeting)
Before adding the Data Modules to the project, make sure only the MainForm form is in the “Auto-create forms”
lane. To do this, select Project | Options | Forms (see Figure 27).

Figure 27 – Setting MainForm as the unique Auto-create form


Now, you can create the Data Modules. First, create the MainDM Data Module. Choose File | New | Other | Delphi
Files, and double-click on the Data Module item (Figure 28). Save the new Data Module unit as “uMainDM.pas”,
and change its Name property to “MainDM”.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 423 -
Figure 28 – Adding a Data Module component to the project
Add a TSQLConnection component from the DBExpress palette (Figure 29). Name it “SQLConnection”, and
change its LoginPrompt property to False.

Figure 29 – TSQLConnection component

Copy the database file “MEETINGORGANIZER.GDB” from the “DatabaseModel” folder (which can be
found in the Student CD) to C:\Interbase_DB path.

Click on the Data Explorer Pallete, select DBExpress, right-click on InterBase, and select Add New Connection..
Choose “MEETINGORGANIZER” as the Connection Name (Figure 30). Click on OK to confirm.

Figure 30 – “New Connection” dialog box


Now, you only need to set the database path (C:\Interbase_DB\MEETINGORGANIZER.GDB). See Figure 31.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 424 -
Remember to set the User_Name and Password options. User sysdba, used here, is InterBase’s default user.
Sysdba’s password is masterkey

Figure 31 – Setting the database path, User_Name, and Password


Access the SQLConnection property in the Object Inspector Pallete and set the Driver to Interbase and
ConnectionName to “MEETINGORGANIZER”.
Add the unit Forms to the uMainDM Implementation’s uses clause.
Now, in the MainDM’s OnCreate event, the database connection should be established. To accomplish that, add the
following code:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 425 -
procedure TMainDM.DataModuleCreate(Sender: TObject);
begin
if not SQLConnection.Connected then
SQLConnection.Open;
end;
The connection is ready. When the application is loaded and MainDM is created, the SQLConnection configuration
parameters are read and the connection is then opened.
You need to store the connection property in a common place for the entire application, while the application is
open. Since the main form is destroyed only when the application is closed, add this functionality into the MainForm
form. Open TMainForm, add the following property, and type Ctrl + Shift + C. Add the unit SqlExpr to the
Interface’s uses clause.

public
{ Public declarations }
property DBConnection: TSQLConnection;
end;
After creating this property, assign the connection to it. So, in MainDM’s OnCreate event, open the connection by
typing the following code:

TMainForm(Application.MainForm).DBConnection := SQLConnection;
Its OnCreate event looks like this:

procedure TMainDM.DataModuleCreate(Sender: TObject);


begin
if not SQLConnection.Connected then
SQLConnection.Open;
TMainForm(Application.MainForm).DBConnection := SQLConnection;
end;
The database connection is now available for the application.
You can now add the other Data Modules. As each entity (Meeting, Room, and User) will have its own Data
Module, you will create a common Data Module - with basic functionalities - to work as the ancestor for the other
Data Modules.
Add a new Data Module, save it as “uAbstractDataModule.pas” and change its Name property to
“AbstractDataModule”.
Add a TSQLDataset from the DBExpress palette, and name it “sqlControl”.
Add a TDataSetProvider from the Data Access pallet and name it “dspControl”. Modify its Dataset property to
“sqlControl” and its UpdateMode to upWhereChanged. Set its Options property as follows:
• poIncFieldProps = True
• poAutoRefresh = True
• poPropagateChanges = True
• poAllowCommandText = True
In the same palette, add a TClientDataSet and name it “cdsControl”, modify its ProviderName property to
“dspControl”, and change its PacketRecords property to 1.
Now you need to add some common functionality to the AbstractDataModule Data Module.
Add the following code to the “AbstractDataModule” Published section and then press CTRL+SHIFT+C:

procedure cdsApplyUpdates(DataSet: TDataSet);


procedure cdsCancelUpdates(DataSet: TDataSet);
Keep reading for the implementation of the procedures:
Applying updates:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 426 -
procedure TAbstractDataModule.cdsApplyUpdates(DataSet: TDataSet);
begin
if TClientDataSet(DataSet).ChangeCount > 0 then
TClientDataSet(DataSet).ApplyUpdates(0);
end;
Canceling updates:

procedure TAbstractDataModule.cdsCancelUpdates(DataSet: TDataSet);


begin
if TClientDataSet(DataSet).ChangeCount > 0 then
TClientDataSet(DataSet).CancelUpdates;
end;
Refreshing (and canceling pending changes):

procedure TAbstractDataModule.cdsControlBeforeRefresh(DataSet: TDataSet);


begin
if TClientDataSet(DataSet).ChangeCount > 0 then
TClientDataSet(DataSet).CancelUpdates;
end;
Add the following code to cdsControl’s BeforeDelete event:

procedure TAbstractDataModule.cdsControlBeforeDelete(DataSet: TDataSet);


begin
if MessageDlg('Delete record?', mtConfirmation, [mbYes, mbNo], 0, mbYes) = mrNo
then
raise Exception.Create('Canceled!');
end;
Add the units’ Dialogs and Controls to the AbstractDataModule uses clause.
Select the cdsControl component. In the Object Inspector Event page, modify its AfterDelete and AfterPost events
to point to the cdsApplyUpdates procedure. The AfterCancel event should point to the cdsCancelUpdates procedure.
See Figure 32.

Figure 32 – cdsControl events tab


Now create the Reconcile Error Dialog. Choose File | New | Other | Delphi Files and double-click on the
“Reconcile Error Dialog” icon (Figure 33). Save the unit of the new form as “uRecError.pas” and name it
“ReconcileErrorForm”.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 427 -
Figure 33 – Creating the Reconcile Error Dialog
Add the uRecError unit to the AbstractDataModule uses clause, and create the following method in the
AbstractDataModule published section. Then press CTRL+SHIFT+C:

procedure cdsReconcileError(DataSet: TCustomClientDataSet;


E: EReconcileError;
UpdateKind: TUpdateKind;
var Action: TReconcileAction);
Add the following code to the implementation of the cdsReconcileError procedure:

procedure TAbstractDataModule.cdsReconcileError(DataSet: TCustomClientDataSet;


E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction);
begin
Action := HandleReconcileError(DataSet, UpdateKind, E);
end;
Add the code below to the AbstractDataModule’s onCreate event. This method assigns the cdsReconcileError
procedure to the OnReconcileError event for all TClientDataSet components.

procedure TAbstractDataModule.DataModuleCreate(Sender: TObject);


var
i: Integer;
begin
for i := 0 to ComponentCount - 1 do
if Components[i] is TClientDataSet then
TClientDataSet(Components[i]).OnReconcileError := cdsReconcileError;
end;
At last, add the DBConnection property below (in the TAbstractDataModule’s public section):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 428 -
property DBConnection : TSQLConnection; read GetDBConnection write
SetDBConnection;
Press CTRL+SHIFT+C on the keyboard, so that Delphi creates the declaration and implementation of the field and
method. Add the uMainDM unit to the AbstractDataModule uses clause. Code the SetDBConnection procedure as
follows:

procedure TAbstractDataModule.SetDBConnection(value: TSQLConnection);


var
i: Integer;
begin
fDBConnection := value;
for i := 0 to ComponentCount - 1 do
if Components[i] is TSQLDataSet then
if not TSQLDataSet(Components[i]).Active then
TSQLDataSet(Components[i]).SQLConnection := fDBConnection;
end;
In order to check if the required fields of a dataset are filled, add the procedure below to the public section:

procedure TAbstractDataModule.CheckRequiredFields(DataSet: TDataSet);


var
i: Integer;
begin
for i := 0 To DataSet.FieldCount - 1 do
if DataSet.Fields[i].Required then
if Trim(VarToSTr(DataSet.Fields[i].Value)) = '' then
raise Exception.Create('Field ''' + DataSet.Fields[i].DisplayLabel + '''
is required!');
end;
Now create a function to return a number to be used as primary key for the tables in the application, every time an
insert command is executed.
To do so, add the following method to the public section:

function TAbstractDataModule.GenerateID(Gen: String): Integer;


var
sqlID: TSQLQuery;
begin
sqlID := TSQLQuery.Create(nil);
try
sqlID.SQLConnection := TMainForm(Application.MainForm).DBConnection;;

sqlID.SQL.Add('SELECT GEN_ID(' + Gen + ', 1) AS nID');


sqlID.SQL.Add(' FROM RDB$DATABASE');
sqlID.Open;

if sqlID.RecordCount > 0 then


Result := sqlID.FieldByName('nID').AsInteger
else
Result := -1;
finally
sqlID.Free;
end;
end;
The AbstractDataModule Data Module (Figure 34) that will be the ancestor for other Data Modules is ready.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 429 -
Figure 34 –AbstractDataModule Data Module
Now, create the TAbstractControl class which will work as the ancestor class for the other application control
classes.
Add a new unit to the project and save it as “uAbstractControl.pas”.
In the uAbstractControl unit, create a new class descending from TComponent, and name it TAbstractControl.
Create two properties in the public section of this class. Name the first property “DBConnection” and declare it as
TSQLConnection type. Name the second property “TD” and declare it as TTransactionDesc type. The
DBConnection property will represent the database connection while the TTransactionDesc property will represent
the transactions.

type
TAbstractControl = class(TComponent)
public
property DBConnection : TSQLConnection;
property TD : TTransactionDesc;
end;
Add the following units to the Interface’s uses clause.

interface

uses
Forms, SysUtils, Classes, Variants, SqlExpr;
Put the keyboard cursor inside the TAbstractControl class and press the CTRL+SHIFT+C keys. This way Delphi
will create the implementation of fields and methods for the DBConnection and TD properties.
Since the TAbstractControl descendent control classes will be responsible for creating the forms and the Data
Modules, you need to create abstract methods (in its public section) to do this.
The following code creates the form:

procedure CreateDefaultForm; virtual; abstract;


The following code creates the Data Module:

procedure CreateDefaultDM; virtual; abstract;


Now, create a constructor method for the TAbstractControl class and add the uMainForm and Forms units to the
Implementation’s uses clause:

implementation

uses uMainForm;
Implement the constructor method as follows (declare it as override):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 430 -
constructor TAbstractControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);

DBConnection := TMainForm(Application.MainForm).DBConnection;

fTD.GlobalID := 1;
fTD.TransactionID := 1;
fTD.IsolationLevel := xilREADCOMMITTED;

CreateDefaultDM;
end;
Now, implement the destructor method as follows (declare it as override):

destructor TAbstractControl.Destroy;
begin
inherited Destroy;
end;
At the moment, the class constructor method is called, and the application current connection is assigned to the
DBConnection property.
At last, add this code to the Interface section. This message is shown when a transaction fails.

const MO_ExceptionMsg = 'Meeting Organizer Error: ' + #13;


Add the following method (in the public section), which is responsible for formatting the data used in SQL
statements:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 431 -
function TAbstractControl.DataFormat(Data: Variant; DataType: Char): string;
begin
if VarIsEmpty(Data) then
begin
Result := '';
end
else
begin
if DataType = 'C' then
begin
Result := #39 + Data + #39;
end
else
if DataType = 'D' then
begin
Result := 'CAST(' + #39 + FormatDateTime('mm/dd/yyyy',
VarToDateTime(Data)) + #39 + ' AS DATE)'
end
else
if DataType = 'T' then
begin
Result := 'CAST(' + #39 + FormatDateTime('mm/dd/yyyy hh:nn:ss',
VarToDateTime(Data)) + #39 + ' AS TIMESTAMP)'
end
else
if DataType = 'N' then
begin
Result := StringReplace(Result, '.', '', [rfReplaceAll]);
Result := StringReplace(Result, ',', '.', [rfReplaceAll]);
end;
end;
end;
The complete code for uAbstractControl is:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 432 -
unit uAbstractControl;

interface

uses
Forms, SysUtils, Classes, Variants, DBXpress, SqlExpr;

const
MO_ExceptionMsg = 'Meeting Organizer Error: ' + #13;

type
TAbstractControl = class(TComponent)
private
fTD: TTransactionDesc;
fDBConnection: TSQLConnection;
procedure SetTD(value: TTransactionDesc);
function GetTD: TTransactionDesc;
procedure SetDBConnection(value: TSQLConnection);
function GetDBConnection: TSQLConnection;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure CreateDefaultDM; virtual; abstract;
procedure CreateDefaultForm; virtual; abstract;
property TD: TTransactionDesc read GetTD write SetTD;
property DBConnection: TSQLConnection read GetDBConnection write
SetDBConnection;
function DataFormat(Data: Variant; DataType: Char): string;
end;

implementation

uses uMainForm;

{ TAbstractControl }

constructor TAbstractControl.Create(AOwner: TComponent);


begin
inherited Create(AOwner);

DBConnection := TMainForm(Application.MainForm).DBConnection;

fTD.GlobalID := 1;
fTD.TransactionID := 1;
fTD.IsolationLevel := xilREADCOMMITTED;

CreateDefaultDM;
end;

destructor TAbstractControl.Destroy;
begin
inherited Destroy;
end;

function TAbstractControl.GetDBConnection: TSQLConnection;


begin
Result := fDBConnection;
end;

function TAbstractControl.GetTD: TTransactionDesc;

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 433 -
begin
result := fTD;
end;

procedure TAbstractControl.SetDBConnection(Value: TSQLConnection);


begin
fDBConnection := Value;
end;

procedure TAbstractControl.SetTD(Value: TTransactionDesc);


begin
fTD := Value;
end;

function TAbstractControl.DataFormat(Data: Variant; DataType: Char): string;


begin
if VarIsEmpty(Data) then
begin
Result := '';
end
else
begin
if DataType = 'C' then
begin
Result := #39 + Data + #39;
end
else
if DataType = 'D' then
begin
Result := 'CAST(' + #39 + FormatDateTime('mm/dd/yyyy',
VarToDateTime(Data)) + #39 + ' AS DATE)'
end
else
if DataType = 'T' then
begin
Result := 'CAST(' + #39 + FormatDateTime('mm/dd/yyyy hh:nn:ss',
VarToDateTime(Data)) + #39 + ' AS TIMESTAMP)'
end
else
if DataType = 'N' then
begin
Result := StringReplace(Result, '.', '', [rfReplaceAll]);
Result := StringReplace(Result, ',', '.', [rfReplaceAll]);
end;
end;
end;

end.
The next step is to create the user register. To do so, you will need:
• A Data Module to contain the data access components (MODEL)
• A form to do registers and searches (VIEW)
• A control class that will be responsible for the business rule (CONTROLLER)
Add a new Data Module to the project. Choose File | New | Other | Inheritable Items, select the
AbstractDataModule icon, and click on the OK button. Save the unit as “uUserDM.pas” and change its name
property to “UserDM”.
To make it easier to configure the data access components at design time, add the uMainDM unit to the uses clause
of the newly created Data Module.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 434 -
Figure 35 – The Data Module UserDM
Select the sqlControl component and change its SQLConnection property to MainDM.SQLConnection. Click on the
ellipsis button of the CommandText property and add the following SQL instruction into the CommandText Editor
dialog box:

SELECT USR.USER_ID,
USR.NAME,
USR.EMAIL,
USR.PHONE,
USR.LOGIN,
USR.PASSW,
USR.ISADMIN
FROM USERS USR
ORDER BY USR.USER_ID
Go back to the UserDM Data Module and add all fields to the sqlControl component (Figure 36). The same
procedure should be applied to the cdsControl component.

Figure 36 – Adding all fields to the sqlControl component


Change the sqlControl component’s USER_ID field’s pfInKey property to True and its DefaultExpression property
to 0. Configure the cdsControl component fields as follows:

FieldName DefaultExpression DisplayLabel pfInKey FixedChar Required

USER_ID 0 User ID True - True

NAME Name False False True

EMAIL E-Mail False False True

PHONE Phone False False False

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 435 -
FieldName DefaultExpression DisplayLabel pfInKey FixedChar Required

LOGIN Login Name False False True

PASSW Password False False True

ISADMIN 'N' Is Admin? False True True


Table 12 - Setting the properties of cdsControl fields
Add the following code to ISADMIN’s OnGetText event:

procedure TUserDM.cdsControlISADMINGetText(Sender: TField; var Text: string;


DisplayText: Boolean);
begin
inherited;
if Sender.Value = 'Y' then
Text := 'YES'
else
Text := 'NO';
end;
Now you need to create the user registration form. Choose File | New | Other. Select the AbstractDataTabForm icon
and click on the OK button. Save the unit as “uUserForm.pas”, change its Name property to “UserForm” and its
caption property to “User”.
Add the “uUserDM” unit to the Implementation’s uses clause of the uUserForm unit. Select the datControl
component and change the DataSet property to UserDM.cdsControl (Figure 37).

Figure 37 – Changing the DataSet property


Set the DBGridSearch’s DataSource option to datControl. Now, right-click on the DBGridSearch grid, and click on
the “Columns Editor...” option; add all fields and arrange them as shown in Figure 38.

Figure 38 - Columns Editor


Now you need to add the fields to the PageControl’s Edit tab. Go to the UserDM Data Module and open the
cdsControl’s Fields Editor. Keep the Fields Editor open, go back to the UserForm form, select all fields of the Fields
Editor and drag them to the Edit tab (Figure 39).

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 436 -
Figure 39 – Dragging the fields to the TabSheet tab
Change the USER_ID’s DBEdit component Color property to clSkyBlue, its ParentFont property to True, and its
Enabled property to False.
Replace the TDBEdit component of the last added field (Is Admin?) with a TDBComboBox component. Change the
TDBComboBox component properties, as shown in Figure 40.

Figure 40 – Setting DBComboBox’s Datafield property


Get to TDBComboBox’s Items properties window and add two items (YES-NO, as shown in Figure 41).

Figure 41 – Adding items to the DBComboBox component


Go to the code editor and change the form’s global variable name to:

var
fUserForm: TUserForm;
Add the following code to the UserForm’s OnDestroy event; it will assign nil to the fUserForm variable:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 437 -
procedure TUserForm.FormDestroy(Sender: TObject);
begin
inherited;
fUserForm := nil;
end;
The user register form is finished. Now you will create the class responsible for the user business rule.
Add a new unit to the project and save it as “uUserControl.pas”.
Open Model View and add a new class named “TUserControl” to the uUserControl unit. Change its extends
property by selecting the TAbstractControl class in the “Select Class to Extend” dialog box, as shown in Figure 42.

Figure 42 – Setting the TAbstractControl’s Extends property


Add the uUserDM, uUserForm, and Classes units to the uUserControl unit Interface’s uses clause, and add the
Forms unit to the Implementation’s uses clause.
Create the following fields for the TUserControl class:

strict private
var
fUserDM: TUserDM;
public
fUserID: integer;
fUserLogin: string;
fUserName: string;
fUserIsAdmin:boolean;
Add a constructor method named Create (declare it as override) and implement it as follows:

constructor TUserControl.Create(AOwner: TComponent);


begin
fUserDM := nil;
fUserForm := nil;
inherited Create(AOwner);
end;
Add a destructor method named Destroy (declare it as override) and implement it as shown below:

destructor TUserControl.Destroy;
begin
inherited;
end;
Now, add the method that creates the user form into its public section (declare it as override):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 438 -
procedure TUserControl.CreateDefaultForm;
begin
inherited;
if fUserForm = nil then
begin
fUserForm := TUserForm.Create(Self);
fUserForm.datControl.DataSet := fUserDM.cdsControl;
fUserForm.datControl.DataSet.Open;
end
else fUserForm.Show;
end;
Add another method, in the public section again, to create the UserDM Data Module (declare it as override):

procedure TUserControl.CreateDefaultDM;
begin
inherited;
fUserDM := TUserDM.Create(Self);
fUserDM.DBConnection := DBConnection;
end;
Implement the Singleton Pattern to the TUserControl class through the Diagram View, as shown in Figure 43.

Figure 43 – Implementing the Singleton Pattern


The GetInstance method created by the Singleton Pattern should be presented as follows:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 439 -
class function TUserControl.GetInstance: TUserControl;
begin
If FInstance = nil Then
begin
FInstance := uUserControl.TUserControl.Create(Application);
end;
Result := FInstance;
end;
Go to the main form, double-click on the ActionManager component, and, in the ActionManager editor (Figure 44),
add a new action to the File category. Set its Name property as “acUser”, its Caption property as “User”, and link
the corresponding image to the ImageIndex property.

Figure 44 - ActionManager Editor


Add the uUserControl unit to the Implementation’s uses clause of the uMainForm unit and code the “acUser”
action’s onExecute event as follows:

procedure TMainForm.acUserExecute(Sender: TObject);


begin
TUserControl.GetInstance.CreateDefaultForm;
end;
Associate the acUser action to the MainMenu component, as shown in Figure 45.

Figure 45 – Associating the acUser Action to the MainMenu item

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 440 -
Go to the project options (Project | Options) and keep only MainForm form and MainDM as “Auto-create forms”.
Now you need to define some rules for including users. One of these rules is the login duplicity check. So, create the
following method as public in the TUserControl class.

function TUserControl.FindLoginName(UserID: integer;


LoginName: string): boolean;
var
sqlFind: TSQLQuery;
begin
sqlFind := TSQLQuery.Create(nil);
try
sqlFind.SQLConnection := DBConnection;

sqlFind.SQL.Add('SELECT * FROM USERS');


sqlFind.SQL.Add(' WHERE LOGIN = ' + QuotedStr(LoginName));
sqlFind.SQL.Add(' AND USER_ID <> ' + IntToStr(UserID));
sqlFind.Open;

result := (sqlFind.RecordCount > 0);


finally
sqlFind.Free;
end;
end;
Remember to add the SqlExpr and SysUtils units to the uUserControl uses clause.
Add the following code to the TUserDM cdsControl’s BeforePost event:

procedure TUserDM.cdsControlBeforePost(DataSet: TDataSet);


begin
inherited;
CheckRequiredFields(DataSet);

if TUserControl.GetInstance.FindLoginName(
DataSet.FieldByName('USER_ID').AsInteger,
DataSet.FieldByName('LOGIN').AsString) then
raise Exception.Create('Duplicated login name!');

if Dataset.State = dsInsert then


Dataset.FieldByName('USER_ID').AsInteger := GenerateID('GEN_USERS_ID');
end;
Code the method that is responsible for maintaining the user’s authorization log:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 441 -
procedure TUserControl.AuthorizationLog;
var
sqlAutLog: TSQLQuery;
bLocalTrans: boolean;
begin
sqlAutLog := TSQLQuery.Create(nil);
try
sqlAutLog.SQLConnection := DBConnection;

sqlAutLog.SQL.Add('INSERT INTO AUTLOG (');


sqlAutLog.SQL.Add(' AUTLOG_ID,');
sqlAutLog.SQL.Add(' LOGINDATE,');
sqlAutLog.SQL.Add(' LOGINTIME,');
sqlAutLog.SQL.Add(' LOGINNAME');
sqlAutLog.SQL.Add(' ) VALUES (');
sqlAutLog.SQL.Add(' 0, ');
sqlAutLog.SQL.Add('CURRENT_DATE, ');
sqlAutLog.SQL.Add('CURRENT_TIME, ');
sqlAutLog.SQL.Add(QuotedSTR(fUserLogin) + ')');

bLocalTrans := False;
if not DBConnection.InTransaction then
begin
bLocalTrans := True;
DBConnection.StartTransaction(TD);
end;

try
sqlAutLog.ExecSQL(True);
if (bLocalTrans) then
DBConnection.Commit(TD);
except
on E: Exception do
begin
if (bLocalTrans) then
DBConnection.Rollback(TD);
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;
end;

finally
sqlAutLog.Free;
end;
end;
The code below is used to get a user’s email. This method receives the user ID as argument and returns the user’s e-
mail as a string value:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 442 -
function TUserControl.GetUserMail(UserID: Integer): string;
var
sqlFind: TSQLQuery;
begin
sqlFind := TSQLQuery.Create(nil);
try
sqlFind.SQLConnection := DBConnection;

sqlFind.SQL.Add('SELECT EMAIL FROM USERS');


sqlFind.SQL.Add(' WHERE USER_ID = ' + IntToStr(UserID));
sqlFind.Open;

if not sqlFind.IsEmpty then


result := sqlFind.FieldByName('EMAIL').AsString
else
result := '';
finally
sqlFind.Free;
end;
end;
One of Meeting Organizer’s requirements is to send notifications to users through e-mail messages.
To simplify this operation, you’ll create a class that inserts the messages in the database (in a table called
MSGLOG), instead of sending e-mail messages to the users. Later on, you can modify this class’ implementation or
create an application that reads the MSGLOG table’s records and then automatically sends the e-mail messages.
To do so, add a new unit to the project, save it as uMsgControl, and code it as follows:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 443 -
unit uMsgControl;

interface
uses uAbstractControl, Classes;

type
TMsgControl = class(TAbstractControl)
public
procedure PostNotification(email: string; Text: string);
class function GetInstance: TMsgControl;
procedure CreateDefaultForm; override;
procedure CreateDefaultDM; override;
strict private
class var
FInstance: TMsgControl;
end;

implementation

uses
Forms, SqlExpr, SysUtils;

procedure TMsgControl.PostNotification(email: string; Text: string);


var
sqlPost: TSQLQuery;
bLocalTrans: boolean;
begin
sqlPost := TSQLQuery.Create(nil);
try
sqlPost.SQLConnection := DBConnection;

sqlPost.SQL.Add('INSERT INTO MSGLOG (');


sqlPost.SQL.Add(' MSGLOG_ID,');
sqlPost.SQL.Add(' EMAIL,');
sqlPost.SQL.Add(' MSG,');
sqlPost.SQL.Add(' SENT');
sqlPost.SQL.Add(' ) VALUES (');
sqlPost.SQL.Add(' 0, ');
sqlPost.SQL.Add(QuotedSTR(email) + ', ');
sqlPost.SQL.Add(QuotedSTR(Text) + ', ');
sqlPost.SQL.Add(QuotedSTR('N') + ')');

bLocalTrans := False;
if not DBConnection.InTransaction then
begin
bLocalTrans := True;
DBConnection.StartTransaction(TD);
end;

try
sqlPost.ExecSQL(True);
if (bLocalTrans) then
DBConnection.Commit(TD);
except
on E: Exception do
begin
if (bLocalTrans) then
DBConnection.Rollback(TD);
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 444 -
end;

finally
sqlPost.Free;
end;
end;

class function TMsgControl.GetInstance: TMsgControl;


begin
if FInstance = nil then
begin
FInstance := uMsgControl.TMsgControl.Create(Application);
end;
Result := FInstance;
end;

procedure TMsgControl.CreateDefaultForm;
begin
inherited;
end;

procedure TMsgControl.CreateDefaultDM;
begin
inherited;
end;

end.

Notice that the CreateDefaultForm and CreateDefaultDM methods are only declared because they are
abstract methods in TMsgControl’s ancestor class. The TMsgControl class implements the Singleton
Pattern.

Go to the UserDM Data Module and add the following code to the dspControl’s BeforeUpdateRecord event, in
order to notify a user that he or she has a new account.

procedure TUserDM.dspControlBeforeUpdateRecord(Sender: TObject;


SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;
var Applied: Boolean);
var
msg: string;
begin
inherited;
if UpdateKind = ukInsert then
begin
msg := 'You have a new account on the Meeting Organizer!' + #13#10 +
'Login Name: ' + DeltaDS.FieldByName('LOGIN').AsString + #13#10 +
'Password: ' + DeltaDS.FieldByName('PASSW').AsString;

TMsgControl.GetInstance.PostNotification(DeltaDS.FieldByName('EMAIL').AsString,
msg);
end;
end;
It’s time to create the Room registration. The steps involved in creating the Room register are similar to those used
to create the user register. First of all, add a new Data Module descending from AbstractDataModule. Save the new
Data Module unit as “uRoomDM.pas” and change its name property to RoomDM.
Add the uMainDM unit to the Implementation’s uses clause of the uRoomDM unit.
Select the sqlControl component, change the SQLConnection property to MainDM->SQLConnection, and then add
the following SQL statement to its CommandText property:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 445 -
select CAPACITY, LOCATION, NAME, ROOM_ID from ROOM
Add all fields to the sqlControl and cdsControl components. Change the ROOM_ID field’s pfInKey property of both
sqlControl and cdsControl to True, and their DefaultExpression property to 0.
Change cdsControl’s fields DisplayLabel property as follows:

FieldName DisplayLabel

ROOM_ID Room ID

NAME Name

LOCATION Location

CAPACITY Capacity
Table 13 - Setting the DisplayLabel property of the field
Now, create the room registration form. Choose File | New | Other – Inheritable Items, select the
AbstractDataTabForm icon, and click on the OK button. Save the unit as “uRoomForm.pas”. Change its Name
property to “RoomForm” and its Caption property to “Room”.
Add the uRoomDM unit to RoomForm’s uses clause. Select the datControl component and change its Dataset
property to RoomDM.cdsControl.
Now, add the fields to the DBGridSearch, as shown in Figure 46.

Figure 46 – Adding the fields to the Columns Editor


Now you have to add the cdsControl fields to the “Edit” tab. Go to the RoomDM Data Module and open the
cdsControl Fields Editor. Go back to the RoomForm form, select all fields of the Fields Editor and drag them, as
shown in Figure 47.

Figure 47 – Dragging the fields to the TabSheet tab

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 446 -
Change the ROOM_ID’s DBEdit component Color property to clSkyBlue, its ParentFont property to True, and its
Enabled property to False.
Go to the code editor and change the form’s global variable name to:

var
fRoomForm: TRoomForm;
Add the following code to the RoomForm’s OnDestroy event. It will assign nil to the fRoomForm variable:

procedure TRoomForm.FormDestroy(Sender: TObject);


begin
inherited;
fRoomForm := nil;
end;
At this moment, you should create the Room entity’s control class. Choose File | New | Unit and save the new unit
as “uRoomControl.pas”.
Create a new class descending from TAbstractControl in the uRoomControl unit and name it TRoomControl.
Create a field in the new class (as shown below) and add the uRoomDM and uRoomForm units to the Interface’s
uses clause.

TRoomControl = class(TAbstractControl)
strict private
fRoomDM : TRoomDM;
Add a constructor method (declare it as override) to the class:

constructor TRoomControl.Create(AOwner: TComponent);


begin
fRoomDM := nil;
fRoomForm := nil;
inherited Create(AOwner);
end;
Add a destructor method (declare it as override) to the class:

destructor TRoomControl.Destroy;
begin
inherited;
end;
Create the method that will instance the registration form (declare it as override):

procedure TRoomControl.CreateDefaultForm;
begin
inherited;
if fRoomForm = nil then
begin
fRoomForm := TRoomForm.Create(Self);
fRoomForm.datControl.DataSet := fRoomDM.cdsControl;
fRoomForm.datControl.DataSet.Open;
end
else fRoomForm.Show;
end;
Create the method that will instance the RoomDM Data Module (declare it as override):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 447 -
procedure TRoomControl.CreateDefaultDM;
begin
inherited;
fRoomDM := TRoomDM.Create(Self);
fRoomDM.DBConnection := DBConnection;
end;
Add the method that will be responsible for verifying whether the room name that was typed by the user already
exists. Add the SqlExpr and SysUtils units to the Implementation’s uses clause.

function TRoomControl.FindRoomName(RoomID: integer; Name: string): boolean;


var
sqlFind: TSQLQuery;
begin
sqlFind := TSQLQuery.Create(nil);
try
sqlFind.SQLConnection := DBConnection;

sqlFind.SQL.Add('SELECT * FROM ROOM');


sqlFind.SQL.Add(' WHERE NAME = ' + QuotedStr(Name));
sqlFind.SQL.Add(' AND ROOM_ID <> ' + IntToStr(RoomID));
sqlFind.Open;

result := (sqlFind.RecordCount > 0);


finally
sqlFind.Free;
end;
end;
Now you only need to add the Singleton Pattern to the TRoomControl class. The GetInstance method, created by the
Singleton Pattern, should look like this:

class function TRoomControl.GetInstance: TRoomControl;


begin
If FInstance = nil Then
begin
FInstance := uRoomControl.TRoomControl.Create(Application);
end;
Result := FInstance;
end;
Next, code the cdsControl’s BeforePost event of the RoomDM Data Module as follows:

procedure TRoomDM.cdsControlBeforePost(DataSet: TDataSet);


begin
CheckRequiredFields(DataSet);

if TRoomControl.GetInstance.FindRoomName(
DataSet.FieldByName('ROOM_ID').AsInteger,
DataSet.FieldByName('NAME').AsString) then
raise Exception.Create('Duplicated room name!');

if Dataset.State = dsInsert then


Dataset.FieldByName('ROOM_ID').AsInteger := GenerateID('GEN_ROOM_ID');
end;
Create a new Action in the main form ActionManager component. Name the new action “acRoom”, change its
Caption property to “Room”, and link the corresponding image to the ImageIndex property

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 448 -
Figure 48 – Creating the acRoom action
Add the “uRoomControl” unit to the uMainForm unit uses clause. Code acRoom action’s onExecute event as
follows:

procedure TMainForm.acRoomExecute(Sender: TObject);


begin
inherited;
TRoomControl.GetInstance.CreateDefaultForm;
end;
Remember to associate the acRoom action to the MainMenu component. Finally, remove RoomForm and the
RoomDM Data Module from the “Auto-create forms” section (Project | Options).
In the next step, you’ll create the Meeting Manager form. For the meeting manager, you will need the following
items:
• A Data Module (“MeetingDM”) to represent the Model
• A form (“MeetingForm”) to represent the View
• A class for the business rules (“TMeetingControl”) to represent the Control
• A form for managing the meetings (“MeetingAddForm”)
• A form for selecting the participants (“MeetingParticipantsForm”)
• A form for selecting the available meeting room (“MeetingRoomForm”)
• A form for selecting the available timeslots for a certain room (“MeetingTimeForm”)
Start by creating the Data Module. Choose File | New | Other | Inheritable Items, select the AbstractDataModule
icon, and click on OK. Save the Data Module unit as “uMeetingDM.pas” and change its Name property to
“MeetingDM”.
Add the uMainDM unit to the uMeetingDM Implementation uses clause. Select the sqlControl component and
change its SQLConnection property to MainDM.SQLConnection. Add the following SQL statement to the
CommandText property:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 449 -
SELECT M.MEETING_ID,
M.TOPIC,
M.DURATION,
M.STARTDATE,
M.STARTTIME,
M.USER_ID,
U.NAME AS USER_NAME,
M.ROOM_ID,
R.NAME AS ROOM_NAME,
M.LASTCHANGE
FROM MEETING M
LEFT OUTER JOIN USERS U ON (M.USER_ID = U.USER_ID)
LEFT OUTER JOIN ROOM R ON (M.ROOM_ID = R.ROOM_ID)
WHERE M.STARTDATE >= 'TODAY'
Add the fields to the sqlControl and cdsControl components (Figure 49).

Figure 49 – Adding the fields


In the Fields Editor, select the MEETING_ID field, change its pfInKey property to True and its DefaultExpression
property to 0, as shown in Figure 50. Follow this instruction not only for the cdsControl component but also for the
sqlControl component.

Figure 50 – Changing the value of the pfKey property


Two of the fields (NAME_USER and NAME_ROOM) that were included in the SQL statement are not part of the
Meeting table (they were brought through a join). These fields cannot be updated in the database and therefore they
should not be included in INSERT or UPDATE statements. To solve this problem, select the NAME_USER and
NAME_ROOM fields and change their pfInUpdate and pfInWhere properties to False (it should be done in the
sqlControl and cdsControl components).

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 450 -
Figure 51 – Changing the pfInUpdate and pfInWhere properties
Change the DisplayLabel of the cdsControl fields according to Table 14.

FieldName DisplayLabel EditMask

MEETING_ID Meeting ID

TOPIC Topic

DURATION Duration

STARTDATE Date !99/99/0000;1;_

STARTTIME Start Time !90:00:00;1;_

USER_ID User ID

USER_NAME User Name

ROOM_ID Room ID

ROOM _NAME Room Name

LASTCHANGE Last Change


Table 14 – Setting the DisplayLabel field properties
Now add the following components:

Type Name

TSQLDataset sqlParticipants

TDataSetProvider dspParticipants

TClientDataSet cdsParticipants
Table 15 – Component names

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 451 -
Figure 52 - MeetingDM Data Module components
Select the sqlParticipants component, change its SQLConnection property to MainDM->SQLConnection, and add
the following SQL statement to its CommandText property:

SELECT PAR.MEETING_ID,
PAR.USER_ID,
USE.NAME AS USER_NAME
FROM PARTICIPANT PAR
LEFT OUTER JOIN USERS USE ON (PAR.USER_ID = USE.USER_ID)
WHERE PAR.MEETING_ID = :MEETING_ID
Notice that in this SQL statement a parameter (:MEETING_ID) is being defined. In the Params property of the
sqlParticipants component, set the DataType property to ftInteger:

Figure 53 – Adding the MEETING_ID parameter


Select dspParticipants and change its DataSet property to sqlParticipants. Select cdsParticipants and change its
ProviderName property to dspParticipants. Add all fields to the cdsParticipants and sqlParticipants components.
Since there is a field that doesn’t belong to the Participants table (USER_NAME), change its pfInUpdate and
pfInWhere properties to False (do it to the cdsParticipants and sqlParticipants components). See below:

Figure 54–Changing the pfInWhere property


In order to list the users that can be added as participants to a meeting, add the components listed in Table 16 to the
MeetingDM DataModule:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 452 -
Component Name

SqlDataSet sqlSearchPart

DataSetProvider dspSearchPart

ClientDataSet cdsSearchPart
Table 16 – MeetingDM search components

Figure 55 – MeetingDM search components


Select the sqlSearchPart component, change its SQLConnection property to MainDM->SQLConnection and add the
following SQL statement to its CommandText property:

SELECT U.USER_ID,
U.NAME AS USER_NAME
FROM USERS U
WHERE U.USER_ID NOT IN (SELECT P.USER_ID
FROM PARTICIPANT P
WHERE P.MEETING_ID = :MEETING_ID)
Change the MEETING_ID parameter DataType property to ftInteger.
Select dspSearchPart and change the DataSet property to sqlSearchPart. Select cdsSearchPart and change the
ProviderName property to dspSearchPart. Add all fields to the cdsSearchPart and sqlSearchPart components.
Now, change the Options property of dspParticipants and dspSearchPart of the MeetingDM Data Module to have the
following configuration:

Figure 56 – Option property configuration


Now, create the form that will be used to view the arranged meetings. Choose File | New | Other – Inheritable
Items, select the AbstractDataTabForm icon, and click on OK. Save the unit as “uMeetingForm.pas”, change its
Name property to “MeetingForm” and its Caption property to “Meeting”.
Add the uMeetingDM unit to the MeetingForm uses clause. Change the datControl’s DataSet property to
MeetingDM.cdsControl.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 453 -
Figure 57 - Setting the datControl’s DataSet property
Drag the MeetingDM’s cdsControl fields (Figure 58) to the MeetingForm:

Figure 58 – Dragging the cdsControl fields to the MeetingForm


Add the components listed in Table 17 and configure the MeetingForm as shown in Figure 59.

Component Name Caption/Text DataSource DataSet

DataSource datSearch MeetingDM->cdsSearchPart

DataSource datParticipants MeetingDM->cdsParticipants

TLabel lblSearch Search…

TEdit edtSearch <empty>

TBitBtn butUserSearch <empty>

TDBGrid dbgSearch datSearch

TDBGrid dbgParticipants datParticipants

TSpeedButton butUserAdd <empty>

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 454 -
Component Name Caption/Text DataSource DataSet

TSpeedButton butUserDel <empty>


Table 17 – MeetingForm components

Figure 59 – MeetingForm
Configure the other TDBGrid properties as follows (Table 18):

Name Options ParentFont ReadOnly

dbgSearch dgEditing = False false True


dgColLines = False
dbRowLines = False

dbgParticipants dgEditing = False false True


dgColLines = False
dbRowLines = False
Table 18 – Other TDBDataGrid properties
Click on the PageControl’s Search tab and set the DBGridSearch’s DataSource option to datControl. Now, right-
click on the DBGridSearch grid, click on the “Columns Editor...” option, add all fields and arrange them, as shown
in Figure 60.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 455 -
Figure 60 - Setting the grid columns
Select the sqlParticipants field and change its Visible property to False.
Rename the global variable MeetingForm to fMeetingForm and code the MeetingForm’s OnDestroy event as
follows:

procedure TMeetingForm.FormDestroy(Sender: TObject);


begin
inherited;
fMeetingForm := nil;
end;
Code the method that adds a user as a participant of a meeting (butUserAdd Buttons’s OnClick event):

procedure TMeetingForm.butUserAddClick(Sender: TObject);


begin
inherited;
if datControl.State in [dsInsert, dsEdit] then
begin

datParticipants.DataSet.InsertRecord([datControl.DataSet.FieldByName('MEETING_ID'
).AsInteger,

datSearch.DataSet.FieldByName('USER_ID').AsInteger,

datSearch.DataSet.FieldByName('USER_NAME').AsString]);
datSearch.DataSet.Delete;
end;
end;
Code the method that removes a participant from a meeting (butDelAdd Buttons’s OnClick event):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 456 -
procedure TMeetingForm.butUserDelClick(Sender: TObject);
begin
inherited;
if datControl.State in [dsInsert, dsEdit] then
begin

datSearch.DataSet.InsertRecord([datParticipants.DataSet.FieldByName('USER_ID').As
Integer,

datParticipants.DataSet.FieldByName('USER_NAME').AsString]);
datParticipants.DataSet.Delete;
end;
end;
Code the method that enables/disables some buttons of the form, according to the state of the datControl datasource
(datControl’s OnStateChange event):

procedure TMeetingForm.datControlStateChange(Sender: TObject);


begin
inherited;
butUserAdd.Enabled := (datControl.State in [dsInsert, dsEdit]) and not
datSearch.DataSet.IsEmpty;
butUserDel.Enabled := (datControl.State in [dsInsert, dsEdit]) and not
datParticipants.DataSet.IsEmpty;
butTimeRoomForm.Enabled := datControl.State in [dsInsert, dsEdit];
end;
Code the method that enables/disables the butUserAdd button, according to the state of the datControl datasource
(datSearch’s OnStateChange event):

procedure TMeetingForm.datSearchDataChange(Sender: TObject; Field: TField);


begin
inherited;
butUserAdd.Enabled := (datControl.State in [dsInsert, dsEdit]) and not
datSearch.DataSet.IsEmpty;
end;
Code the method that enables/disables the butUserDel button, according to the state of the datControl datasource
(datParticipants’s OnStateChange event):

procedure TMeetingForm.datParticipantsDataChange(Sender: TObject;


Field: TField);
begin
inherited;
butUserDel.Enabled := (datControl.State in [dsInsert, dsEdit]) and not
datParticipants.DataSet.IsEmpty;
end;
Before finishing the MeetingForm implementation, you should create the control class, which has some methods
MeetingForm depends on.
Now, create the control class. Choose File | New | Unit and save it as “uMeetingControl.pas”.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 457 -
Figure 61 – Adding a new Unit to the project
Create a class named TMeetingControl, descending from TAbstractControl. Add the uMeetingDM and
uAbstractControl units to the Interface’s uses clause, and the uMeetingForm unit to the Implementation’s uses
clause. Add two fields to the new class, as shown in the code below:

strict private
var
fMeetingDM: TMeetingDM;
Add a constructor method (declare it as override), and code it as follows:

constructor TMeetingControl.Create(AOwner: TComponent);


begin
fMeetingDM := nil;
fMeetingForm := nil;
inherited Create(AOwner);
end;
Add a destructor method (declare it as override) and code it as follows:

destructor TMeetingControl.Destroy;
begin
inherited;
end;
Remember to add the Classes unit to the Interface’s uses clause.
Add the method (declare it as override) that will create the MeetingForm form:

procedure TMeetingControl.CreateDefaultForm;
begin
inherited;
if fMeetingForm = nil then
begin
fMeetingForm := TMeetingForm.Create(Self);

fMeetingForm.datControl.DataSet := fMeetingDM.cdsControl;
fMeetingForm.datSearch.DataSet := fMeetingDM.cdsSearchPart;
fMeetingForm.datParticipants.DataSet := fMeetingDM.cdsParticipants;
fMeetingForm.datControl.DataSet.Open;
end
else
fMeetingForm.Show;
end;
Remember to include the Forms unit to the Implementation’s uses clause.
Add a method (declare it as override) that will create the MeetingDM Data Module.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 458 -
procedure TMeetingControl.CreateDefaultDM;
begin
inherited;
fMeetingDM := TMeetingDM.Create(Self);
fMeetingDM.DBConnection := DBConnection;
end;
Implement the Singleton Pattern over the TMeetingControl class. The GetInstance method should look like this:

class function TMeetingControl.GetInstance: TMeetingControl;


begin
If FInstance = nil Then
begin
FInstance := uMeetingControl.TMeetingControl.Create(Application);
end;
Result := FInstance;
end;
Add a public method that will verify if the meeting date is valid:

function TMeetingControl.CheckMeetingDate: boolean;


begin
result := (fMeetingDM.cdsControl.FieldByName('STARTDATE').AsDateTime >= Date);
end;
Add a public method that will verify if the user trying to edit a meeting is the meeting owner:

function TMeetingControl.CheckMeetingOwner: boolean;


begin
result := (TUserControl.GetInstance.fUserID =
fMeetingDM.cdsControl.FieldByName('USER_ID').AsInteger);
end;
Add a public method that will verify if the meeting has at least 2 participants:

function TMeetingControl.CheckNumberOfParts: boolean;


begin
result := (fMeetingDM.cdsParticipants.RecordCount >= 2);
end;
Before updating the database, the application should verify if a participant is not busy at that moment. To do so, add
the following method to the MeetingDM Data Module. This method will return True in case the participant is
available; otherwise it will return False:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 459 -
function TMeetingControl.ValidateParticipants(Meeting_ID: integer; User_ID:
Integer;
StartDate: TDateTime; StartTime: TTime; Duration: integer): boolean;
var sqlMeetingUser: TSQLQuery;
MeetingStart, MeetingEnd: TDateTime;
begin
MeetingStart := StartTime;
MeetingEnd := IncMinute(MeetingStart, Duration);

sqlMeetingUser := TSQLQuery.Create(nil);
sqlMeetingUser.SQLConnection := DBConnection;
try
try
sqlMeetingUser.SQL.Add('SELECT MEE.STARTTIME AS MEETING_START,');
sqlMeetingUser.SQL.Add(' MEE.STARTTIME+(MEE.DURATION*60) AS
MEETING_END');
sqlMeetingUser.SQL.Add(' FROM MEETING MEE');
sqlMeetingUser.SQL.Add(' INNER JOIN PARTICIPANT PAR ON (MEE.MEETING_ID =
PAR.MEETING_ID)');
sqlMeetingUser.SQL.Add(' WHERE MEE.MEETING_ID <> ' +
IntToStr(Meeting_ID));
sqlMeetingUser.SQL.Add(' AND MEE.STARTDATE = ' + DataFormat(StartDate,
'D'));
sqlMeetingUser.SQL.Add(' AND PAR.USER_ID = ' + IntToStr(User_ID));

Result := True;
sqlMeetingUser.Open;
While not sqlMeetingUser.Eof do
begin
Result := not (
(
(MeetingStart >=
sqlMeetingUser.FieldByName('MEETING_START').AsDateTime) and
(MeetingStart <=
sqlMeetingUser.FieldByName('MEETING_END').AsDateTime)
) or
(
(MeetingEnd >=
sqlMeetingUser.FieldByName('MEETING_START').AsDateTime) and
(MeetingEnd <=
sqlMeetingUser.FieldByName('MEETING_END').AsDateTime)
)
);
sqlMeetingUser.Next;
end;
except on E: Exception do
begin
result := False;
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;
end;
finally
sqlMeetingUser.Free;
end;
end;
Before updating the database, the application should also verify if the room is not busy at that moment. To do so,
add the following method to the MeetingDM Data Module. This method will return True in case the room is
available; otherwise it ill return False:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 460 -
function TMeetingControl.ValidateMeetingRoom(Meeting_ID: integer; Room_ID:
Integer;
StartDate: TDateTime; StartTime: TTime; Duration: integer): boolean;
var sqlMeetingRoom: TSQLQuery;
MeetingStart, MeetingEnd: TDateTime;
begin
MeetingStart := StartTime;
MeetingEnd := IncMinute(MeetingStart, Duration);

sqlMeetingRoom := TSQLQuery.Create(nil);
sqlMeetingRoom.SQLConnection := DBConnection;
try
try
sqlMeetingRoom.SQL.Add('SELECT MEE.STARTTIME AS MEETING_START,');
sqlMeetingRoom.SQL.Add(' MEE.STARTTIME+(MEE.DURATION*60) AS
MEETING_END');
sqlMeetingRoom.SQL.Add(' FROM MEETING MEE');
sqlMeetingRoom.SQL.Add(' WHERE MEE.ROOM_ID = ' + IntToStr(Room_ID));
sqlMeetingRoom.SQL.Add(' AND MEE.MEETING_ID <> ' +
IntToStr(Meeting_ID));
sqlMeetingRoom.SQL.Add(' AND MEE.STARTDATE = ' + DataFormat(StartDate,
'D'));

Result := True;
sqlMeetingRoom.Open;
While not sqlMeetingRoom.Eof do
begin
Result := not (
(
(MeetingStart >=
sqlMeetingRoom.FieldByName('MEETING_START').AsDateTime) and
(MeetingStart <=
sqlMeetingRoom.FieldByName('MEETING_END').AsDateTime)
) or
(
(MeetingEnd >=
sqlMeetingRoom.FieldByName('MEETING_START').AsDateTime) and
(MeetingEnd <=
sqlMeetingRoom.FieldByName('MEETING_END').AsDateTime)
)
);
sqlMeetingRoom.Next;
end;
except on E: Exception do
begin
result := False;
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;
end;
finally
sqlMeetingRoom.Free;
end;
end;
After a meeting is validated and posted to the database, the application should notify the participants. To do so, add
the following method to the MeetingDM DataModule:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 461 -
procedure TMeetingControl.SendMeetingNotification(User_ID: integer; StartDate:
TDateTime;
StartTime: TTime; Duration: integer; RoomName: string; Topic: string;
Action: integer);
var
sUserMsg: string;
sUserMail: string;
begin
case Action of
0: sUserMsg := 'Your Meeting has been modified!' + #13;
1: sUserMsg := 'You have a new Meeting!' + #13;
2: sUserMsg := 'Your Meeting has been canceled!' + #13;
end;
sUserMsg := sUserMsg + 'Date: ' + DateToStr(StartDate) + #13 +
'Hour: ' + TimeToStr(StartTime) + #13 +
'Duration: ' + IntToStr(Duration) + #13 +
'Room: ' + RoomName + #13 +
'Topic: ' + Topic;

sUserMail := TUserControl.GetInstance.GetUserMail(User_ID);
TMsgControl.GetInstance.PostNotification(sUserMail, sUserMsg);
end;
In the dspControl BeforeUpdateRecord event, insert the method below. It will call the ValidateParticipants,
ValidateMeetingRoom, and SendMeetingNotification methods:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 462 -
procedure TMeetingDM.dspControlBeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
inherited;
if SourceDS.Name = 'sqlControl' then
begin
{ Validating meeting room }
if not TMeetingControl.GetInstance.ValidateMeetingRoom(

DeltaDS.FieldByName('MEETING_ID').AsInteger,
DeltaDS.FieldByName('ROOM_ID').AsInteger,

DeltaDS.FieldByName('STARTDATE').AsDateTime,

DeltaDS.FieldByName('STARTTIME').AsDateTime,

DeltaDS.FieldByName('DURATION').AsInteger) then
raise Exception.Create('There is no more availability for this room!');

cdsParticipants.First;
While not cdsParticipants.Eof do
begin

{ Validating meeting participants }


if (UpdateKind = ukInsert) or (UpdateKind = ukModify) then
if not TMeetingControl.GetInstance.ValidateParticipants(

DeltaDS.FieldByName('MEETING_ID').AsInteger,

cdsParticipants.FieldByName('USER_ID').AsInteger,

DeltaDS.FieldByName('STARTDATE').AsDateTime,

DeltaDS.FieldByName('STARTTIME').AsDateTime,

DeltaDS.FieldByName('DURATION').AsInteger) then
raise Exception.Create('There is no more availability for user ' +

cdsParticipants.FieldByName('USER_NAME').AsString + '!');

{ Sending meeting notification }


TMeetingControl.GetInstance.SendMeetingNotification(

cdsParticipants.FieldByName('USER_ID').AsInteger,
DeltaDS.FieldByName('STARTDATE').AsDateTime,
DeltaDS.FieldByName('STARTTIME').AsDateTime,
DeltaDS.FieldByName('DURATION').AsInteger,
DeltaDS.FieldByName('ROOM_NAME').AsString,
DeltaDS.FieldByName('TOPIC').AsString,
Integer(UpdateKind));

{ If the meeting was canceled then remove participants }


if UpdateKind = ukDelete then
cdsParticipants.Delete
else
cdsParticipants.Next;
end;

if UpdateKind in [ukModify, ukDelete] then

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 463 -
cdsParticipants.ApplyUpdates(0);
end;
end;
Add a public method to the MeetingControl, which will be responsible for retrieving a free time list, based on the
date and duration of a meeting:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 464 -
function TMeetingControl.GetFreeTimeList(Meeting_ID: integer; StartDate: TDate;
Duration: integer): TClientDataSet;
var bFree: boolean;
sUsers: string;
sqlMeeting, sqlTimeSlot: TSQLQuery;
MeetingStart, MeetingEnd: TDateTime;
begin
result := TClientDataSet.Create(Self);
result.FieldDefs.Add('FREE_TIME', ftTime);
result.CreateDataSet;
result.Fields[0].DisplayLabel := 'Meeting Time';

sqlTimeSlot := TSQLQuery.Create(nil);
sqlTimeSlot.SQLConnection := DBConnection;
sqlMeeting := TSQLQuery.Create(nil);
sqlMeeting.SQLConnection := DBConnection;
try
try
sUsers := '';
fMeetingDM.cdsParticipants.First;
while not fMeetingDM.cdsParticipants.Eof do
begin
if sUsers <> '' then
sUsers := sUsers + ', ';
sUsers := sUsers +
fMeetingDM.cdsParticipants.FieldByName('USER_ID').AsString;
fMeetingDM.cdsParticipants.Next;
end;

sqlMeeting.SQL.Add('SELECT PAR.USER_ID,');
sqlMeeting.SQL.Add(' MEE.STARTTIME AS MEETING_START,');
sqlMeeting.SQL.Add(' MEE.STARTTIME+(MEE.DURATION*60) AS
MEETING_END');
sqlMeeting.SQL.Add(' FROM MEETING MEE');
sqlMeeting.SQL.Add(' INNER JOIN PARTICIPANT PAR ON (MEE.MEETING_ID =
PAR.MEETING_ID)');
sqlMeeting.SQL.Add(' WHERE MEE.MEETING_ID <> ' + IntToStr(Meeting_ID));
sqlMeeting.SQL.Add(' AND MEE.STARTDATE = ' + DataFormat(StartDate,
'D'));
sqlMeeting.SQL.Add(' AND PAR.USER_ID IN (' + sUsers + ')');

sqlTimeSlot.SQL.Add(' SELECT TIM.MEETINGTIME');


sqlTimeSlot.SQL.Add(' FROM TIMESLOTS TIM');
sqlTimeSlot.SQL.Add('ORDER BY TIM.MEETINGTIME');

sqlTimeSlot.Open;
While not sqlTimeSlot.Eof do
begin
MeetingStart := sqlTimeSlot.FieldByName('MEETINGTIME').AsDateTime;
MeetingEnd := IncMinute(MeetingStart, Duration);

bFree := True;
sqlMeeting.Open;
While not sqlMeeting.Eof and bFree do
begin
bFree := not (
(
(MeetingStart >=
sqlMeeting.FieldByName('MEETING_START').AsDateTime) and

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 465 -
(MeetingStart <=
sqlMeeting.FieldByName('MEETING_END').AsDateTime)
) or
(
(MeetingEnd >=
sqlMeeting.FieldByName('MEETING_START').AsDateTime) and
(MeetingEnd <=
sqlMeeting.FieldByName('MEETING_END').AsDateTime)
)
);
sqlMeeting.Next;
end;
sqlMeeting.Close;

if bFree then
Result.InsertRecord([MeetingStart]);

sqlTimeSlot.Next;
end;
except on E: Exception do
begin
result.EmptyDataSet;
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;
end;
finally
sqlMeeting.Free;
sqlTimeSlot.Free;
end;
end;
Add a public method to MeetingControl. It will be responsible for retrieving a free room list, based on the start date,
start time, and duration of the meeting:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 466 -
function TMeetingControl.GetFreeRoomList(Meeting_ID: integer; StartDate: TDate;
StartTime: TTime; Duration: integer): TClientDataSet;
var bFree: boolean;
sqlMeeting, sqlRoomList: TSQLQuery;
MeetingStart, MeetingEnd: TDateTime;
begin
result := TClientDataSet.Create(Self);
result.FieldDefs.Add('ROOM_ID', ftInteger);
result.FieldDefs.Add('NAME', ftString, 30);
result.FieldDefs.Add('LOCATION', ftString, 30);
result.FieldDefs.Add('CAPACITY', ftInteger);
result.CreateDataSet;
result.Fields[0].DisplayLabel := 'Room ID';
result.Fields[1].DisplayLabel := 'Room Name';
result.Fields[2].DisplayLabel := 'Location';
result.Fields[3].DisplayLabel := 'Capacity';

sqlMeeting := TSQLQuery.Create(nil);
sqlMeeting.SQLConnection := DBConnection;
sqlRoomList := TSQLQuery.Create(nil);
sqlRoomList.SQLConnection := DBConnection;
try
try
MeetingStart := StartTime;
MeetingEnd := IncMinute(MeetingStart, Duration);

sqlRoomList.SQL.Add(' SELECT ROO.ROOM_ID,');


sqlRoomList.SQL.Add(' ROO.NAME,');
sqlRoomList.SQL.Add(' ROO.LOCATION,');
sqlRoomList.SQL.Add(' ROO.CAPACITY');
sqlRoomList.SQL.Add(' FROM ROOM ROO');
sqlRoomList.SQL.Add('ORDER BY ROO.ROOM_ID');

sqlRoomList.Open;
while not sqlRoomList.Eof do
begin
sqlMeeting.SQL.Clear;
sqlMeeting.SQL.Add('SELECT MEE.STARTTIME AS MEETING_START,');
sqlMeeting.SQL.Add(' MEE.STARTTIME+(MEE.DURATION*60) AS
MEETING_END');
sqlMeeting.SQL.Add(' FROM MEETING MEE');
sqlMeeting.SQL.Add(' WHERE MEE.MEETING_ID <> ' +
IntToStr(Meeting_ID));
sqlMeeting.SQL.Add(' AND MEE.ROOM_ID = ' +
sqlRoomList.FieldByName('ROOM_ID').AsString);
sqlMeeting.SQL.Add(' AND MEE.STARTDATE = ' + DataFormat(StartDate,
'D'));

bFree := True;
sqlMeeting.Open;
While not sqlMeeting.Eof and bFree do
begin
bFree := not (
(
(MeetingStart >=
sqlMeeting.FieldByName('MEETING_START').AsDateTime) and
(MeetingStart <=
sqlMeeting.FieldByName('MEETING_END').AsDateTime)
) or
(

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 467 -
(MeetingEnd >=
sqlMeeting.FieldByName('MEETING_START').AsDateTime) and
(MeetingEnd <=
sqlMeeting.FieldByName('MEETING_END').AsDateTime)
)
);
sqlMeeting.Next;
end;

if bFree then
Result.InsertRecord([sqlRoomList.FieldByName('ROOM_ID').AsInteger,
sqlRoomList.FieldByName('NAME').AsString,
sqlRoomList.FieldByName('LOCATION').AsString,

sqlRoomList.FieldByName('CAPACITY').AsInteger]);

sqlRoomList.Next;
end;
except on E: Exception do
begin
result.EmptyDataSet;
raise Exception.Create(MO_ExceptionMsg + E.Message);
end;
end;
finally
sqlMeeting.Free;
end;
end;
In the cdsControl’s OnNewRecord event, add the following code to assign the current user as the meeting owner:

procedure TMeetingDM.cdsControlNewRecord(DataSet: TDataSet);


begin
inherited;
{ Meeting Owner }
cdsControl.FieldByName('USER_ID').AsInteger :=
TUserControl.GetInstance.fUserID;
end;
Now you will implement the functionality to allocate the start schedule and the room for a meeting.
To do so, follow these steps:
• Create a form to be used to choose the desired schedule and room
• Implement the logic in the uMeetingControl unit
First of all, create the form used to choose schedule and room for a meeting. Choose File | New | Other |
Inheritable Items, select AbstractForm, and click on OK. Save the unit as uMeetingTimeRoomForm.
Change the following MeetingTimeRoomForm form properties:

Property Value

BorderIcons.biSystemMenu True

BorderIcons.biMinimize False

BorderIcons.biMaximize False

BorderIcons.biHelp False

BorderStyle bsSingle

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 468 -
Property Value

Caption Meeting Time and Room

Name MeetingTimeRoomForm

Position poMainFormCenter
Table 19 – MeetingTimeRoomForm form properties
Add two TDataSource components to MeetingTimeRoomForm and configure it as in Table 20

Name AutoEdit

datFreeTimeList False

datFreeRoomList False
Table 20 – TDataSource properties
Add the components listed in Table 21 and 22 to MeetingTimeRoomForm and configure the layout, as shown in
Figure 62:

Component Name Options ParentFont ReadOnly DataSource

TDBGrid dbgFreeTimeList dgEditing = False False True datFreeTimeList


dgColLines = False
dbRowLines = False

TDBGrid dbgFreeRoomList dgEditing = False False True datFreeRoomList


dgColLines = False
dbRowLines = False
Table 21 - MeetingTimeRoomForm DBGrids configuration

Component Name ModalResult Align

TPanel pnlBottom AllBottom

TBitBtn btnOK mrOK

TBitBtn btnCancel mrCancel


Table 22 - MeetingTimeRoomForm TPanel and TButton configuration

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 469 -
Figure 62 – MeetingTimeRoomForm form
Now, add the uMeetingControl unit to the uMeetingTimeRoomForm uses clause:

implementation

uses uMeetingControl;
Add the following properties to the public section of the TMeetingTimeRoomForm class:

public
{ Public declarations }
Meeting_ID: integer;
StartDate: TDateTime;
Duration: integer;
Code the MeetingTimeRoomForm’s OnFormShow event as follows:

procedure TMeetingTimeRoomForm.FormShow(Sender: TObject);


begin
inherited;
datFreeTimeList.DataSet :=
TMeetingControl.GetInstance.GetFreeTimeList(Meeting_ID,
StartDate,
Duration);
datFreeTimeList.DataSet.First;
end;
Code the datFreeTimeList’s OnDataChange event as follows:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 470 -
procedure TMeetingTimeRoomForm.datFreeTimeListDataChange(Sender: TObject;
Field: TField);
var FreeTime: TTime;
begin
inherited;
if datFreeTimeList.DataSet <> nil then
begin
FreeTime := datFreeTimeList.DataSet.FieldByName('FREE_TIME').AsDateTime;
datFreeRoomList.DataSet :=
TMeetingControl.GetInstance.GetFreeRoomList(Meeting_ID,
StartDate,
FreeTime,
Duration);
datFreeRoomList.DataSet.First;
end;
end;
Now, go back to the MeetingDM Data Module and implement the following events of the cdsControl component:
AfterScroll (refreshes the list of available users and participants in the MeetingForm):

procedure TMeetingDM.cdsControlAfterScroll(DataSet: TDataSet);


begin
inherited;
cdsSearchPart.Close;
cdsSearchPart.Params.ParamByName('MEETING_ID').AsInteger :=
DataSet.FieldByName('MEETING_ID').AsInteger;
cdsSearchPart.Open;

cdsParticipants.Close;
cdsParticipants.Params.ParamByName('MEETING_ID').AsInteger :=
DataSet.FieldByName('MEETING_ID').AsInteger;
cdsParticipants.Open;
end;
AfterUpdateRecord (applies the pending changes in the cdsParticipants to the database):

procedure TMeetingDM.dspControlAfterUpdateRecord(Sender: TObject;


SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind);
begin
inherited;
if UpdateKind = ukInsert then
cdsParticipants.ApplyUpdates(0);
end;
BeforeDelete (verifies if the user that is trying to delete a meeting is the meeting owner):

procedure TMeetingDM.cdsControlBeforeDelete(DataSet: TDataSet);


begin
if not TMeetingControl.GetInstance.CheckMeetingOwner then
raise Exception.Create('This operation is granted to the meeting owner
only!');
inherited;
end;
BeforeEdit (verifies if the user that is trying to edit a meeting is the meeting owner):

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 471 -
procedure TMeetingDM.cdsControlBeforeEdit(DataSet: TDataSet);
begin
if not TMeetingControl.GetInstance.CheckMeetingOwner then
raise Exception.Create('This operation is granted to the meeting owner
only!');
inherited;
end;
BeforePost (verifies if the meeting has at least 2 participants, assigns an ID to the meeting if it is a new one, and
updates the LastChange field in the database):

procedure TMeetingDM.cdsControlBeforePost(DataSet: TDataSet);


begin
inherited;
CheckRequiredFields(DataSet);

if not TMeetingControl.GetInstance.CheckNumberOfParts then


raise Exception.Create('At least two participants must be selected!');

{ Generating MeetingID }
if DataSet.State = dsInsert then
begin
DataSet.FieldByName('MEETING_ID').AsInteger := GenerateID('GEN_MEETING_ID');
cdsParticipants.First;
While not cdsParticipants.Eof do
begin
cdsParticipants.Edit;
cdsParticipants.FieldByName('MEETING_ID').AsInteger :=
DataSet.FieldByName('MEETING_ID').AsInteger;
cdsParticipants.Post;
cdsParticipants.Next;
end;
end;

{ Logging date/time when meeting was changed }


DataSet.FieldByName('LASTCHANGE').AsDateTime := Now;
end;
Now, code the method that will ensure that the user selects a room and time after changing the meeting duration. To
do so, double-click on cdsControl, select the Duration field, and code its OnChange event as follows:

procedure TMeetingDM.cdsControlDURATIONChange(Sender: TField);


begin
inherited;
if cdsControl.State in [dsInsert, dsEdit] then
begin
cdsControl.FieldByName('STARTTIME').AsString := '';
cdsControl.FieldByName('ROOM_ID').AsString := '';
cdsControl.FieldByName('ROOM_NAME').AsString := '';
end;
end;
Go back to the uMeetingControl unit and add two public methods:
First, a method to create the form that enables the user to select a room and time for the meeting:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 472 -
function TMeetingControl.CreateTimeRoomForm(var Time: TTime; var Room_ID:
integer;
var Room_Name: string): boolean;
var
fTimeRoomForm: TMeetingTimeRoomForm;
begin
Result := False;

if fMeetingDM.cdsControl.FieldByName('STARTDATE').IsNull or
fMeetingDM.cdsControl.FieldByName('DURATION').IsNull then
raise Exception.Create('"Date" and "Duration" are required fields for this
operation!');

if not CheckMeetingDate then


raise Exception.Create('Retroactivity is not allowed!');

if not CheckNumberOfParts then


raise Exception.Create('At least two participants must be selected for this
operation!');

fTimeRoomForm := TMeetingTimeRoomForm.Create(nil);
try
fTimeRoomForm.Meeting_ID :=
fMeetingDM.cdsControl.FieldByName('MEETING_ID').AsInteger;
fTimeRoomForm.StartDate :=
fMeetingDM.cdsControl.FieldByName('STARTDATE').AsDateTime;
fTimeRoomForm.Duration :=
fMeetingDM.cdsControl.FieldByName('DURATION').AsInteger;
if fTimeRoomForm.ShowModal = mrOK then
begin
Time :=
fTimeRoomForm.datFreeTimeList.DataSet.FieldByName('FREE_TIME').AsDateTime;
Room_ID :=
fTimeRoomForm.datFreeRoomList.DataSet.FieldByName('ROOM_ID').AsInteger;
Room_Name :=
fTimeRoomForm.datFreeRoomList.DataSet.FieldByName('NAME').AsString;

Result := True;
end;
finally
fTimeRoomForm.Free;
end;
end;
Then, create a method that locates a user in the available users list:

procedure TMeetingControl.LocateUser(UserName : string);


begin
if UserName <> '' then
fMeetingDM.cdsControl.Locate('USER_NAME', UserName, [loPartialKey,
loCaseInsensitive]);
end;
Finally, go back to the MeetingForm form and code the OnClick event for the butUserSearch and
butTimeRoomForm buttons:
butUserSearch’s OnClick event:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 473 -
procedure TMeetingForm.butUserSearchClick(Sender: TObject);
begin
inherited;
TMeetingControl.GetInstance.LocateUser(edtSearch.Text);
end;
butTimeRoomForm’s OnClick event:

procedure TMeetingForm.butTimeRoomFormClick(Sender: TObject);


var Time: TTime;
Room_ID: integer;
Room_Name: string;
begin
inherited;
if TMeetingControl.GetInstance.CreateTimeRoomForm(Time, Room_ID, Room_Name)
then
begin
datControl.DataSet.FieldByName('STARTTIME').AsDateTime := Time;
datControl.DataSet.FieldByName('ROOM_ID').AsInteger := Room_ID;
datControl.DataSet.FieldByName('ROOM_NAME').AsString := Room_Name;
end;
end;
Open the MainForm and add a new action to the ActionManager. Change the new action Name property to
acMeetingManager, its Caption property to Meeting Manager, its Category property to Control, and its ImageIndex
property to the proper image.

Figure 63 – Configuring the acMeetingManager action


Add the uMeetingControl unit to the Implementation’s uses clause. Code the acMeetingManager action’s
OnExecute event as follows:

procedure TMainForm.acMeetingManagerExecute(Sender: TObject);


begin
inherited;
TMeetingControl.GetInstance.CreateDefaultForm;
end;
Associate the acMeetingManager action with the “Meeting Manager” item of the MainMenu (Figure 64).

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 474 -
Figure 64 – Associating the acMeetingManager action
Go to the project options (Project | Options) and keep only the MainForm form and the MainDM Data Module as
“Auto-create forms”.
Now that you have already created the User, Room, and Meeting forms, you should create the Login form.
Add a new inherited form to the project by choosing File | New | Other | Inheritable Items, selecting the
AbstractForm icon, and clicking on OK. Save the unit as “uUserLoginForm.pas”, change its Name property to
UserLoginForm, and its Caption property to “User Login”.
Modify the following UserLoginForm form properties (Table 23):

Property Value

BorderStyle bsDialog

KeyPreview True

Position poDesktopCenter

Table 23 – UserLoginForm form properties


Add the following components (Table 24):

Component Name

TEdit edtUserLogin

TEdit edtPassword

TBitBtn btnOK

TBitBtn btnCancel

TLabel lblUserLogin

TLabel lblPassword
Table 24 – Components to be added
Change the Kind property of both buttons: bkOK to the btnOK button, and bkCancel to the btnCancel button. Set
btnOk’s Enabled property to False, just as well. Change edtPassword’s PasswordChar property to “*”, and the text
value will be masked.
Place the components on the form. It will then look as seen in Figure 65.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 475 -
Figure 65 – frmMeetingLoginForm
Include the uUserControl unit to the Implementation’s uses clause and insert the following code in edtUserLogin’s
OnChange event. Associate the OnChange event of the edtPassword component with the OnChange event of the
edtUserLogin component.

procedure TUserLoginForm.edtUserLoginChange(Sender: TObject);


begin
inherited;
btnOk.Enabled := (edtUserLogin.Text <> '') and (edtPassword.Text <> '');
end;
Go to the uUserControl unit and create the method that will execute the UserLoginForm form, according to the code
below:

function TUserControl.DoUserLogin: boolean;


var
frmUserLoginForm: TUserLoginForm;
begin
frmUserLoginForm := TUserLoginForm.Create(nil);
try
result := (frmUserLoginForm.ShowModal = mrOK);
finally
frmUserLoginForm.Free;
end;
end;
Remember to include the Controls and uUserLoginForm units to the Implementation’s uses clause.
Now, insert the method (in its Public section) that will validate the login information of any user who attempts to
access the application. Before inserting the code, add the DB unit to the Implementation’s uses clause.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 476 -
function TUserControl.ValidateUserLogin(UserLogin, Password: string): boolean;
var
sqlLogin: TSQLQuery;
begin
sqlLogin := TSQLQuery.Create(nil);
try
sqlLogin.SQLConnection := DBConnection;

sqlLogin.SQL.Add('SELECT * FROM USERS');


sqlLogin.SQL.Add(' WHERE LOGIN = ' + QuotedStr(UserLogin));
sqlLogin.SQL.Add(' AND PASSW = ' + QuotedStr(Password));
sqlLogin.Open;

if sqlLogin.RecordCount = 0 then
result := false
else
begin
fUserID := sqlLogin.FieldByName('USER_ID').AsInteger;
fUserLogin := sqlLogin.FieldByName('LOGIN').AsString;
fUserName := sqlLogin.FieldByName('NAME').AsString;
fUserIsAdmin := (sqlLogin.FieldByName('ISADMIN').AsString = 'Y');

result := True;
end;
finally
sqlLogin.Free;
end;
end;
Now, go back to the UserLoginForm form. Insert the following code in its OnCloseQuery event:

procedure TUserLoginForm.FormCloseQuery(Sender: TObject;


var CanClose: Boolean);
begin
inherited;
if ModalResult = mrOK then
begin
if not TUserControl.GetInstance.ValidateUserLogin(edtUserLogin.Text,
edtPassword.Text) then
begin
MessageDlg('Invalid user/password!', mtWarning, [mbOK], 0);
CanClose := False;
end
else
TUserControl.GetInstance.AuthorizationLog;
end;
end;
In Project | Options…, remove the UserLoginForm form from the “Auto-Create forms:” section.
Go to MainForm form and change its Visible property to False. Add the following code into MainForm’s OnShow
event:

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 477 -
procedure TMainForm.FormShow(Sender: TObject);
begin
inherited;
StatusBar.Panels[2].Text := 'Welcome ' + TUserControl.GetInstance.fUserName;
StatusBar.Panels[3].Text := 'Is Admin: ';

acUser.Enabled := TUserControl.GetInstance.fUserIsAdmin;
acRoom.Enabled := TUserControl.GetInstance.fUserIsAdmin;

if TUserControl.GetInstance.fUserIsAdmin then
StatusBar.Panels[3].Text := StatusBar.Panels[3].Text + 'YES'
else
StatusBar.Panels[3].Text := StatusBar.Panels[3].Text + 'NO';
end;
Now go to Project | View Source, and insert the following condition in the code:

begin
Application.Initialize;
Application.Title := 'Meeting Organizer';
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TMainDM, MainDM);
if TUserControl.GetInstance.DoUserLogin then
Application.Run
else
Application.Terminate;
end.
Save the project and run the application (F9).
If the steps depicted above were followed accordingly, the application must work as expected. Now, it is possible to
schedule meetings with the people involved, in a specific time and room, as well as add new people to the program.

Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 478 -

You might also like