Manual Delphi Certificacion PDF
Manual Delphi Certificacion PDF
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.
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
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.
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.
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.
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:
WriteList(evenNumbers, 10);
WriteList(decades, 10);
end;
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:
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;
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
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.
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.
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
Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 30 -
Figure 2 - Saving a Unit
Configuring the Project. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 31 -
Project Manager
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.
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
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.
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:
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.
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
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:
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.
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.
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
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.
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.
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
Object-Oriented Programming. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 60 -
Component Property Value
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.
edtRetailerName.Text := ARetailer.Name;
edtRetailerLocation.Text := ARetailer.Location;
edtRetailerTerms.Text := ARetailer.Terms;
chkMultipleLocations.Checked := ARetailer.MultipleLocations;
end;
edtWholesalerName.Text := AWholesaler.Name;
edtWholesalerLocation.Text := AWholesaler.Location;
edtWholesalerTerms.Text := AWholesaler.Terms;
chkShipByTruck.Checked := AWholesaler.ShipByTruck;
end;
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.
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:
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:
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;
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:
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.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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).
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;
...
type
TMyClassHelper = class helper for TMyClass
procedure HelloWorld;
function MyFunc: Integer;
end;
...
procedure TMyClassHelper.HelloWorld;
begin
writeln(Self.ClassName);
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
Property Value
Name frmStartInherited
Now that a form has been added to the project, analyze the project file to see how it has changed.
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
Value Meaning
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.
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:
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
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.
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
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.
TEdit edtFirstNumber
TEdit edtSecondNumber
TEdit edtResult
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):
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:
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:
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:
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:
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
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).
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
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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
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.
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;
var
FName:String;
FLocation:String;
FTerms:String;
end;
implementation
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:
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.
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:
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:
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.
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.
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.
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
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
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.
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:
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
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.
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;
implementation
{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;
The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 137 -
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.
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.
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:
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.
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.
The Singleton Design Pattern. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 142 -
…
if TUserControl.GetInstance.DoUserLogin then
…
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
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.
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.
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.
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.
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.
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.
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.
Note: The ResultsForm is a modal form; thus, the handler uses the ShowModal method.
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.
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.
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.
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.
Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 156 -
Figure 10 – Submenus definition
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.
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:
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:
Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 159 -
Figure 14 – Editing ActionList
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:
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.
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.
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.
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:
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 := '';
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);
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.
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;
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:
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;
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:
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.
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.
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.
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.
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.
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.
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.
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:
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
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:
TEdit edtIDUser
TEdit edtName
TEdit edtUserName
TEdit edtPassWord
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:
Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 182 -
Figure 52 – Form to query users
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.
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.
Description Type
Name String
Username String
PassWord String
After it’s done, the user record will look like this:
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;
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:
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.
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;
Basic Interface Elements. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 189 -
Figure 63 – Adding a Pattern
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.
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:
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:
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:
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:
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.
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.
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.
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.
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.
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.
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
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.
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.
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)
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:
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’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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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:
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
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:
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.
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.
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.
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.
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.
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
Component Description
Encapsulates a DBExpress connection to a database server
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.
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.
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
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
ID Name ID
Type Integer
Size 0
Type String
Size 30
Type String
Size 30
Type DateTime
Size 0
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.
Lookup
In the following example, it is explained how to create a Lookup field in a dataset.
• Create a new VCL Forms Application
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.
Operator Use
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.
Calculated
In the following example, it will be explained how to create a Calculated field in a dataset.
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:
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.
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
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.
Value Description
Skip Skips updating the record that raised the error condition, and leaves the
unapplied changes in the change log.
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.
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:
Help files .hlp, .cnt, and .toc (if used) or any other Help files the application supports
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.
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:
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
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.
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.
.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.
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.
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.
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
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.
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:
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.
Synchronized Views
The change propagation mechanism insures that all views simultaneously reflect the current state of the model.
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.
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.
Chart control with headers, footers, 4 axes, legend, walls, 3D, paging √ √
Database DBChart √ √
Standard Series styles (Line, Bar, Area, Pie, Fast-Line, Point, Horiz.Bar) √ √
Basic gradients √ √
Hundreds of small improvements and properties in all Chart and Series styles sub- √ √
components
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 √ √
Series Datasources √
(Dataset, SingleRecord, Text, XML)
TSeriesDataSet component √
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
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
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 √
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):
Component Description
VCL TDataSet derived component that contains all series data in Chart.
Introduction to TeeChart. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 309 -
Component Description
A derived ComboBox class that displays the combo border when the
cursor is hovered over it.
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.
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:
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
<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
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.
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.
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 incorrect!</p>
<p>This is correct</p>
In HTML, some elements can be improperly nested within each other:
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:
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.
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>
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.
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:
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.
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.
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”?>
<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:
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:
<?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:
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:
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.
<?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>
<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>
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.
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.
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 DOM
Advantages
Disadvantages
• 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).
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:
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:
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.
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:
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
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 }
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:
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;
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:
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.
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:
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:
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;
ClientDataSet.Open;
end;
And finally, the “Apply” code of the button:
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
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
Implements a button the user can click to manage the fields of submit forms.
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.
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.
Introduction to VCL for the WEB. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 358 -
Component Description
Renders a multi-line edit control. TIWMemo allows the user to enter one or
more lines of text.
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 read-only multi-line text. TIWText can also be used to output raw
HTML without the need to create a custom component.
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.
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.
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
Component Description
A layout manager that renders greater control over the visual objects on
the form.
Allows displaying data that are sent to the client (browser) and kept on
the client-side.
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.
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.
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.
A data-aware TIWText.
A data-aware TIWRadioGroup.
Component Description
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.
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.
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.
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.
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.
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.
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:
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.
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}
initialization
TIWForm1.SetAsMainForm;
end.
Compile and run the web application.
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:
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.
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
{$R *.dfm}
procedure TIWForm1.IWAppFormRender(Sender: TObject);
begin
DataModule1.cdsEmployee.Open;
end;
initialization
TIWForm1.SetAsMainForm;
end.
Run the web 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.
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.
TIWEdit Text
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}
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:
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.
Component Description
Introduction to Rave Reports. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 382 -
Component Description
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
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:
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:
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.
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):
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:
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.
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!
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):
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.
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:
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
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
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.
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.
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.
Introduction to DataSnap. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
- 400 -
DataSnap in the Object Repository
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:
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.
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:
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”
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).
Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 408 -
Creating the Main Form
Properties Value
BorderStyle bsSingle
Color clSilver
FormStyle fsMDIForm
Name MainForm
WindowState wsMaximized
Table 1 – Main Form Properties List
Add the following components to the MainForm form.
Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 409 -
Component Name Description
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):
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.
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.
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.
Property Value
Caption Close
ShortCut CTRL+Q
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:
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:
Properties Value
Name AbstractForm
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:
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:
Figure 19 – AbstractForm
Properties Value
Name AbstractDataForm
FormStyle fsMDIChild
Table 6 – Form properties
Add the following components to AbstractDataForm:
Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 417 -
Component Name Description
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.
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.
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
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.
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.
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.
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).
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.
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.
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
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:
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:
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:
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:
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:
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.
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 }
DBConnection := TMainForm(Application.MainForm).DBConnection;
fTD.GlobalID := 1;
fTD.TransactionID := 1;
fTD.IsolationLevel := xilREADCOMMITTED;
CreateDefaultDM;
end;
destructor TAbstractControl.Destroy;
begin
inherited Destroy;
end;
Building the Meeting Organizer. Copyright ©2008 Embarcadero Technologies, Inc. All Rights Reserved.
Delphi 2009 Application Development for Win32
- 433 -
begin
result := fTD;
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.
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
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.
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.
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:
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.
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.
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.
if TUserControl.GetInstance.FindLoginName(
DataSet.FieldByName('USER_ID').AsInteger,
DataSet.FieldByName('LOGIN').AsString) then
raise Exception.Create('Duplicated login name!');
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;
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;
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;
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;
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.
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.
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:
TRoomControl = class(TAbstractControl)
strict private
fRoomDM : TRoomDM;
Add a constructor 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.
if TRoomControl.GetInstance.FindRoomName(
DataSet.FieldByName('ROOM_ID').AsInteger,
DataSet.FieldByName('NAME').AsString) then
raise Exception.Create('Duplicated room name!');
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:
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).
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.
MEETING_ID Meeting ID
TOPIC Topic
DURATION Duration
USER_ID User ID
ROOM_ID Room ID
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:
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
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:
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:
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
Figure 59 – MeetingForm
Configure the other TDBGrid properties as follows (Table 18):
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:
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):
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:
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:
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
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 + '!');
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));
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.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.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:
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
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:
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:
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):
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):
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):
{ 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;
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!');
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:
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:
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
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.
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;
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:
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 -