Delphi Programming


Object Pascal is a draft for an object oriented ANSI/ISO standard of the venerable Pascal programming language (circa 1989-1990). The draft never made it to a full standard, but some Apple dialects are pretty close to it.

Object Pascal is the programming language you use in Delphi. It is mostly similar to Turbo Pascal, but Borland has added some features to it. I will deal with these later. Object Pascal is obviously an object oriented language. For those who don't know what this is I'll give a brief summary in the next section. Those who are familiar with OOP can skip it.

The Object Pascal dialect used in Delphi was basically a strongly (and incompatibly) enhanced version of the proposals in this draft is the native language of Delphi. (e..g in the original Object Pascal, all methods are virtual automatically). Delphi when started was regarded more as an IDE just like Visual Studio for RAD development purpose uptil Delphi version 6.0. However, from version 7.0 Delphi 7.0 itself was regarded as the language and the Object Pascal lost its name.

Delphi has undergone through lot of hands first from Borland Inc to Inprise (which infact was Borland itself) and then to CodeGear and now eventually is being sold out to Embarcadero Technologies. The most recent release of Delphi as of now is Delphi XE launched in the year 2010.

Name Issues[]

Technically speaking the developers of Delphi no longer call the language "Object Pascal" but consider it a variant of Object Pascal called "The Delphi Programming Language". This may be due to many non-ANSI standard changes they desired to include or possibly to make their extensions proprietary. Another reason might be more trademark related. But if it looks like pascal and quacks like pascal...

Free Pascal still calls the language Object Pascal, even though it is nearly 100% compatible to Delphi's version. Free Pascal is slowly also building a dialect closer to classical Object Pascal (for Mac Pascal compability).

Free Pascal also features another object oriented Pascal for Mac GUI (COCOA) interfacing, see Objective Pascal

Coding in Pascal[]

Delphi allows developers with three types of commentings. Both single line and multiple line commenting is allowed to the developers. The different types of comments allowed are -

  • {this is a comment}
  • (* this is another comment *)
  • // this is a comment up to the end of the line

The opening curly bracket '{' initiates a comment while the closing curly bracket '}' suggests the termination of the multi line comment. Similar is the case with the combination of open bracket and star '(*' and star and closing bracket '*)' for multi line comments. A single line comment can be made by simple two forward slashes '//'. It should be noted that anything written after the '//' will be ignored by the compiler.

The developer must note that the multi line comments '{' and '(*' are considered multi line comments only if they are not immediately followed by a dollar '$' sign, else they will be considered as compiler directives such as -

{$R *.DFM} which a developer can commonly find in unit file associated with a form file.

Syntax Highlighting[]

To make it easier to read and write Pascal code, the Delphi editor has a feature called color syntax highlighting. Depending on the meaning in Pascal of the words you type in the editor, they are displayed using different colors. There are several 3rd party IDE plugins that offer enhancements for Syntax Highlighting.

Expressions and Operators[]

Operators and Precedence

Unary Operators (Highest Precedence)

  • '@' Address of the variable or function (returns a pointer)
  • 'not' Boolean or bitwise not

Multiplicative and Bitwise Operators

  • '*' Arithmetic multiplication or set intersection
  • '/' Floating-point division
  • 'div' Integer division
  • 'mod' Modulus (the remainder of integer division)
  • 'as' Allows a type-checked type conversion among at runtime (part of the RTTI support)
  • 'and' Boolean or bitwise and
  • 'shl' Bitwise left shift
  • 'shr' Bitwise right shift

Additive Operators

  • '+' Arithmetic addition, set union, string concatenation, pointer offset addition
  • '-' Arithmetic subtraction, set difference, pointer offset subtraction
  • 'or' Boolean or bitwise or
  • 'xor' Boolean or bitwise exclusive or

Relational and Comparison Operators (Lowest Precedence)

  • '=' Test whether equal
  • '<>' Test whether not equal
  • '<' Test whether less than
  • '>' Test whether greater than
  • '<=' Test whether less than or equal to, or a subset of a set
  • '>=' Test whether greater than or equal to, or a superset of a set
  • 'in' Test whether the item is a member of the set
  • 'is' Test whether object is type-compatible (another RTTI operator)

Set Operators[]

The set operators includes: union (+), difference (-), intersection (*), membership test (in), plus some relational operators. To add an element to a set, you can make the union of the set with another one that has only the element you need.

Here's a Delphi example related to font styles:

  • Style := Style + [fsBold];
  • Style := Style + [fsBold, fsItalic] - [fsUnderline];



   Value: Integer;
   IsCorrect: Boolean;
   A, B: Char;
   Value := 10;
   IsCorrect := True;
   Value: Integer = 10;
   IsCorrect: Boolean = True;
 Note: This initialization technique works only for global variables,
 not for variables declared inside the scope of a
 procedure or method.


When you declare a constant, the compiler can choose whether to assign a memory location to the constant, and save its value there, or to duplicate the actual value each time the constant is used. This second approach makes sense particularly for simple constants.


   Thousand = 1000;
   Pi = 3.14;
   Thousand: Integer = 1000;

Resource String Constants[]

A string constant defined with the resourcestring directive is stored in the resources of the program, in a string table.

   CompanyName = 'Delphi';
 procedure TForm1.Button1Click(Sender: TObject);
   ShowMessage (CompanyName);

The interesting aspect of this is that if you examine it with a resource explorer you'll see the new strings in the resources. This means that the strings are not part of the compiled code but stored in a separate area of the executable file.

The advantage of resources is in an efficient memory handling performed by Windows and in the possibility of localizing a program (translating the strings to a different language) without having to modify its source code.

Data Types[]

In Pascal there are several predefined data types, which can be divided into three groups: ordinal types, real types, and strings.

Ordinal Types[]

  Size                      Signed Range          Unsigned Range
  8 bits           ShortInt(-128 to 127)          Byte (0 to 255)
  16 bits          SmallInt (-32768 to 32767)     Word (0 to 65,535)
  32 bits          LongInt (-2,147,483,648      
                   to 2,147,483,647)              LongWord (0 to 4,294,967,295)
  64 bits          Int64	
  16/32 bits       Integer                        Cardinal

Ordinal Types Routines[]

  Routine                                               Purpose
  • Dec    Decrements the variable passed as parameter, by one or by 
           the value of the optional second parameter.
  • Inc    Increments the variable passed as parameter, by one or by 
           the specified value. 
  • Odd    Returns True if the argument is an odd number. 
  • Pred   Returns the value before the argument in the order determined 
           by the data type, the predecessor. 
  • Succ   Returns the value after the argument, the successor. 
  • Ord    Returns a number indicating the order of the argument within
           the set of values of the data type. 
  • Low    Returns the lowest value in the range of the ordinal type 
           passed as its parameter. 
  • High   Returns the highest value in the range of the ordinal data type.

Real Types[]

Real types represent floating-point numbers in various formats. The smallest storage size is given by Single numbers, which are implemented with a 4-byte value. Then there are Double floating-point numbers, implemented with 8 bytes, and Extended numbers, implemented with 10 bytes.

Date and Time[]

TDateTime is not a predefined type the compiler understands, but it is defined in the system unit as:

   TDateTime = type Double;

System Routines for the TDateTime Type:

   Routine                       Description
   ¤ Now                         Returns the current date and time into a 
                                 single TDateTime value.
   ¤ Date                        Returns only the current System date. 
   ¤ Time                        Returns only the current System time. 
   ¤ DateTimeToStr               Converts a date and time value into a string,
                                 using default formatting; 
                                 to have more control on the conversion use 
                                 the FormatDateTime function instead. 
   ¤ DateTimeToString            Copies the date and time values into a 
                                 string buffer, with default formatting.
   ¤ DateToStr                   Converts the date portion of a TDateTime 
                                 value into a string. 
   ¤ TimeToStr                   Converts the time portion of a TDateTime 
                                 value into a string. 
   ¤ FormatDateTime              Formats a date and time using the specified 
                                 format; you can specify which values you want
                                 to see and which format to use, providing a 
                                 complex format string.
   ¤ StrToDateTime               Converts a string with date and time 
                                 information to a TDateTime value, raising 
                                 an exception in case of an error in the format
                                 of the string. 
   ¤ StrToDate                   Converts a string with a date value into 
                                 the TDateTime format. 
   ¤ StrToTime                   Converts a string with a time value into the
                                 TDateTime format. 
   ¤ DayOfWeek                   Returns the number corresponding to the day 
                                 of the week of the TDateTime value passed as
   ¤ DecodeDate                  Retrieves the year, month, and day values from
                                 a date value. 
   ¤ DecodeTime                  Retrieves out of a time value. 
   ¤ EncodeDate                  Turns year, month, and day values into a 
                                 TDateTime value. 
   ¤ EncodeTime                  Turns hour, minute, second, and millisecond 
                                 values into a TDateTime value.

Besides calling TimeToStr and DateToStr you can use the more powerful FormatDateTime function, as I've done in the last method above (see the Delphi Help file for details on the formatting parameters). Notice also that time and date values are transformed into strings depending on Windows international settings. Delphi reads these values from the system, and copies them to a number of global constants declared in the SysUtils unit.

Some of them are:

  • DateSeparator: Char;
  • ShortDateFormat: string;
  • LongDateFormat: string;
  • TimeSeparator: Char;
  • TimeAMString: string;
  • TimePMString: string;
  • ShortTimeFormat: string;
  • LongTimeFormat: string;
  • ShortMonthNames: array [1..12] of string;
  • LongMonthNames: array [1..12] of string;
  • ShortDayNames: array [1..7] of string;
  • LongDayNames: array [1..7] of string;

Typecasting and Type Conversions[]

   N: Integer;
   C: Char;
   B: Boolean;
   N := Integer ('X');
   C := Char (N);
   B := Boolean (0);

System Routines for Type Conversion:

  Routine                   Description
  ¤ Chr                     Converts an ordinal number into an ANSI character.
  ¤ Ord                     Converts an ordinal-type value into the number 
                            indicating its order.
  ¤ Round                   Converts a real-type value into an Integer-type 
                            value, rounding its value. 
  ¤ Trunc                   Converts a real-type value into an Integer-type 
                            value, truncating its value. 
  ¤ Int                     Returns the Integer part of the floating-point 
                            value argument. 
  ¤ IntToStr                Converts a number into a string. 
  ¤ IntToHex                Converts a number into a string with its 
                            hexadecimal representation. 
  ¤ StrToInt                Converts a string into a number, raising an 
                            exception if the string does not represent 
                            a valid integer. 
  ¤ StrToIntDef             Converts a string into a number, using a default 
                            value if the string is not correct. 
  ¤ Val                     Converts a string into a number (traditional 
                            Turbo Pascal routine, available for
  ¤ Str                     Converts a number into a string, using formatting 
                            parameters (traditional Turbo Pascal
                            routine, available for compatibility). 
  ¤ StrPas                  Converts a null-terminated string into a 
                            Pascal-style string. This conversion is 
                            automatically done for AnsiStrings in 32-bit 
  ¤ StrPCopy                Copies a Pascal-style string into a 
                            null-terminated string. This conversion is done 
                            with a 
                            simple PChar cast in 32-bit Delphi.
  ¤ StrPLCopy               Copies a portion of a Pascal-style string into 
                            a null-terminated string. 
  ¤ FloatToDecimal          Converts a floating-point value to record 
                            including its decimal representation 
                            (exponent, digits, sign).  
  ¤ FloatToStr              Converts the floating-point value to its string 
                            representation using default formatting. 
  ¤ FloatToStrF             Converts the floating-point value to its string 
                            representation using the specified formatting. 
  ¤ FloatToText             Copies the floating-point value to a string 
                            buffer, using the specified formatting. 
  ¤ FloatToTextFmt          As the previous routine, copies the 
                            floating-point value to a string buffer, 
                            using the specified formatting. 
  ¤ StrToFloat              Converts the given Pascal string to a 
                            floating-point value. 
  ¤ TextToFloat             Converts the given null-terminated string 
                            to a floating-point value.

Subrange Types[]

A subrange type defines a range of values within the range of another type (hence the name subrange). You can define a subrange of the Integer type, from 1 to 10 or from 100 to 1000, or you can define a subrange of the Char type, as in:

   Ten = 1..10;
   OverHundred = 100..1000;
   Uppercase = 'A'..'Z';

In the definition of a subrange, you don’t need to specify the name of the base type. You just need to supply two constants of that type. The original type must be an ordinal type, and the resulting type will be another ordinal type.

When you have defined a subrange, you can legally assign it a value within that range.

This code is valid:

   UppLetter: UpperCase;
   UppLetter := 'F';

But this one is not:

   UppLetter: UpperCase;
   UppLetter := 'e'; // compile-time error

Writing the code above results in a compile-time error, "Constant expression violates subrange bounds." If you write the following code instead:

   UppLetter: Uppercase;
   Letter: Char;
   Letter :='e';
   UppLetter := Letter;

Delphi will compile it. At run-time, if you have enabled the Range Checking compiler option (in the Compiler page of the Project Options dialog box), you’ll get a Range check error message.

Enumerated Types[]

Enumerated types constitute another user-defined ordinal type. Instead of indicating a range of an existing type, in an enumeration you list all of the possible values for the type. In other words, an enumeration is a list of values.

Here are some examples:

   Colors = (Red, Yellow, Green, Cyan, Blue, Violet);
   Suit = (Club, Diamond, Heart, Spade);

Each value in the list has an associated ordinality, starting with zero. When you apply the Ord function to a value of an enumerated type, you get this zero-based value. For example, Ord (Diamond) returns 1.

The Delphi VCL (Visual Component Library) uses enumerated types in many places.

For example, the style of the border of a form is defined as follows:

   TFormBorderStyle = (bsNone, bsSingle, bsSizeable,
   bsDialog, bsSizeToolWin, bsToolWindow);

When the value of a property is an enumeration, you usually can choose from the list of values displayed in the Object Inspector, as shown in Figure.

Figure: An enumerated type property in the Object Inspector

Object Inspector

The Delphi Help file generally lists the possible values of an enumeration. As an alternative you can use the OrdType program, available on [1], to see the list of the values of each Delphi enumeration, set, subrange, and any other ordinal type.

Set Types[]

Set types indicate a group of values, where the list of available values is indicated by the ordinal type the set is based onto. These ordinal types are usually limited, and quite often represented by an enumeration or a subrange. If we take the subrange 1..3, the possible values of the set based on it include only 1, only 2, only 3, both 1 and 2, both 1 and 3, both 2 and 3, all the three values, or none of them.

A variable usually holds one of the possible values of the range of its type. A set-type variable, instead, can contain none, one, two, three, or more values of the range. It can even include all of the values.

Here is an example of a set:

   Letters = set of Uppercase;

Now I can define a variable of this type and assign to it some values of the original type. To indicate some values in a set, you write a comma-separated list, enclosed within square brackets. The following code shows the assignment to a variable of several values, a single value, and an empty value:

   Letters1, Letters2, Letters3: Letters;
   Letters1 := ['A', 'B', 'C'];
   Letters2 := ['K'];
   Letters3 := [];

In Delphi, a set is generally used to indicate nonexclusive flags. For example, the following two lines of code (which are part of the Delphi library) declare an enumeration of possible icons for the border of a window and the corresponding set type.

   TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);
   TBorderIcons = set of TBorderIcon;

A set-type property in the Object Inspector:

Set Type Property

Another property based on a set type is the style of a font. Possible values indicate a bold, italic, underline, and strikethrough font. Of course the same font can be both italic and bold, have no attributes, or have them all. For this reason it is declared as a set. You can assign values to this set in the code of a program as follows:

  Font.Style := []; // no style
  Font.Style := [fsBold]; // bold style only 
  Font.Style := [fsBold, fsItalic]; // two styles

Array Types[]

Array types define lists of a fixed number of elements of a specific type. You generally use an index within square brackets to access to one of the elements of the array. The square brackets are used also to specify the possible values of the index when the array is defined. For example, you can define a group of 24 integers with this code:

   DayTemperatures = array [1..24] of Integer;

In the array definition, you need to pass a subrange type within square brackets, or define a new specific subrange type using two constants of an ordinal type. This subrange specifies the valid indexes of the array. Since you specify both the upper and the lower index of the array, the indexes don’t need to be zero-based, as is necessary in C, C++, Java, and other programming languages.

Simple and Compound Statements[]

A Pascal statement is simple when it doesn't contain any other statements. Examples of simple statements are assignment statements and procedure calls. Simple statements are separated by a semicolon:

 X := Y + Z;  // assignment
 Randomize;   // procedure call

Usually, statements are part of a compound statement, marked by begin and end brackets. A compound statement can appear in place of a generic Pascal statement.

Here is an example:

   A := B;
   C := A * 2;
 The semicolon after the last statement before the end isn't required, 
 as in the following: 
   A := B;
   C := A * 2

Both versions are correct. The first version has a useless (but harmless) semicolon. This semicolon is, in fact, a null statement; that is, a statement with no code. Notice that, at times, null statements can be used inside loops or in other particular cases.

Conditional Statements[]

A conditional statement is used to execute either one of the statements it contains or none of them, depending on some test. There are two basic flavors of conditional statements: if statements and case statements.

If Statements

  procedure TForm1.Button1Click(Sender: TObject);
    { simple if statement }
    if CheckBox1.Checked then
      ShowMessage ('CheckBox1 is checked')
  procedure TForm1.Button2Click(Sender: TObject);
    // if-then-else statement
    if CheckBox2.Checked then
      ShowMessage ('CheckBox2 is checked')
      ShowMessage ('CheckBox2 is NOT checked');

Notice that you cannot have a semicolon after the first statement and before the else keyword, or the compiler will issue a syntax error. The if-then-else statement, in fact, is a single statement, so you cannot place a semicolon in the middle of it.

  procedure TForm1.Button4Click(Sender: TObject);
    // compound if statement
    if CheckBox1.Checked then
      if CheckBox2.Checked then
        ShowMessage ('CheckBox1 and 2 are checked')
        ShowMessage ('Only CheckBox1 is checked')
      ShowMessage ('Checkbox1 is not checked, who cares for Checkbox2?')

Case Statements

If your if statements become very complex, at times you can replace them with case statements. A case statement consists in an expression used to select a value, a list of possible values, or a range of values. These values are constants, and they must be unique and of an ordinal type. Eventually, there can be an else statement that is executed if none of the labels correspond to the value of the selector.

Here are two simple examples:

  case Number of
   1: Text := 'One';
   2: Text := 'Two';
   3: Text := 'Three';
  case MyChar of
   '+' : Text := 'Plus sign';
   '-' : Text := 'Minus sign';
   '*', '/': Text := 'Multiplication or division';
   '0'..'9': Text := 'Number';
   'a'..'z': Text := 'Lowercase character';
   'A'..'Z': Text := 'Uppercase character';
   Text := 'Unknown character';

Loops in Pascal[]

The Pascal language has the typical repetitive statements of most programming languages, including for, while, and repeat statements. Most of what these loops do will be familiar if you've used other programming languages, so I'll cover them only briefly.

The For Loop

The for loop in Pascal is strictly based on a counter, which can be either increased or decreased each time the loop is executed. Here is a simple example of a for loop used to add the first ten numbers.

    K, I: Integer;
    K := 0;
    for I := 1 to 10 do
      K := K + I;

This same for statement could have been written using a reverse counter:

    K, I: Integer;
    K := 0;
    for I := 10 downto 1 do
      K := K + I;

The for loop in Pascal is less flexible than in other languages (it is not possible to specify an increment different than one), but it is simple and easy to understand. If you want to test for a more complex condition, or to provide a customized counter, you need to use a while or repeat statement, instead of a for loop.

Note: The counter of a for loop doesn't need to be a number. It can be a value of any ordinal type, such as a character or an enumerated type.

While and Repeat Statements

The difference between the while-do loop and the repeat-until loop is that the code of the repeat statement is always executed at least once. You can easily understand why by looking at a simple example:

  while (I <= 100) and (J <= 100) do
    // use I and J to compute something...
    I := I + 1;
    J := J + 1;
    // use I and J to compute something...
    I := I + 1;
    J := J + 1;
  until (I > 100) or (J > 100);

If the initial value of I or J is greater than 100, the statements inside the repeat-until loop are executed once anyway.

The other key difference between these two loops is that the repeat-until loop has a reversed condition. The loop is executed as long as the condition is not met. When the condition is met, the loop terminates. This is the opposite from a while-do loop, which is executed while the condition is true. For this reason I had to reverse the condition in the code above to obtain a similar statement.

The With Statement[]

  Form1.Canvas.Pen.Width := 2;
  Form1.Canvas.Pen.Color := clRed;

But it is certainly easier to write this code:

  with Form1.Canvas.Pen do
    Width := 2;
    Color := clRed;

When you are writing complex code, the with statement can be effective and spares you the declaration of some temporary variables, but it has a drawback. It can make the code less readable, particularly when you are working with different objects that have similar or corresponding properties.

A further drawback is that using the with statement can allow subtle logical errors in the code that the compiler will not detect.

For example:

  with Button1 do
    Width := 200;
    Caption := 'New Caption';
    Color := clRed;

This code changes the Caption and the Width of the button, but it affects the Color property of the form, not that of the button! The reason is that the TButton components don't have the Color property, and since the code is executed for a form object (we are writing a method of the form) this object is accessed by default.

If we had instead written:

  Button1.Width := 200;
  Button1.Caption := 'New Caption';
  Button1.Color := clRed; // error!

Pascal Procedures and Functions[]

In Pascal, a routine can assume two forms: a procedure and a function. In theory, a procedure is an operation you ask the computer to perform, a function is a computation returning a value. This difference is emphasized by the fact that a function has a result, a return value, while a procedure doesn't. Both types of routines can have multiple parameters, of given data types.

  procedure Hello;
    ShowMessage ('Hello world!');
  function Double (Value: Integer) : Integer;
    Double := Value * 2;
  // or, as an alternative
  function Double2 (Value: Integer) : Integer;
    Result := Value * 2;

Reference Parameters[]

Pascal routines allow parameter passing by value and by reference. Passing parameters by value is the default: the value is copied on the stack and the routine uses and manipulates the copy, not the original value.

Passing a parameter by reference means that its value is not copied onto the stack in the formal parameter of the routine (avoiding a copy often means that the program executes faster). Instead, the program refers to the original value, also in the code of the routine. This allows the procedure or function to change the value of the parameter. Parameter passing by reference is expressed by the var keyword.

Here is an example of passing a parameter by reference using the var keyword:

  procedure DoubleTheValue (var Value: Integer);
    Value := Value * 2;

In this case, the parameter is used both to pass a value to the procedure and to return a new value to the calling code.

When you write:

    X: Integer;
    X := 10;
    DoubleTheValue (X);

The value of the X variable becomes 20, because the function uses a reference to the original memory location of X, affecting its initial value.

Passing parameters by reference makes sense for ordinal types, for old-fashioned strings, and for large records. Delphi objects, in fact, are invariably passed by value, because they are references themselves. For this reason passing an object by reference makes little sense (apart from very special cases), because it corresponds to passing a "reference to a reference."

Constant Parameters[]

The  keyword is used to start a section of constant definitions.  The section is terminated by the next keyword in a program.   Within the section, one or more constants may be defined. These can be a mixture of normal or typed constants:  These give a name Name1 to a fixed expression,Expression1. The expression must resolve into one of the following types:  These are very odd. They are constant only in the sense that their value persists all parts of a program. Yet it can be changed (as long as the compiler directive {WriteableConst} is set On).  They are used, for example, when a routine needs to hold values that are preserved across calls.  It is better to use Object Oriented principles to allow data to be preserved across accesses.   When passing data to a routine (function or procedure), you can prefix the parameter definition with  if the value is never updated. This marginally improves performance, clarifies routine operation, and prevents accidental updates of the value.

  function DoubleTheValue (const Value: Integer): Integer;
    Value := Value * 2;      // compiler error
    Result := Value;

Dynamic Arrays[]

  procedure TForm1.Button1Click(Sender: TObject);
    Array1: array of Integer;
    Array1 [1] := 100; // error
    SetLength (Array1, 100);
    Array1 [99] := 100; // OK

Variants Have No Type[]

In general, you can use variants to store any data type and perform numerous operations and type conversions. Notice that this goes against the general approach of the Pascal language and against good programming practices.

A variant is type-checked and computed at run time. The compiler won't warn you of possible errors in the code, which can be caught only with extensive testing.

    V: Variant;
  you can assign to it values of several different types:
  V := 10;
  V := 'Hello, World';
  V := 45.55;

Once you have the variant value, you can copy it to any compatible-or incompatible-data type. If you assign a value to an incompatible data type, Delphi performs a conversion, if it can. Otherwise it issues a run-time error.

  procedure TForm1.Button1Click(Sender: TObject);
    V: Variant;
    V := 10;
    Edit1.Text := V;
    V := 'Hello, World';
    Edit2.Text := V;
    V := 45.55;
    Edit3.Text := V;