Professional Documents
Culture Documents
Delphi does a lot of work for us - the programmer simply uses the mouse to click,
drag, size and position graphical parts to build each screen of the application.
Each part (or element) can be passive (displaying text or graphics), or active
(responding to a user mouse or keyboard action).
We have shown the form reduced in size for convenience here, but you will find it
larger on your computer. It is a blank form, onto which we can add various controls
and information. The menu window has a row of graphical items that you can add to
the form. They are in tabbed groups : Standard, Additional, Win32 and so on.
We will select the simplest from the Standard collection. Click on the A image to
select a Label. This A will then show as selected:
Having selected a graphical element, we then mark out on the form where we want
to place the element. This is done by clicking and dragging. This gives us our first
form element:
Let us blank out the caption. We do this in the window called the Object Inspector
(available under the View menu item if not already present):
Writing a more meaningful program
end;
end.
The displayed file before correction
Teh cat sat on eth mat
The cat did not sit on eth mat or teh floor
if changeCounts[ETH] = 1
then Label2.Caption := 'eth changed once'
else Label2.Caption := 'eth changed '+
IntToStr(changeCounts[ETH])+' times';
if changeCounts[EHT] = 1
then Label3.Caption := 'eht changed once'
else Label3.Caption := 'eht changed '+
IntToStr(changeCounts[EHT])+' times';
end.
The displayed file before correction
Teh cat
sat on
eth mat
The cat
did not
sit on
eth mat
or teh
floor
Teh teh
teh eth
eth eht
eht
Final
line of
5.
The displayed file after correction
The cat
sat on
the mat
The cat
did not
sit on
the mat
or the
floor
The the
the the
the the
the
Final
line of
5.
The displayed statistics
Teh/teh
Delphi data types
Data is stored in the memory of the computer when the program runs (it can also be stored in a file, but that is
another matter beyond the scope of this tutorial). Each memory 'slot' is identified by a name that the programmer
chooses. For example LineTotal might be used to name a memory slot that holds the total number of lines in a
Word Processor document.
The program can freely read from and write to this memory slot. This kind of data is called a Variable. It can
contain data such as a number or text. Sometimes, we may have data that we do not want to change. For
example, the maximum number of lines that the Word Processor can handle. When we give a name to such data,
we also give it its permanent value. These are called constants.
Each variable starts with the name you choose, followed by a : and then the variable type. As with all Delphi
statements, a ; terminates the line. As you can see, you can define multiple variables in one line if they are of the
same type.
It is very important that the name you choose for each variable is unique, otherwise Delphi will not know how to
identify which you are referring to. It must also be different from the Delphi language keywords. You'll know when
you have got it right when Delphi compiles your code OK (by hitting Ctrl-F9 to compile).
Delphui is not sensitive about the case (lower or upper) of your names. It treats theCAT name the same as
TheCat.
Number types
Delphi provides many different data types for storing numbers. Your choice depends on the data you want to
handle. Our Word Processor line count is an unsigned Integer, so we might choose Word which can hold values
up to 65,535. Financial or mathematical calculations may require numbers with decimal places - floating point
numbers.
var
// Integer data types :
Int1 : Byte; // 0 to 255
Int2 : ShortInt; // -127 to 127
Int3 : Word; // 0 to 65,535
Int4 : SmallInt; // -32,768 to 32,767
Int5 : LongWord; // 0 to 4,294,967,295
Int6 : Cardinal; // 0 to 4,294,967,295
Int7 : LongInt; // -2,147,483,648 to 2,147,483,647
Int8 : Integer; // -2,147,483,648 to 2,147,483,647
Int9 : Int64; // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
Text types
Like many other languages, Delphi allows you to store letters, words, and sentences in single variables. These
can be used to display, to hold user details and so on. A letter is stored in a single character variable type, such
as Char, and words and sentences stored in string types, such as String.
var
Str1 : Char; // Holds a single character, small alphabet
Str2 : WideChar; // Holds a single character, International alphabet
Str3 : AnsiChar; // Holds a single character, small alphabet
Str4 : ShortString; // Holds a string of up to 255 Char's
Str5 : String; // Holds strings of Char's of any size desired
Str6 : AnsiString; // Holds strings of AnsiChar's any size desired
Str7 : WideString; // Holds strings of WideChar's of any size desired
Integer and floating point numbers
The different number types in Delphi
Delphi provides many different data types for storing numbers. Your choice depends on the data you want to handle. In general,
smaller number capacities mean smaller variable sizes, and faster calculations. Ideally, you should use a type that comfortably
copes with all possible values of the data it will store.
For example, a Byte type can comfortably hold the age of a person - no-one to date has lived as long as 255 years.
With decimal numbers, the smaller capacity types also have less precision. Less numbers of significant digits. Let us look at the
different types:
Byte 1 0 to 255
ShortInt 1 -127 to 127
Word 2 0 to 65,535
SmallInt 2 -32,768 to 32,767
LongWord 4 0 to 4,294,967,295
Cardinal 4* 0 to 4,294,967,295
LongInt 4 -2,147,483,648 to 2,147,483,647
Integer 4* -2,147,483,648 to 2,147,483,647
Int64 8 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
* Note : the Integer and Cardinal types are both 4 bytes in size at present (Delphi release
7), but are not guaranteed to be this size in the future. All other type sizes are guaranteed.
const
YOUNG_AGE = 23; // Small integer constant
MANY = 300; // Bigger integer constant
RICH = 100000.00; // Decimal number : note no thousand commas
var
Age : Byte; // Smallest positive integer type
Books : SmallInt; // Bigger signed integer
Salary : Currency; // Decimal used to hold financial amounts
Expenses : Currency;
TakeHome : Currency;
begin
Age := YOUNG_AGE; // Assign from a predefined constant
Books := MANY + 45; // Assign from a mix of constants (expression)
Salary := RICH; // Assign from a predefined constant
Expenses := 12345.67; // Assign from a literal constant
TakeHome := Salary; // Assign from another variable
TakeHome := TakeHome - Expenses; // Assign from an expression
end;
Age is set to 23
Books is set to 345
Salary is set to 100000.00
Expenses is set to 12345.67
TakeHome is set to 87654.33
Numerical operators
Number calculations, or expressions, have a number of primitive operators available:
var
myInt : Integer; // Define integer and decimal variables
myDec : Single;
Strings and characters
alphabets of China, Japan and so on. These are called International characters. International applications must
use WideChar and WideString types.
Strings
A single character is useful when parsing text, one character at a time. However, to handle words and sentences
and screen labels and so on, strings are used. A string is literally a string of characters. It can be a string of Char,
AnsiChar or WideChar characters.
var
source, target, last : String;
begin
source := 'Hello World'; // Assign from a string literal
target := source; // Assign from another variable
last := 'Don''t do that'; // Quotes in a string must be doubled
end;
source is now set to : Hello World
target is now set to : Hello World
last is now set to : Don't do that
String operators
There are a number of primitive string operators that are commonly used:
var
myString : string;
begin
myString := 'Hello ' + 'World'; // String concatenation
var
Source, Target : string;
begin
Source := '12345678';
Target := Copy(Source, 3, 4); // Target now = '3456'
Target := '12345678';
Insert('-+-', Target, 3); // Target now = '12-+-345678'
Enumerations, SubRanges and Sets
Enumerations
The provision of enumerations is a big plus for Delphi. They make for readable and reliable code. An enumeration is
simply a fixed range of named values. For example, the Boolean data type is itself an enumeration, with two
possible values : True and False. If you try to assign a different value to a boolean variable, the code will not
compile.
Defining enumerations
When you want to use an enumeration variable, you must define the range of possible values in an
enumeration type first (or use an existing enumeration type, such as boolean). Here is an example:
type
TSuit = (Hearts, Diamonds, Clubs, Spades); // Defines enumeration range
var
suit : TSuit; // Defines enumeration variable
begin
suit := Clubs; // Set to one of the values
end;
The TSuit type definition creates a new Delphi data type that we can use as a type for any new variable in our
program. (If you define types that you will use many times, you can place them in a Unit file and refer to this
in a uses statement in any program that wants to use them). We have defined an enumeration range of names
that represent the suits of playing cards.
We have also defined a suit variable of that TSuit type, and have assigned one of these values. Note that there
are no quote marks around these enumeration values - they are not strings, and they take no storage.
In fact, each of the enumeration values is equated with a number. The TSuit enumeration will have the
following values assigned :
And you can use these values instead of the enumeration values, although this loses many of the benefits of
enumerations. You can even override the default number assignments:
type
TSuit = (Hearts=13, Diamonds, Clubs=22, Spades);
var
suit : TSuit;
begin
suit := Clubs;
end;
The enumeration values assigned are now :
type
TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun); // Enumeration values
var
today : TDay;
weekend : Boolean;
begin
today := Wed; // Set today to be Wednesday
type
TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun); // Enumeration values
var
day : TDay; // Enumeration variable
begin
for day := Mon to Fri do
begin
// day has each of the values Mon to Fri ( 1 to 5) in 5 iterations
// of this loop, allowing you to whatever you want.
end;
end;
Holding sets of data
About arrays
Arrays are ordered collections of data of one type. Each data item is called an element, and is accessed by its
position (index) in the array. They are very useful for storing lists of data, such as customers, or lines of text.
There are a number of types of array, and array may be single or multidimensional (lists of lists in effect).
Constant arrays
It is probably easiest to introduce arrays that are used to hold fixed, unchangeable information. Constant arrays.
These can be defined in two kinds of ways:
const
Days : array[1..7] of string = ('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
This first way declares the array as well as the contents in one statement. Alternatively:
type
TDays = array[1..7] of string;
const
Days : TDays = ('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
In both cases, we have defined an array of constants that represent the days of the week. We can use them by day
number:
const
Days : array[1..7] of string = ('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
var
i : Integer;
begin
for i := 1 to 5 do // Show the weekdays
ShowMessageFmt('Day %d = %s',[i,Days[i]]);
end;
The ShowMessageFmt routine displays our data - click on it to learn more.
The data displayed by the above code is:
Day 1 = Mon
Day 2 = Tue
Day 3 = Wed
Day 4 = Thu
Day 5 = Fri
type
TCars = (Ford, Vauxhall, GM, Nissan, Toyota, Honda);
var
cars : array[TCars] of string; // Range is 0..5
japCars : array[Nissan..Honda] of string; // Range is 3..5
begin
// We must use the appropriate enumeration value to index our arrays:
japCars[Nissan] := 'Bluebird'; // Allowed
japCars[4] := 'Corolla'; // Not allowed
japCars[Ford] := 'Galaxy'; // Not allowed
end;
Note that the cars array is defined using just a data type - TCars. The size and range of the data type
dictates how we use the array.
Static arrays
There are other ways that arrays vary. Static arrays are the easiest to understand, and have been covered so far.
They require the size to be defined as part of the array definition. They are called static because their size is static,
and because they use static memory.
Dynamic arrays
Dynamic arrays do not have their size defined in their declaration:
Storing groups of data together
What are records?
Records are a useful and distinguishing feature of delphi. They provide a very neat way of having named data structures -
groups of data fields. Unlike arrays, a record may contain different types of data.
Records are fixed in size - the definition of a record must contain fixed length fields. We are allowed to have strings, but
either their length must be specified (for example a : String[20]), or a pointer to the string is stored in the record. In this
case, the record cannot be used to write the string to a file. The TPoint type is an example of a record. Before we go any
further, let us look at a simple example.
type
TCustomer = record
name : string[30];
age : byte;
end;
var
customer : TCustomer;
begin
// Set up our customer record
customer.name := 'Fred Bloggs';
customer.age := 23;
end;
When we define a record, each field is simply accessed by name, separated by a dot from the record variable name. This
makes records almost self documenting, and certainly easy to understand.
Above, we have created one customer, and set up the customer record fields.
type
TCustomer = record
name : string[30];
age : byte;
end;
var
John, Nancy : TCustomer;
begin
// Set up our customer records
with John do
begin
name := 'John Moffatt'; // Only refer to the record fields
age := 67;
end;
with Nancy do
begin
name := 'Nancy Moffatt'; // Only refer to the record fields
age := 77;
end;
end;
Please note that this is quite a complex piece of code - it uses a procedure that takes a variable number of parameters,
specially passed in square brackets (see Procedure for more on procedures).
unit Unit1;
interface
uses
Forms, Dialogs;
Programming logic
What is programming logic?
Programming in Delphi or any other language would not work without logic. Logic is the glue that holds together
the code, and controls how it is executed. For example, supposing we were writing a word procesor program. When
the user presses the Enter key, we will move the cursor to a new line. The code would have a logical test for the
user hitting the Enter key. If hit we do a line throw, if not, we continue on the same line.
If then else
In the above example, we might well use the If statement to check for the Enter key.
var
number : Integer;
text : String;
begin
number := Sqr(17); // Calculate the square of 17
if number > 400
then text := '17 squared > 400' // Action when if condition is true
else text := '17 squared <= 400'; // Action when if condition is false
end;
text is set to : '17 squared <= 400'
There are a number of things to note about the if statement. First that it spans a few lines - remember that
Delphi allows statements to span lines - this is why it insists on a terminating ;
Second, that the then statement does not have a terminating ; -this is because it is part of the if statement,
which is finished at the end of the else clause.
Third, that we have set the value of a text string when the If condition is successful - the Then clause - and
when unsuccessful - the Else clause. We could have just done a then assignment:
Note also that the then clause now has a terminating ; to signify the end of the if statement.
Nested if statements
There is nothing to stop you using if statements as the statement of an if statement. Nesting can be useful,
and is often used like this:
if condition1
then statement1
else if condition2
then statement2
else statement3;
However, too many nested if statements can make the code confusing. The Case statement, discussed below,
can be used to overcome a lot of these problems.
Logicial primitives
Before we introduce these, it is appropriate to introduce the Boolean data type. It is an enumerated type, that can
Repeating sets of commands
Why loops are used in programming
One of the main reasons for using computers is to save the tedium of many repetitive tasks. One of the main uses of loops in
programs is to carrry out such repetitive tasks. A loop will execute one or more lines of code (statements) as many times as you
want.
Your choice of loop type depends on how you want to control and terminate the looping.
Counting up
Here is a simple example counting up using numeric values:
var
count : Integer;
begin
For count := 1 to 5 do
ShowMessageFmt('Count is now %d',[count]);
end;
Count is now 1
Count is now 2
Count is now 3
Count is now 4
Count is now 5
The ShowMessageFmt routine is useful for displaying information - click on it to read more.
type
TWeekDay = (Monday=1, Tuesday, Wednesday, Thursday, Friday);
var
weekday : TWeekDay;
hours : array[TWeekDay] of byte;
begin
// Set up the hours every day to zero
for weekDay := Monday to Friday do
hours[weekDay] := 0;
var
letter : Char;
begin
for letter := 'G' downto 'A' do
ShowMessage('Letter = '+letter)
end;
Letter = G
Letter = F
Letter = E
Letter = D
Letter = C
Letter = B
Letter = A
The For statements in the examples above have all executed one statement. If you want to execute more than one, you must
enclose these in a Begin and End pair.
Functions and procedures
An overview
A subroutine is like a sun-program. It not only helps divide your code up into sensible, manageable chunks, but it
also allows these chunks to be used (called) by different parts of your program. Each subroutine contains one of
more statements.
In common with other languages, Delphi provides 2 types of subroutine - Procedures and Functions. Functions
are the same as procedures except that they return a value in addition to executing statements. A Function, as its
name suggests, is like a little program that calculates something, returning the value to the caller. On the other
hand, a procedure is like a little routine that performs something, and then just finishes.
Parameters to subroutines
Both functions and procedures can be defined to operate without any data being passed. For example, you might
have a function that simply returns a random number (like the Delphi Random function). It needs no data to get it
going.
Likewise, you can have a procedure that carries out some task without the need for data to dictate its operations.
For example, you might have a procedure that draws a square on the screen. The same square every time it is
called.
Often, however, you will pass data, called parameters, to a subroutine. (Note that the definition of a subroutine
refers to parameters as arguments - they are parameters when passed to the subroutine).
In serious code you should handle error situations so that at the very least, the user is informed about the error in your
chosen way.
Delphi uses the event handling approach to error handling. Errors are (mostly) treated as exceptions, which cause
program operation to suspend and jump to the nearest exception handler. If you don't have one, this will be the Delphi
default handler - it will report the error and terminate your program.
Often, you will want to handle the error, and continue with your program. For example, you may be trying to display a
picture on a page, but cannot find it. So you might display a placeholder instead. Much like Internet Explorer does.
begin
Try
...
The code we want to execute
...
Except
...
This code gets executed if an exception occurs in the above block
...
end;
end;
We literally try to execute some code, which will run except when an error (exception) occurs. Then the except code
will take over.
var
number1, number0 : Integer;
begin
try
number0 := 0;
number1 := 1;
number1 := number1 div number0;
ShowMessage('1 / 0 = '+IntToStr(number1));
except
on E : Exception do
begin
ShowMessage('Exception class name = '+E.ClassName);
ShowMessage('Exception message = '+E.Message);
end;
end;
end;
When the division fails, the code jumps to the except block. The first ShowMessage statement therefore does not get
executed.
In our exception block, we can simpl place code to act regardless of the type of error. Or we can do different things
depending on the error. Here, we use the On function to act on the exception type.
The On clause checks against one of a number of Exception classes. The top dog is the Exception class, parent of all
exception classes. This is guaranteed to be activated above. We can pick out of this class the name of the actual
exception class name (EDivByZero) and the message (divide by zero).
except
// IO error
On E : EInOutError do
ShowMessage('IO error : '+E.Message);
// Dibision by zero
On E : EDivByZero do
ShowMessage('Div by zero error : '+E.Message);
// Catch other errors
Else
ShowMessage('Unknown error');
end;
var
date1, date2, date3 : TDateTime; // TDateTime variables
begin
date1 := Yesterday; // Set to the start of yesterday
date2 := Date; // Set to the start of the current day
date3 := Tomorrow; // Set to the start of tomorrow
date4 := Now; // Set to the current day and time
end;
date1 is set to something like 12/12/2002 00:00:00
date2 is set to something like 13/12/2002 00:00:00
date3 is set to something like 14/12/2002 00:00:00
date4 is set to something like 13/12/2002 08:15:45
Note : the start of the day is often called midnight in Delphi documentation, but this is misleading, since it would be
midnight of the wrong day.
var
month : Integer;
begin
for month := 1 to 12 do // Display the short and long month names
begin
ShowMessage(ShortMonthNames[month]);
ShowMessage(LongMonthNames[month]);
end;
end;
The ShowMessage routine display the following information:
Jan
January
Feb
February
Mar
March
Apr
April
May
May
Jun
June
Jul
July
Aug
August
Sep
September
Oct
October
Nov
November
Dec
December
Short and long day names
It is important to note that these day arrays start with index 1 = Sunday. This is not a good standard (it is not ISO 8601
compliant), so be careful when using with ISO 8601 compliant routines such as DayOfTheWeek
var
Files
more:
ChDir Change the working drive plus path for a specified drive
CreateDir Create a directory
DeleteFile Delete a file specified by its file name
Erase Erase a file
FileExists Returns true if the given file exists
FileSearch Search for a file in one or more directories
FileSetDate Set the last modified date and time of a file
Flush Flushes buffered text file data to the file
GetCurrentDir Get the current directory (drive plus directory)
MkDir Make a directory
RemoveDir Remove a directory
Rename Rename a file
RenameFile Rename a file or directory
RmDir Remove a directory
SelectDirectory Display a dialog to allow user selection of a directory
SetCurrentDir Change the current directory
Truncate Truncates a file size
These lists can be furnished from text files in one fell swoop. Here we show a TStringList object being created, and loaded
from a file:
var
fileData : TStringList; // Our TStringList variable
begin
fileData := TStringList.Create; // Create the TSTringList object
fileData.LoadFromFile('Testing.txt'); // Load from Testing.txt file
...
We can display the whole file in a Memo box:
memoBox.Text := fileData.Text;
and we can display or process the file with direct access to any line at any time. In the example code below, we open a text
file, reverse all lines in the file, and then save it back. Not terribly useful, but it shows the power of TStringList.
var
fileData : TStringList;
saveLine : String;
lines, i : Integer;
begin
fileData := TStringList.Create; // Create the TSTringList object
fileData.LoadFromFile('Test.txt'); // Load from Testing.txt file
end.
And here is how the code could be used :
var
list : TNumberList;
value : Int64;
i : Integer;
begin
// Create a number list object
list := TNumberList.Create;
// Add the first 30 even numbers to the list, each doubled in size
for i := 0 to 29 do
list.Add(i * 2);
// Finish printing
Printer.EndDoc;
end;
end;
end.
Object Orientation overview
What is object orientation?
Before the mid 1980's, the majority of programming languages used commercially operated in a procedural manner.
You could trace the code operation linearly line by line. The only jumps were as a result of conditional logic and
subroutine calls.
This was proving to be an error strewn method of writing large applications. You could happily modularise it by
packaging sub-programs into functions or procedures, but there was a limit to how these helped you. Not least
because subroutines do not hold onto their data across calls.
Also, with the advance of graphical interfaces, and a principally mouse click driven user interface, programs were
becoming event driven. They needed to respond to whatever of the many possible gui (graphical user interface)
objects on the screen the user chose to click next.
Object orientation radically changed the face of programming for many people. It took the concept of subroutines into
a completely different zone. Now they retained their data, and were in fact, collections of routines under one umbrella.
You called an object to do something, and left the object to sort out how it did it. You did not need to furnish the
internal data since it was retained across calls. For example, a list object could have items added or removed from the
list at will. And the object could be asked to sort the list into sequence.
Objects also allowed events, such as mouse clicks, to be handled neatly - a button object could call a routine (method)
of another object when clicked. This was now true moduralisation.
Additionally, OO introduced cleaner ways of allowing fields to be seen externally to the class - via Properties. Now a
class can have methods and properties - we seldom let fields be seen externally - they are used internally to make the
class work. A Property can be read only, write only or both. All very neat.
But a class is only a set of field, property and method definitions. It is a data type. We must create an instance of a
class before we can use it. We can make any number of instances of a single class. For example, we may make 3
different list class instances - one for CDs one for LPs and one for Cassettes. Each list object hides its storage of these
lists. We merely access its internal lists by the provided methods and properties. A read only property, for example,
may give the size of teh list as it stands at the moment.
Creating an instance of a class is called instantiation, and generates an object. Each instance of a class is a separate
object.
type
// Define a simple class
TSimple = class(TObject)
simpleCount : Byte;
property count : Byte
read simpleCount;
procedure SetCount(count : Byte);
end;
We have defined a class called TSimple as a new data type. It is a data type because we have to instantiate it to
create a variable. We create an object by calling the Create method. Ah ha! There is no Create method that can be
seen. This is because we have inherited it (see the Inherit tutorial for further) from the motherf of all classes :
TObject. This is the class we have based our class on. In fact, TObject is assumed by default, so we could have typed
:
TSimple = class
Our code has a field called simpleCount that is a Byte type. It can be read by the count property. When we do, we
use the property name count, rather than the internal simpleCount name.
Our method SetCount sets the simpleCount value. Before we go any further, we must define this method, or our code
will not compile:
var
simple : TSimple;
Avoiding memory leaks
Memory and Object Orientation
Object Orientation has transformed the process of application development. It has allowed complex code to be
written in nicely encapsulated modules (objects). When you create an object, Delphi handles the memory allocation
for the object as you call the Create method of the object class.
But there is a down side to this automation that is often overlooked, especially by newcomers, giving rise to
memory leaks.
This is a very real problem in many programs, including commercial applications. If you are serious about your
code, you should follow the principles in this tutorial.
var
rover : TCar;
you are simply declaring a reference to an object. When you create an object for this reference :
begin
rover := TCar.Create;
Delphi allocates memory for the object, and executes the Create constructor method of the class to do any
object initialisation required. The rover variable now points to this new object. There is no magic link from the
object to the variable - simply a reference.
var
rover : Tcar;
myCar : TCar;
begin
rover := TCar.Create;
myCar := rover;
The myCar assignment simply makes the myCar variable point to the object that rover points to. No copying
of the object is done. Only one object exists at this time. And it is not magically linked to either variable.
Delphi does not keep track of who has referred to the object because it cannot easily keep track of all possible
variables that might refer to the object. So, for example, you could set both of these variables to nil and the
object will still persist.
begin
// Create the TSTringList object
fileData := TStringList.Create; // This allocates object memory
Inheritance in Delphi
Object oriented (OO) languages, as their name implies, revolve around another aspect of the real World, where a chunk
of code and data is treated as an object. An object in the real World, such as a football, has characteristics, such as size
(the data), and actions, such as being kicked (a method). Such objects in an OO language are defined in a class. A class
of objects, such as a class of balls, of which a football is a sub-class. The sub-class has specific features (such as a set
of sewn panels) but also inherits the parent class features. It too has a size, and can be kicked. In code terms, a
Football class would inherit the parent Ball class size variable (data), and kick method (code) :
type
// Define a Ball class
TBall = class
protected
ballSize : Byte;
ballSpeed : Byte;
published
procedure Kick(power : Byte);
function GetSpeed : Byte;
constructor Create(size : Byte);
end;
var
beachBall : TBall;
soccerBall : TFootball;
begin
// Create our two balls
beachBall := TBall.Create(5);
soccerBall := TFootball.Create(5, 12);
Note also that a class may extend only one ancestor class, but can implement multiple interfaces.
Interfaces
begin
// Instantiate our bike and car objects
mumsBike := TBicycle.Create(false, 24);
dadsCar := TCar.Create('Nissan bluebird');
if mumsBike.isRecyclable
then ShowMessage('Mums bike is recyclable')
else ShowMessage('Mums bike is not recyclable');
end;
end.
The ShowMessage shows the following program output:
Dads car is recyclable
Mums bike is not recyclable
Writing a class unit
// Check the first character of the fromStr
if stText[index] = fromStr[1] then
begin
if AnsiMidStr(stText, index, fromSize) = fromStr then
begin
// Increment the replace count
Inc(count);
// If no character match :
if not matched then
begin
// Store the current character in the target string, and
// then skip to the next source string character
newText := newText + stText[index];
Inc(index);
end;
end;
// Copy the newly built string back to stText - as long as we made changes
if count > 0 then stText := newText;
The whole of the Stringy is shown on the next page, along with sample code that illustrates use of it.
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,
Stringy; // Use our new Stringy unit
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// We'll restore the string and look for all occurences of 'rich'
// Notice how the myText object remembers where it is in the following
// sequence of calls. This is a huge benefit of object orientation.
myText.Text := 'In an enriched time there was a Rich man, with a rich sister';
position := myText.FindFirst('rich');
end.
Standard tab GUI components
GUI components
GUI stands for Graphical User Interface. It refers to the windows, buttons, dialogs, menus and everything visual in a
modern application. A GUI component is one of these graphical building blocks. Delphi lets you build powerful applications
using a rich variety of these components.
These components are grouped under a long set of tabs in the top part of the Delphi screen, starting with Standard at
the left. We'll look at this Standard tab here. It looks something like this (Delphi allows you to tinker with nearly
everything in its interface, so it may look different on your system):
Each of the components is itemised below with a picture of a typical GUI object they can create:
Label
Edit
Button
CheckBox
RadioButton
ScrollBar
Note that the displayed components were taken from an XP computer. In order to get the new XP look (the XP 'themed'
GUI look), you must add the XP Manifest component to you form. It is found under the Win32 component tab:
XP Manifest component.
We'll now cover each of the components in turn. Components have many properties and methods and events, but we'll
keep the descriptions to the point to keep this article short enough. Each component is added to you form by clicking it
and then clicking (or dragging and releasing) on your form.
Frame objects
These were introduced in Delphi 5. They represent a powerful mechanism, albeit one that is a little advanced for a Delphi
Basics site. However, it is worth describing their role if you want to research further.
A frame is essentially a new object. It is defined using the File|New menu. Only then can you add the frame to your form
using the Frame component. You can add the same frame to as many forms of your application as you want. This is
because the frame is designed as a kind of template for a part of a form. It allows you to define the same look and feel
for that part of each form. And more importantly, each instance of the frame inherits everything from the original frame.
For further reading, Mastering Delphi by Cantu covers this topic with example code.
Menus