Sunteți pe pagina 1din 118

Delphi (Beginner) Chapter 3 Creating your first 'Hello World' Delphi Application Page 1: Creating a simple project in Delphi.

Welcome to the third chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. An overview of application development with Delphi, including creating a simple project, writing code, compiling and running a project. Also, find out how to ask Delphi for help. More of this Feature Page 2: Writing and compiling code Page 3: Saving your work, geting help

Join the Discussion Creating your first 'Hello World' Delphi "Post your views and Application comments to this It's time to create a simple example in Delphi now. chapter of the free When you start Delphi, a default project is created Delphi Programming with one form. This default project automatically Course" creates a blank form, with its associated unit file and Discuss! a project file, among others. Related To get started, from the beginning, close anything Resources that's open by choosing File | Close All from the A Beginner's Guide to main menu. Delphi Programming.TOC Before you create your first Delphi project, you need to know what you want to develop; a DLL, MDI application, SDI application a CLX application (for Linux) or something else. To start without all the bells and whistles we'll create a standard SDI Windows application. Simply point your mouse to File | New and select Application. This creates a new project group with a single application in it.

Page | 1

The new project contains an empty form, a unit (associated with its form), and a project file. As you develop and build your application, new files will be created and added to the project. The project files are listed in the Project Manager window, display it by selecting View | Project Manager from the main Delphi menu. With the Project Manager, you can easily visualize how all your project files are related. If you share files among different projects, using the Project Manager is recommended because you can quickly and easily see the location of each file in the project. Application vs. CLX Application With some versions of Delphi (supposed Delphi 6 Professional or Enterprise), you can build and develop cross platform applications that can be ported to Linux and compiled withKylix. To develop a CLX application, instead of standard Windows application, you could pick CLX Application from the File | New menu. The Delphi IDE is similar to one when you build Windows applications, except that the Component palette changes dynamically to show the objects that are available for use in Linux CLX applications. Since this course is about Delphi for the Windows platform, we will be exploring Delphi programming from that point of view. However, if you have Kylix and want to join this course you are of course encouraged to do so. Even though, my intention at this stage of this Course, is not to explain differences between CLX (Linux) and VCL (Windows) development you should know that there are no reasons why you should not join the course and just have in mind that when we talk about, let's say, form1.DFM you think form1.XFM. Creating your first 'Hello World' Delphi Application Page 2: Writing code, compiling and running your application for the first time.

Hello Delphi Now that we've created a project, we can begin work on our first application. This first application will be pretty simple - we'll change the caption of the (main) form once the application is executed. The change will be initiated from code - no user interaction will be necessary. To add the code that changes the caption of the form we need to *call* the Code Editor window. If you have the Project Manager displayed on the screen, double click the Form1. This will bring up the Form designer window to the front. Another way to bring the Form1 to the front of the screen is to select Form1 from the Window menu. Once Form1 is on top and active, double click it. This action has the following result: the Code editor is positioned on the top of the screen and Delphi creates the skeleton code for the new event handler. Note: another way of achieving the same result is to activate Form1 in the Object Inspector, select the Events tab and double click in the OnCreate column value.

Page | 2

As stated in the second chapter of this course, each form has a collection of events such as a mouse click, keypress, or component activation for which you can specify some additional behavior. In this case the event is called OnCreate. This event occurs when the form is created. The skeleton code looks like:

procedure TForm1.FormCreate(Sender: TObject); begin //this is where your code goes end

For the moment do not get bothered with the meaning of the text you see. Now alter the code so that it looks like:

procedure TForm1.FormCreate(Sender: TObject); begin Caption := 'Hello Delphi! ' + DateTimeToStr(Now); end

Running a project for the first time To see the results of this action, you need to (successfully) compile and run you project. From the Run menu choose Run or press F9. The compiler will try to build the project and execute your application. If the compiler encounters an error, it displays an Error dialog box. When you choose OK to dismiss the dialog box, the Code editor places the cursor on the line of code containing the error.

Page | 3

Note: if you want to see progress reports while your program compiles, you'll need to check the "Show compiler progress" check box in the "Compiling and running" section on the Preferences page of the Environment Options dialog box. Call this dialog box by selecting Environment Options from the Tools menu.

If everything goes well (it should) your application is executed and you see a blank form on the screen. Note several things. First, the form is blank - there are no dots that make up the grid you see when designing the form look. Second, there is a new button on the Windows Task Bar - when you point to it you'll see that it has the Project1 caption. Third, the caption of Delphi IDE is changed from "Delphi 6 - Project 1" to "Delphi 6 - Project 1 [Running]". And fourth, most important for us, the caption of the form is Hello Delphi ! + *date and time of the execution*. There is not much you can do with this window, you can move it resize it and finally close it. Every time you (compile and) run this project a form caption will say Hello Delphi with the date and time of the execution. Ok, I know this is not a lot, but be patient - this is your first project - it is not supposed to do something meaningful. If you want a little more, here goes another simple example. Creating your first 'Hello World' Delphi Application Page 3: Saving your work. Getting help from Delphi.

Saving the project To properly get the job done, you should save the project, along with all its associated files. To save the current form design and its code, select File | Save All from the main menu bar. By default, Delphi opens the Projects folder. I suggest you to create a new folder (inside the Projects folder) for your project. Let's call it "HelloExample". While in the Save As dialog, open the newly created HelloExample folder and save the following
Page | 4

files: . save Unit1 as MainUnit.pas . save Project1 as HelloProject.dpr Note 1: When you have saved the unit file, the corresponding form was saved as MainUnit.dfm Note 2: In the Code Editor window, Unit1 is now referred to as MainUnit. Note 3: Since you have saved the project with the *new* name, if you run your application now, the button on the Task Bar will say "HelloProject". Of course the name of the application and the name of the project do not need to be the same, later we will see how to change the name of a Delphi application.

Note, if you open up the HelloExample folder in the Windows Explorer, you should find several files inside it. These are MainUnit.pas, MainUnit.dfm and several others. The most important file inside this folder is the HelloProject.exe. This is your applications executable file, if you double click it you'll execute it. If you want to "install" your application on another machine this is the only file you need to copy. Getting HELP from Delphi Let's stop for the moment to explore ways to get help from Delphi in situations when help is necessary. First of all, Delphi is supplied with extensive documentation. If you do not have the printed manuals, those that came (as PDF) with the installation will do. As stated in the first chapter of this course, the books include: . Quick Start - a brief introduction to Delphi, . Object Pascal Language Guide - a complete reference to the underlying Delphi programming language, and . Developers Guide - which covers advanced topics, from creating database applications to creating your custom components. Beside printed materials, Delphi holds a great deal of information in the Help system. Even though you'll need to learn how to use it, it is really worth it - there are many code examples to help you understand all the nuts and bolts of Object Pascal programming. What's more, context-sensitive Help is available from nearly every portion of the Code editor. To get context-sensitive Help from the Code editor window simply place the cursor on the property, event, method, procedure or type for which you want Help, then press F1. Try it. Position the mouse cursor inside the word "Caption" in the Code Editor (the wordCaption you typed in the only example so far) and hit the F1 key.

Page | 5

Once you press the F1 key, a pop up window will ask you to specify more exactly what you want to know. Here comes the hard part: how in the world you know what topic to pick. The "problem" lies in the fact that, in Delphi, many components have properties of the same name (and behavior). To get the help on Form Caption property you need to pick TControl.Caption. Why TControl, when you are working with Form not something called TControl? Well, for the moment this is hard to explain, let's just say that Form derives from something called Control and that Control has a Caption property. What you will find out is that in general, Caption is used for text that appears as a window title. But how will you know what to pick? There is a solution. Point to Object Inspector, Properties page. Select the property you want to find out about and than press F1. Some exercises for you... Since this Course is an online course, there is much you can do to prepare for the next chapter. At the end of each chapter I'll try to provide several tasks for you to get more familiar with Delphi and the topics we discuss in the current chapter. Here are some exercises for you: 0. Learn about the Name property of the Form object. Note that the Name property should tell you what the form does. 1. Explore the Object Inspector and try to figure what properties relate to the Form positioning on the screen (Left, Top, Width, Height, ...) and what happens when you alter them at design time. 2. Try to change the color of the Form from the Object Inspector (Color property) 3. Learn about the BorderIcons and BorderStyle properties and how they relate to visual representation of the Form at run time. 4. Find what exactly DateTimeToStr is used for. 5. Be sure not to miss the next chapter!

Chapter 4 Learn about: properties, events and Delphi Pascal Page 1: Placing Components on a Form

Page | 6

Welcome to the fourth chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. Create your second simple Delphi application allowing you to learn hot to place components on a form, set their properties, write event handler procedures to make components cooperate together. Creating your second Delphi Application In the previous chapter we have created a simple Delphi application without going into details of the Object Pascal language behind all Delphi projects. This time, our task is to create a more complex application that involves several Delphi components - again, the application will be pretty simple. The idea is to help you understand the Delphi Pascal source and how components operate and communicate with their properties, methods and events. To start, run Delphi. As explained, when you run Delphi a new project (application) is created with one blank form. Placing Components on a Form All forms of a Delphi application have one or more components. Components, or objects, usually display information or allow the user to perform an action. For example a Label is used to display static text, an Edit box is used to allow user to input some data, a Button can be used to initiate actions. Any combination of components can be placed on a form, you should remember that Windows is even-driven, while your application is running a user can interact with any component on a form, it is your task, as a programmer, to decide what happens when a user clicks a button or changes a text in an Edit box. As stated before, Delphi supplies a number of components for you to create complex user interfaces. You can find all the components you can place on a form on the Component palette. To place a component on a form, locate its icon on the Palette and double-click it. This action places a component on the active form. Visual representation of most components is set with their set of properties. When you first place a component on a form, it is placed in a default position, with default width and height. You can change the size and position later, by using the Object Inspector. Note: to remove a component from a form, click on it and press the [Del] key. Later, in this Course, I'll explain what happens to code (if some exists) associated with the component. Your second Delphi application We can now start adding components to a form. Activate the only form in a project, point to the Component palette and select the "Standard" tab.
Page | 7

We will add three standard Windows components and write some example code to see how components work together.

Double click the following three components: TLabel : use this component when you want to add some text to a form that the user can't edit. TEdit : standard Windows edit control. Edit controls are used to retrieve text that users type. TButton : use this component to put a standard push button on a form. Using drag-and-drop to rearrange the components to appear on a form similar to:

Note: even though at this stage of the Course, it is not important to create "nicely looking" user interfaces, I strongly encourage you to always try to arrange components on form so they create a user friendly interface. Here are some suggestions and Standards of Screen Design.

Learn about: properties, events and Delphi Pascal Page 2: Changing Component Properties; Writing Code - Events and Event Handlers Changing Component Properties After you place components on a form, you can set their properties with the Object Inspector. The properties are different for each type of component, some properties apply to most components. Altering a component property, changes the way a component behaves and appears in an application. All the components have a property called "Name". The Name property is very important; it specifies the name of the component as referenced in code. When you first place a component on a form, Delphi will provide a default name for the component: Label1, Edit1, Button1. I suggest you to
Page | 8

give your components a meaningful name before writing the code that refers to them. You can do this by changing the value of the Name property in the Object Inspector. Here are some suggestions on how to give names to components. Note: with the last statement in mind, I'll do the opposite. In most cases, I'll leave all the default component names through this Course - just as they appear when you place them on a form. To actually change a component property you first need to activate it click it to select it - small square handles appear at each corner and in the middle of each side. Another way to select a component is to click its name in the drop down list that appears at the top of the Object Inspector. This list lists all the components on the active form along with their types in the following format: "Name Type". When a component is selected, its properties (and events) are displayed in the Object Inspector. To change the component property click on a property name in the Object Inspector; then either type a new value or select from the drop-down list. For example, change the Caption property for Button1 (I'll refer components by their names) to 'Hello...' (of course without the single quotation marks)

Components have different kinds of properties; some can store a boolean value (True or False), like Enabled. To change a boolean property double click the property value to toggle between the states. Some properties can hold a number (e.g. Width or Left), a string (e.g. Caption or Text) or even a set of "simple valued" properties. When a property has an associated editor, to set complex values, an ellipsis button appears near the property name. For example if you click the ellipsis of the Font property a Font property dialog box will appear. Now, change the Caption (the static text the label displays on the form) of Label1 to 'Your name please:'. Change the Text property (text displayed in the edit box - this text will be changeable at run time) of Edit1 to 'Zarko Gajic' (this is my name, write your name).

Page | 9

Writing Code - Events and Event Handlers To really enable components to do something meaningful you have to write some actionspecific code for each component you want to react on user input. Remember: components are building block of any Delphi form, the code behind each component ensures a component will react on an action. Each Delphi component, beside its properties, has a set of events. Windows as even-led environment requires the programmer to decide how a program will (if it will) react on user actions. You need to understand that Windows is a message-based operating system. System messages are handled by a message handler that translates the message to Delphi event handlers. For instance, when a user clicks a button on a form, Windows sends a message to the application and the application reacts to this new event. If the OnClick event for a button is specified it gets executed. The code to respond to events is contained in Delphi event procedures (event handlers). All components have a set of events that they can react on. For example, all clickable components have an OnClick event that gets fired if a user clicks a component with a mouse. All such components have an event for getting and loosing the focus, too. However if you do not specify the code for OnEnter and OnExit (OnEnter got focus; OnExit - lost focus) the event will be ignored by your application. To see a list of events a component can react on, select a component and in the Object Inspector activate the Events tab. To really create an event handling procedure, decide on what event you want your component to react, and double click the event name. For example, select the Button1 component, and double click the OnClick event name. Delphi will bring the Code Editor to the top of the screen and the skeleton code for the OnClick event will be created.

procedure TForm1.Button1Click(Sender: TObject); begin //this is where your code goes end

Note: For the moment there is no need to understand what all the words in the above code stand for. Just follow along, we'll explain all that in the following chapters.

Page | 10

As you will understand more clearly through this course, a procedure must have a unique name within the form. The above procedure, Delphi component event-driven procedure, is named for you. The name consists of: the name of the form (prefixed with T) "TForm", a full stop ".", the component name "Button1", and the event name "Click". For any component there is a set of events that you could create event handlers for. Just creating an event handler does not guarantee your application will do something on the event - you must write some event handling code in the body of the procedure. A few words on Delphi (Object) Pascal The code you write inside event procedures is Pascal code. Object Pascal or Delphi Pascal (as I will mostly call it), a set of object-oriented extensions to standard Pascal, is the language of Delphi. Delphi Pascal enables you to take advantage of object-oriented programming to its fullest. It can be said that Delphi Pascal is to Pascal what C++ is to C. As Delphi was being developed, new language behavior and keywords were added to deal with the component model. In general, Delphi Pascal is a high-level, compiled, strongly typed language that supports structured and object-oriented design. We'll now write some code for the OnClick event handler of Button1. Alter the above procedure body to:

procedure TForm1.Button1Click(Sender: TObject); var s: string; begin s := 'Hello ' + Edit1.Text + ' Delphi welcomes you!'; ShowMessage(s); end; A few words on Delphi Code completion When you reach to the second line and write "Edit1." wait a little, Delphi will display a list box with all the properties of the edit box you can pick. In general, it lists valid elements that you can select from and add to your code. Here's more info on Delphi Code Insight tools. Now, hit F9 to compile and run your project. When the program starts, click the Button1 ('Hello...'). A message box will pop up saying 'Hello Zarko Gajic, Delphi welcomes you!'. Change the text in the Edit box and hit the Button again...

Page | 11

What follows is a simple explanation of the code that runs this application. Let's see. The first line under the procedure name, var s: string;, declares a string typevariable. Variables in Delphi Pascal hold information (values). Variables have to be declared before they can be used. We do this after the var keyword. The first line under the begin keyword, s := 'Hello ' + Edit1.Text + ' Delphi welcomes you!'; sets a value for the variable s. This assignment involves reading a value of the Text property for the Edit component. If you ask Delphi about the Text property of an Edit component, you'll find out that it holds the text string that is displayed in the edit box. That text is of the TCaption type, actually the string type. The last statement, before the end keyword, ShowMessage(s);, is the one that calls the message dialog and sends it the value of variable s - this results in a pop up box your see above. That's it. Again, not too smart, not too hard but serves the purpose. By now you should know how to place components on a form, set their properties and even do a small do-somethingfunny Delphi application. Be sure to visit all the links in the above paragraph. Some exercises for you... Since this Course is an online course, there is much you can do to prepare for the next chapter. At the end of each chapter I'll try to provide several tasks for you to get more familiar with Delphi and the topics we discuss in the current chapter. Here are some exercises for you, after you finish reading this chapter: 1. Play with the Color property of the Form object 2. Use the Font property Editor to alter the font of the TLabel component 3. Find out about the PasswordChar property of the TEdit component to create a simple password dialog form 4. Try adding a code for the OnCreate event of a form to make a form appear centered on a screen. Also, make yourself familiar with the Position property of the TForm object. 5. Be sure not to miss the next chapter! Chapter 5 Understanding the Delphi unit source Page 1: Delphi Unit source code
Page | 12

Welcome to the fifth chapter of the FREE online programming course: A Beginner's Guide to Delphi Programming. Take a closer look at exactly what each keyword means by examining each line of the Delphi form unit source code. Interface, implementation, uses and other keywords explained in easy language! Understanding the unit source In the previous chapter you have created your second simple Delphi application without going into details about the Delphi Pascal keywords that appear in each Delphi unit. This time, our task is to take a look at exactly what each keyword means by examining each line of the forms unit source code. As you know already by now, forms are visible building blocks of all (well, at least 99%) Delphi projects. Each form in a Delphi project has an associated unit. The unit contains the source code for any event handlers attached to the events of the form or the components it contains. The best way to describe the unit code is to take a look at the source. For the moment reefer to the example in the last chapter, especially the unit source. After we have placed a Label, an Edit box and a Button, and added an OnClick event handling procedure for the button, the source code looked like:

01: unit Unit1;

02: interface

03: uses 03: Windows, Messages, SysUtils, Variants, Classes, 03: Graphics, Controls, Forms, Dialogs, StdCtrls;

04: type

Page | 13

05: TForm1 = class(TForm) 06: 07: 08: 09: Edit1: TEdit; Button1: TButton; Label1: TLabel; procedure Button1Click(Sender: TObject);

10: private 11: { Private declarations }

12: public 13: { Public declarations }

14: end;

15: var 16: Form1: TForm1;

17: implementation 18: {$R *.dfm}

19: procedure TForm1.Button1Click(Sender: TObject); 20: var s: string; 21: begin 22: s := 'Hello ' + Edit1.Text + ' Delphi welcomes you!';

Page | 14

23: ShowMessage(s); 24: end;

25: end.

We'll now explore and try to figure what each line stands for. First, a note: Delphi units follow a predefined format. For example, the UNIT keyword must be the first line in the source, followed by INTERFACE... This strict format is "predefined" by Delphi, meaning that when you add an empty form to a project, its associated unit source is already created with special keywords and type declaration. When you add a component on a form and write an event handler for it, Delphi will place the corresponding code at the exact location in the unit file. Note another thing: in the above code, black lines appear in the forms unit source the first time you add a form to a project. Lines colored in green were added by Delphi when you have placed those three components on a form. Lines in red were added by you in the previous chapter. Whenever you create a new form, Delphi creates the corresponding unit file with the skeleton code marked black. The rest of the article will discuss parts of the unit source. Several new words like class, object and similar will be mentioned, do not get frightened if you do not understand what they mean, let's just say that such words are a part of Delphi object oriented programming, we will discuss them in the following chapters more clearly Understanding the Delphi unit source Page 2: Interface, implementation, uses and other keywords explained in easy language! The UNIT keyword A unit file begins with a unit heading, which is followed by the interface, implementation, initialization, and finalization sections. The initialization and finalization sections are optional. The unit heading starts with a word unit (line 01), followed by a unit (file) name. The unit name (Unit1 in the above source) must match the unit file name on a disk. In a single project all unit names must be unique. You should change the unit's name only by using the File-Save As command from the Delphi IDE main menu. Of course, it is completely up to you to
Page | 15

decide how will you name your units. In most cases you'll want your units to have the name similar to the name of the form to which they are linked, like 'MainUnit' for Main form (form with a Name property set to 'Main'). Be sure to give name to units in the early stage of a form design development. The INTERFACE section The interface section of a unit starts with the word interface (line 02) and continues until the word implementation (line 17). This section is used to declare any publicsections of code that appear in a unit. The entire contents of the interface section, including type, variable and procedure declarations, are visible to any other unit which uses this unit. When any other part of the program looks at a unit, all it sees is the interface section. Everything else is hidden, internal to the unit, part of the implementation. You could say that the interface section contains a list of items in the unit that other units can use. In most cases the interface section will define several "subsections", you can see that the code for unit1.pas has a uses clause, a type section, and a variable declaration section. The INTERFACE USES section If the interface section includes a uses clause, it must appear immediately after the word interface. A uses clause (line 03) lists units used by the unit. In most cases, all necessary units are placed in the interface uses clause when Delphi compiler generates and maintains a units source. The Windows, Messages, SysUtils, etc are all standard Delphi units, required by a program. As you drop components on a form, the necessary units will be added automatically to the uses clause. For example, if you add a TOpenDialog component on your form (Dialogs page on the component palette), the Dialogs unit will appear in the uses clause because it contains the logic for the TOpenDialog component (and other Dialog components). In some situations, you'll need to manually add units to interface uses clause. Suppose you are to use the TRegistry object, designed to access the Windows Registry. You cannot drop the TRegistry component on a form, since it does not appear on the component palette - you must manually add the word Registry to the uses list. The INTERFACE TYPE section Another part of the interface section is the type section. The form type declaration (or form class declaration) section introduces the form as a class. The code from line 04 to 14 declares the existence and structure of a class called TForm1.
Page | 16

A few words on classes and objects I'm aware that this is not the place to explain OOP in Delphi, but I sense that something must be stated. The basics of object oriented programming in Delphi will be discussed in the next chapter of this course, however some words must be explained now. A class, or class type, defines a structure consisting of fields, methods, and properties. Instances of a class type are called objects. For example, in real world, a class PROGRAMMER can have properties like: Years_Of_Experience and Projects_Developed. It can expose methods like: Write_Program and Talk_To_Users. A class is something that does not truly exists. An object: DELPHI PROGRAMMER is a specific instance of a class. The TForm1 is a class inherited from TForm (line 05). Each component dropped on a form becomes a field (or variable) of the TForm1 class (lines 06 through 08). For example, Edit1 is a variable of a TEdit type, which you see on the screen when you run the program. When you need to read a value from this particular edit box you use the Edit1 variable, like in 's := Edit1.Text'. Each event handling procedure for a form events or events for components dropped on a form (form fields) will have its declaration (line 09) in the interface type part. For an explanation on private and public parts (lines 10 through 14) of a class declaration, please refer to Hiding Data, a part of the Creating Custom Delphi Components - Inside and Out article. The INTERFACE VAR section This part (line 15,16) of the interface section is used to declare (create) a Form1 object as an instance of the TForm1 class. If you have created your own data type (with fields, properties and methods) as a part of this unit, you could create a variable of that type in this part of the interface section. The IMPLEMENTATION section The implementation section is defined as everything between the implementation word and either the initialization statement or the end of the file (as denoted by the end. keyword). The implementation is where you write code that performs actions. This section is private to the unit, and can contain both declarations and code. The implementation section of a unit can contain its own uses clause as well. A few words on using another unit (form)
Page | 17

As you will see in the following chapters of this course, a (form) unit can use another unit. Simply put, this means that one form can call another form. Suppose you have a main form (form name: MainForm, unit name: MainFormUnit) in a project with an 'About...' button on it. What you want to do is to show an about box form (form name: AboutForm, unit name: AboutFormUnit) when you click on this button. To be able to do this the MainFormUnit must use the AboutFormUnit, the AboutFormUnit should be placed in the implementation uses clause. To actually call a method (procedure or function) from MainFormUnit that is declared in the AboutFormUnit, you use the following syntax: AboutFormUnit.SomeProcedureName(parameters)

Note that the call to a procedure SomeProcedureName consists of a unit name (AboutFormUnit) followed by a period (.) and a procedure name. This fact is very important. If in some stage of the development of your project you decide to save AboutFormUnit under a different name - you will need to change the call to any procedure inside that unit, since the name of the unit will no longer be AboutFormUnit. This is the reason why you should give meaningful name to units in the early stage of form (unit) development. Anything that appears in a unit's implementation section that is not referenced in the interface is private to that unit. This means that a procedure or function declared and defined (implemented) in the implementation section cannot be called from another unit unless its header is listed in that unit's interface. The INITIALIZATION and FINALIZATION sections These two sections are optional; they are not automatically generated when we create a unit. If we want to initialize any data the unit uses, we can add an initialization code to the initialization section of the unit. When an application uses a unit, the code within the unit's initialization part is called before the any other application code runs. If your unit needs to perform any cleanup when the application terminates, such as freeing any resources allocated in the initialization part; you can add a finalization section to your unit. The finalization section comes after the initialization section, but before the final end. Other units of a Delphi project Now, when you have learned about the structure of a Delphi form unit, it's time to see what other units can appear in a project. Every Delphi program has at least two main parts. The first is a project file such as Project1.dpr. You can see the structure of the project file by pointing you mouse to Project|View Source from the Delphi main menu. The second part is one (or more) units attached to forms - the structure of such a unit
Page | 18

is discussed through this article. However, units don't have to be associated with forms. A Code Unit contains code that is called from other units in the project. When you start building libraries of useful routines, you will probably store them in a code unit. To add a new code unit to Delphi application choose File-New ... Unit. The structure of a code unit is similar to form unit, it only lacks the type declaration for the "linked" TForm class. Looking in the past: Code Explorer In the second chapter of this course you have learned the main parts of the Delphi IDE. When we were discussing the Code Editor window, one part of that window was left unexplained - the Code Explorer. By default, the Code Explorer is docked to the left of the Code editor. The Code Explorer makes it easy to navigate through your unit source. The Code Explorer contains a tree diagram that shows all the types, classes, properties, methods, global variables, and global routines defined in your unit. It also shows the other units listed in the uses clause. I use the Code explorer to quickly jump to a procedure or type declaration by simply double-clicking on an item name, like Button1Click. Chapter 6 Delphi Language: tutorials Delphi language, a set of object-oriented extensions to standard Pascal, is the language of Delphi. Delphi Pascal is a high-level, compiled, strongly typed language that supports structured and object-oriented design. Its benefits include easy-to-read code, quick compilation, and the use of multiple unit files for modular programming. Here's a list of tutorials, an introduction to Delphi Pascal, that will help you learn Delphi Pascal. Each tutorial will help you to understand a particular feature of Delphi Pascal language, with practical and easy to understand code snippets. Variable Scope Object Pascal Variable Scope: now you see me, now you don't. Typed constants How to implement persistent values between function calls. Loops Repeating operations in Object Pascal in Object Pascal in Object Pascal in
Page | 19

Object Pascal. Decisions Making decisions in Object Pascal or NOT. Functions and Procedures Creating user defined subroutines in Object Pascal. Routines in Delphi: Beyond the Basics Extending Object Pascal functions and procedures with default parameters and method overloading. Statements/Properties/Variables The basic layout of a Pascal/Delphi program. String Types in Delphi Understanding and managing string data types in Delphi's Object Pascal. Learn about differences between Short, Long, Wide and null-terminated strings. Ordinal and Enumerated Data Types Extend Delphi's built-in types by constructing your own types. Arrays in Object Pascal Understanding and using array data types in Delphi. Records in Delphi Learn about records, Delphi's Pascal data structure that can mix any of Delphi's built in types including any types you have created. Variant Records in Delphi Why and when to use variant records, plus creating an array of records. Pointers in Delphi An introduction to pointer data type in Delphi. What are pointers, why, when and how to use them. Recursions in Delphi Writing and using recursive functions in Object Pascal. Some exercises for you... Since this Course is an online course, there is much you can do to prepare for the next chapter. At the end of each chapter I'll try to provide several tasks for you to get more familiar with Delphi and the topics we discuss in the current chapter. Chapter 7 Sophisticated Delphi Pascal techniques for Beginners Sophisticated Delphi Pascal techniques for Beginners. Time to extend your Delphi Pascal knowledge to the max. Here are some intermediate Delphi problems and articles for everyday development tasks. Welcome to the seventh chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. Time to extend your Delphi Pascal knowledge to the max. Here are some intermediate Delphi problems and articles for everyday development
Page | 20

tasks. Delphi Pascal: a step forward Here's a list of tutorials, and articles, that will help you learn more of Delphi Pascal. Each tutorial will help you to better understand a particular feature of Delphi Pascal language, with practical and easy to understand workigng code examples. Some of the articles use Windows API calls, if you are unfamiliar with the term, do not run away, just follow the article code and you'll understand it completely. Base Conversions, and more Routines for converting Int to Bin, Int to Hex, Int to Roman and vice versa Disk in drive A: Some useful routines when playing with floppy disk and Delphi Start from Delphi Executing and running applications and files from Delphi. Auto completing Dates Date manipulation: improve your code, let them type faster NO GUI Delphi applications Creating a console mode application with Delphi; a text-mode program that runs without a graphical interface. Even more: see how to capture the output of a console application in a GUI Delphi program. Text Files in Delphi Handling ASCII files from Object Pascal code. Errors and Exceptions How to handle errors and exceptions in Delphi. Various tutorials and articles on using Delphi Some exercises for you... Since this Course is an online course, there is much you can do to prepare for the next chapter. At the end of each chapter I'll try to provide several tasks for you to get more familiar with Delphi and the topics we discuss in the current chapter. Here are some suggested actions for you, after you finish reading this chapter: Chapter 8 Commenting Delphi code Learn the art of helping yourself, when it comes to code maintenance. The purpose of adding comments to Delphi code is to provide more program readability using understandable description of what your code is doing.

Welcome to the eighth chapter of the FREE online programming course:


Page | 21

A Beginners Guide to Delphi Programming. Learn the art of helping yourself, when it comes to code maintenance. The purpose of adding comments to Delphi code is to provide more program readability using understandable description of what your code is doing Why, How and When Even though at *this* stage of your latest Delphi project development you are pretty sure about what every line in the source does, you should know that after a few months even the *simplest* functions will be hard to read - you'll simply forget what the idea was.

Commenting code is a manner of adding (displaying) textual information in relation to your code without it being parsed by compiler (or compiled in an exe). The purpose of adding comments to code is to provide an understandable description of what your code is doing - to make your source more readable - for you and for other developers that may need to alter (or understand) the code. When you come back to maintain the code a year later, you will thank yourself. Other users who attempt to maintain your code will thank you as well. There are two kinds of comments in Delphi: block comments and line comments. Line comments (the // symbol - double-slash) were added within Delphi source, as the name suggests, only comments out the current line. They are put at the start of the bit of code you want to comment out and stop the compiler *seeing* any of the code after the comment symbol and until the end of the line. In the next example the j:=5 part of the code is commented.

var i,j :integer ... i:=0; //j:=5 ...

There are two ways to add block comments. The { } symbols and (* *) are used (in pair) to comment out a block of code (i.e. more than one line). That is, any text between a left brace and a right brace constitutes a comment; text between a left-parenthesis-plus-asterisk and an asterisk-plus-right-parenthesis also constitutes a comment. For example (from Windows.pas):

Page | 22

{The name immediately following SUBLANG_ dictates which primary language ID that sublanguage ID can be combined with to form a valid language ID.}

SUBLANG_NEUTRAL = $00; { language neutral } {$EXTERNALSYM SUBLANG_NEUTRAL} SUBLANG_DEFAULT = $01; { user default } ... Notice, however, that a text that contains a dollar sign ($) immediately after the opening { or (* (for block comments) is a compiler directive - something definitively not ignored by the compiler. For example, {$WARNINGS OFF} tells the compiler not to generate warning messages. Note one more thing, if you start a comment with an open brace "{", Delphi expects the comment to end with a closing brace "}", and not an asterisk-paren "*)". You must be quite careful when using block comments as it often seems the case that you could want to comment out a chunk of code that already has comments in it. However, the problem with this is that we can't nest comments (i.e. have one set of comments inside another) in Delphi Pascal. But, if you have a large amount of code that you want to comment out, none of the above operators may help, depending on whether you have already used those operators in the code. Here's a definitive way to comment out code:

{$IFDEF False}

your code goes here do not fear if you have used

Page | 23

line or block comment inside this block!

{$ENDIF}

As a help to developer, Delphi code editor (by default) uses italics and a blue font to specify that a peace of code is actually a comment Delphi Commenting TIPS Delphi Pascal doesn't support nesting comments of the same type within each other. Each unit should begin with a comment block, that generally describes the contents of the unit. Each class definition and each method (procedure/function) within a unit should have a separate function comment, above the method. In certain circumstances, you will use comments to document the arguments passed to a procedure, to state whether those arguments should be within a certain range of values, to convey whether global variables are changed within the procedure, and to relate the procedure's return values. Avoid making obvious comments: i := i + 1; // Add one to i Misleading comments are worse than no comments at all. For commenting, usually { } pairs shall be used. The alternative notation of (* *) shall be reserved for temporarily removing code ("commenting out") during development. The use of // shall be restricted to one-line comments. Tricky, confusing, and/or clever coding techniques should always be documented (if not avoided entirely). Comments preceding functional blocks of code should be aligned with the code on the left hand side When documenting source code, don't forget to update existing comments if the code change causes the description to be inaccurate. Always make sure the description still describes the new functionality. If your comments speak to how the code works, instead of to what it does, you have created an additional code-maintenance problem, because comments that describe how code works must be revised whenever you change the code. Always enter or revise your comments as soon as you write the code. Avoid "saving it for later," because there is rarely time to do it later, or, if there is, you will not understand the code as well when you come back to it. Chapter 9 Yes, even the experienced Delphi programmers make mistakes It happens to me all the time, never mind my 10+ years of Delphi
Page | 24

programming experience. Compiler errors like "Missing operator or semicolon" or ":= expected but = found", after pressing the F9 key (run command in the Delphi IDE), constantly pop up! Suppose you are writing a recursive procedure to calculate the factorial value of an integer. The procedure should also output each iteration value to the screen in a console mode Delphi application. Below is a solution to the problem - to calculate 5! (=120). The output should look like: 1 2 6 24 120 Here's the procedure: [line [line [line [line [line [line [line [line [line [line 0] 1] 2] 3] 4] 5] 6] 7] 8] 9] procedura ErrorTesting; var l, k : integer; j : string; begin j := 1 l := 5; for k:= 1 to l do j = k * j; writeln(j) ; end;

How many possible compile (or logic) errors (not warnings) can you count? How many? I'm counting four. In less than 7 lines of code there are 4 errors - and on the first look the code looks ok! Line Line Line Line 0: 4: 5: 7: Error! Error! Error! Error! Declaration expected but identifier 'procedura' found. Incompatible types: 'String' and 'Integer'. Missing operator or semicolon. := expected but = found

There is even one logic error! If you correct all 4 errors in the source, the program will only output one number: 120. Errors and How to Prevent Errors
Page | 25

There are four types of errors a Delphi programmer can experience while developing, even the simplest program. Design and compile time errors are seen only by a developer, while run time and logic errors tend to crash your program right in front of the user's eyes! The good thing about design and compile time errors is that Delphi will not let you build (compile) the project - therefore stopping you deploying a non working version of the application. The bad thing about the run time and logic errors is that Delphi will build the exe for you - but that exe might or might not work properly. Read on to lean how to prevent and fix common coding errors... This is the second page of the two page article! Design time and compile time errors Fighting against these two is fairly easy. An example. Setting the TabOrder property of any control on a form to more than 32767 will result in a design time error. A message box will pop up telling you what the boundaries are for the property values. On the other hand, compiler errors will prevent you from building the exe for the application. When you press the F9 key and there are some errors in the code, Delphi will stop the compile process and display the errors inside the Messages window. If you know how to correct the particular error, you simply go to the line of code the Delphi compiler reported and fix it. However, if you are unsure about some of the errors (that is how to fix them), you should select that error inside the Messages windows and press F1 (Help). Many times, the error description will provide enough information for you to be able to correct your code. In the case of ':= expected but = found', what we wanted to do is to assign the value of the right side (k * j) of the = sign to the variable (j) on the left. By mistake, we tried to do a logical operation '=' on the expression. Simply, changing '=' to ':=' solves the problem. How about 'Missing operator or semicolon'? Delphi warns that the error is in line 5. I'm pretty sure that in [line 5] i := 5;
Page | 26

there is no missing operator! Ah, yes. We have a missing ';' at the end of line 4. But why did Delphi stated that the error is in line 5? Remember that, in Pascal language, each line of code must end with the ';' sign. In the code above, Delphi compiler actually sees j := 1 i := 5; (two lines) as (one line) j := 1 i := 5; Therefore, a semicolon is often missing on the previous line! Run-time and Logic errors Fighting against these two is, unfortunately, a fairly hard task. Firstly, you'll not know about those errors until the users of your application report them. Secondly, you'll be truly happy if those users succeed in explaining why your program stopped working - in order for you to be able to recreate the situation and correct the code. The "Preventing Access Violations in Delphi code" article provides a more indepth discussion on run time errors. Logic Errors Oh, those nasty logic errors. They can go unnoticed indefinitely, or more scarily, they can go unnoticed indefinitely and really mess with the users data (for example, in an accounting application). There is a logic error in the code above - the output will only contain one line, where we need 5 lines, for each iteration of the for loop. The problem is in the for .. do Delphi statement. The for loop is defined as follows: for counter := initialValue to finalValue do statement The most tricky part is the "statement". Statement can be a simple or structured statement that does not change the value of counter. Structured statements are built from other statements. If the structured statement consists only of simple statements (that is more than one) it MUST be surrounded with the begin / end pair. You, as a beginner developer, would most likely run into problems with
Page | 27

using the for loop with a structured statement as if it were a simple statement. Therefore our for loop should look like this: for k:= 1 to l do begin j = k * j; writeln(j) ; end; Note: ALWAYS, always write Delphi commands that expect a simple OR a structured statement like they expect a structured statement!! In other words, if you have a for loop with only one simple statement inside its body, DO surround that line with the begin / end pair. Help yourself Spend a few bucks and buy a book! "The Tomes of Delphi Developer's Guide to Delphi Troubleshooting", by Clay Shannon, covers all versions of Delphi. This book is an alphabetical reference to nearly 800 design-time, compile-time, and run-time error messages of Borlands Delphi Object Pascal language. Error message entries include an explanation of what actions or omissions may have caused the error message, how to quickly resolve the problem, and how to avoid receiving the error message in the future. Code samples clearly demonstrate how to eliminate the errors in different versions of Delph Chapter 10 Your First Delphi Game: Tic Tac Toe Page 1: Preparing the Game GUI

Welcome to the tenth chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. Designing and developing a real game using Delphi: Tic Tac Toe. Ok, stop playing with Delphi. After nine chapters of this course you are ready for some real action. It's time to create a full working Delphi project, and what type of project would you be more interested than creating a game?
Page | 28

Your goal in this chapter is to create a simple game using Delphi. Here are the topics you'll practically try and learn here: Building a program GUI Building a program Logic Working with arrays Building Functions, Procedures using Parameter Passing Casting one Delphi object to another Tic Tac Toe, the Rules Tic Tac Toe is a traditional game, played by two opponents that alternately place X's and O's into 3x3 playing field.

Before the game starts, players have to decide who will go first and who will mark his moves with an X. After the first move, game proceeds with the opponents alternately placing their marks at any unoccupied cell. The goal of this game is to be the first one with three marks in a row, where the row can be horizontal, diagonal or vertical. If all cells are occupied and both players don't have a winning combination, the game is a draw. Back to the "drawing board" ... Preparing the Tic Tac Toe Game At this moment you should have your copy of Delphi up and running. By default, as you know by now, when you start Delphi, a new (standard Windows) project is created hosting one Form object (and the associated unit). Our game will require only one form where all the action is taking place, naturally we'll use the form object Delphi "prepared" for us. Saving for the first time Before we move on, I suggest you to rename that default form, by assigning the string 'frMain' to the Name property (using the Object Inspector). Next, save the project, by pointing to File | Save All (Delphi IDE main menu). The Save As dialog box will be displayed, providing you with the option to first save the form's unit file, the default name will be Unit1.pas. The offered location will be inside the Projects folder under the Delphi install folder. It is advisable to save every project in a separate folder, therefore, use the 'Create New Folder' toolbar button to create a new folder, and name it 'TicTacToe'. Now, change the name of the Unit1.pas to Main.pas and click Save. You will then be prompted to save the project, by default Delphi will name this project 'Project1' (DPR), of course we'll
Page | 29

change it to, let's say, 'TicTacToe'. That's it. Later on, when you want to save the current status of your project, just use Ctrl+Shift+S (keyboard shortcut for Save All). GUI Ok, first we build the game GUI (graphical user interface). For the moment we only have a form object named 'frMain', use Object Inspector to change the Caption property to 'About Delphi Programming: Tic Tac Toe' The playfield. Your goal is to create a "grid" consisting of 9 cells, where each cell will display either nothing, X or O. The simplest is to use the Label component from the Standard palette. Drop one on the form. Note, we'll need 9 labels looking similar. The easiest way to do that is to create first label, name it, choose the appropriate font, color, and set every other property. For this purpose we will name the label 'lblCell0'. Use the Object Inspector to set the following properties: object lblCell0: TLabel // note Name = lblCell0 Left = 8 Top = 8 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell0' Color = clCream Font.Size = 16 Font.Name = 'Tahoma' Font.Style = [fsBold] Layout = tlCenter end

Note: If you right click anywhere on your form, and choose 'View As Text' command from the context pop up menu, you are presented with text description of the forms attributes,
Page | 30

inside the Code Editor window. To switch back to 'For View', just right click this "code" window and choose 'View As Form'. After we shape up the first (cell) label, we can create other eight elements of the grid. Select the label object, and press CTRL+C. Now, select the form (click anywhere on the form) and press CTRL+V to create another label object. The second object will inherit all properties from the first one, only the Name property will be re-set to Label1. Alter it manually to lblCell1, note that you must also manually change the Caption property to lblCell1. We do not really need the Caption property at this moment, but if you delete it (making it an empty string) you will not be able to differentiate one cell from another (without selecting one, of course). Go on and create all nine labels. Make sure that the Name properties are sequential in the grid, we will build the game logic on the cell position / name. Label object with Name lblCell0 zero should be in the top left corner, and the bottom right label should be lblCell8. When you are finished your form should be looking something like:

Next, we add some more objects to the form: Two TButton objects (Standard Palette), name them 'btnNewGame' and 'btnResetScore', One TRadioGroup object (Standard Palette), name it 'rgPlayFirst', set the Caption property to 'First to play'. Now, select this RadioGroup and point to the Items property, click the ellipsis button. Add two radio buttons, each string in Items makes a radio button appear in the radio group box with the string as its caption. The value of the ItemIndex property determines which radio button is currently selected. One TGroupBox object (Name = 'gbScoreBoard'; Caption = 'Score Board') with six labels (names : lblX, lblXScore, lblO, lblOScore, lblMinus,
Page | 31

lblColon).

Once we have the game GUI, we can proceed to game logic and initialization. Your First Delphi Game: Tic Tac Toe Page 2: Game Initialization

The GUI design part of the Tic Tac Toe game is complete, we move on to game logic and initialization... Initializing the Tic Tac Toe Game Before we start coding we have to decide how to represent the playfield (label/cell grid) in the computer memory. Each cell can be treated individually, we can place all cells in one-dimensional array, or we can make two-dimensional array. Since we want to automate our program as much as possible we will represent cells as a two dimensional array. The top-left "corner" will have an index of [1, 1], the top-right corner will be [1, 3], the bottom-right [3, 3], etc. Two playfield variables will be created for playfield data, as the code will be more readable if we store each player moves separately. Beside playfield variables we need to declare variables that will tell us how many moves are played (iMove), who is on the move (sPlaySign), is the game in progress or is the game over (bGameOver), and two variables that will hold the number of victories for each player (iXScore and iOScore). Place the following code above the implementation keyword inside the unit Main.pas, just below the frMain variable:

Page | 32

... var frMain: TfrMain; //added by Delphi - do NOT delete

iXPos : TXOPosArray; iOPos : TXOPosArray;

sPlaySign : String; bGameOver : Boolean; iMove : Integer; iXScore : Integer; iOScore : Integer; implementation ... Note: the TXOPosArray (two-dimensional integer array - holding 9 elements) type is declared above the form type declaration. Since we'll need to send an array as a parameter to a procedure, we have to define our "array type", like: type TXOPosArray = array [1..3, 1..3] of Integer; Now, inside the FormCreate event handler (double click a form to create one) we add the following code: procedure TfrMain.FormCreate(Sender: TObject); begin

Page | 33

iXScore := 0; iOScore := 0;

InitPlayGround; end;

The iXScore and iOScore variables are initialized when the form is created. This is because we don't want to change their values until user presses the btnResetScore button. All other variables have to be initialized after the end of each game - inside the InitPlayGround procedure. Playfield Initialization During a playfield initialization we will clear the cells (that is, Captions of all Labels with lblCellX names) by setting the Caption property to '' (empty string). It is necessary to set iXPos and iOPos arrays to zero. Beside that, we have to check the value of the rgPlayFirst radio group ItemIndex property to determine which player will play first in the next game. Also, we have to reset the number of moves to zero (iMove), and set the bGameOver variable to False. To create the InitPlayGround procedure, go to the Code Editor window and below the implementation keyword, add the next code:

procedure TfrMain.InitPlayGround; var i, j, k: integer; begin for i := 1 to 3 do begin for j := 1 To 3 do begin

Page | 34

k:= (i - 1) * 3 + j - 1; // 0 .. 8 TLabel(FindComponent('lblCell' + IntToStr(k))).Caption := ''; iXPos[i, j] := 0; iOPos[i][j] := 0; end; end;

if rgPlayFirst.ItemIndex = 0 then sPlaySign := 'X'; if rgPlayFirst.ItemIndex = 1 then sPlaySign := 'O';

bGameOver := False; iMove := 0; end;

Note that you will also need to add this procedure's header in the private section of the form declaration (interface part of the unit):

... private procedure InitPlayGround; ...

Stop for the moment, and consider the next two lines in the above code: k:= (i - 1) * 3 + j - 1; // 0 .. 8 TLabel(FindComponent('lblCell' + IntToStr(k))).Caption := '';
Page | 35

What we want to do in the InitPlayGround procedure is to set the caption to all lblCellX label components to an empty string. One way to achieve this is to use 9 lines of code:

lblCell0.Caption:=''; lblCell1.Caption:=''; lblCell2.Caption:=''; ... lblCell7.Caption:=''; lblCell8.Caption:='';

Bad practice! The above code "tip", searches for a component on a form by its name (FindComponent), casts it to TLabel and assigns an empty string to the Caption Property. FindComponent returns the component in the forms Components property array with the name that matches the string in the only parameter required. Your First Delphi Game: Tic Tac Toe Page 3: Player X, your turn!

Until now we have created the Tic Tac Toe game GUI and we've initialized the variables. We are ready to make our first move. X, your turn! (or O if you prefer) Event OnClick for the particular lblCellX component will be called each time a player presses a (left) mouse button on the Label component making the playfield grid. There is one more thing we have to check before we process the user input. It is very important to be sure that the current game is in progress. In the case that the game is over, we will simply exit this procedure. After we verify that the game is in progress, we can process the user input - in a procedure GamePlay that expect an integer parameter named CellIndex. CellIndex would have the value
Page | 36

set to 0 for the top-left corner cell (label) of the playfield and the value 8 for the bottom-right corner. To get the CellIndex we extract the cell number from the label name, for example, if the label clicked is named lblCell5, the CellIndex for that label (cell) is 5. To create an OnClick event handler procedure for the first label (lblCell0), simply double click it, or select the lblCell0 component, go to Object Inspector, switch to Events and double click the column right to OnClick. Either way, the Code Editor receives the input focus, and you can enter the programming logic code for the OnClick handler:

procedure TfrMain.lblCell0Click(Sender: TObject); var iWin : integer; CellIndex : 0..8; begin if bGameOver = True Then Exit; if TLabel(Sender).Caption <> '' then begin ShowMessage('Cell occupied!'); Exit; end; CellIndex := StrToInt(RightStr(TLabel(Sender).Name,1)); iWin := GamePlay(CellIndex); end; Note 0: If the cell is occupied (was clicked before during the game), we exit the procedure. In order to "see" whether the cell is occupied we use the Sender parameter, cast it to TLabel and test the Caption property. If the cell already hosts 'X' or 'O', we display a message using the ShowMessage Delphi function. Note 1 : to assign a value to CellIndex we use the RightStr function defined in the StrUtils.pas unit. You'll need to manually add this unit to the uses list in the interface uses
Page | 37

clause of the Main.pas unit. If you forget to do that, the next time you try to compile your project, the compiler will report an error: "Undeclared Identifier 'RightStr'"!! Note 2: The above event handler was coded with the following in mind: there is no need to have a separate event handler for each and every of 9 cells making the playfield. What we are preparing for, with the above code, is to use this one procedure for all lblCellX label components and their OnClick events. To have the lblCellX (where X stands for 0 to 8) share this specific OnClick event handler, do the following: a) select the "rest" of the eight lblCell label components (Shift + Click 'em); b) Go the Events page of the Object Inspector c) In the right column to the OnCLick, from the drop down list pick the lblCell0Click. If you experience difficulties, check the How To Share an Event Handler in Delphi. Processing the Move Before we start with processing, let me refresh your memory. iXPos and iOPos arrays hold information about already played moves by the X and O player. iMove tells us how many moves are already made. The user's choice must be the regular move, which means that user didn't click on the already occupied cell. If the user makes a regular move, we have to increase the counter (iMove) by one. Then we have to translate the coordinates form one-dimensional to two-dimensional array. The xo_Move value of 0 will be translated to [1,1], 1 to [1,2] ... 3 to [2,1] ... 8 to [3,3].

function TfrMain.GamePlay(xo_Move : Integer):integer; var x, y : 1..3; iWin : integer; begin Result := -1;

Inc(iMove); x := (xo_Move Div 3) + 1; y := (xo_Move Mod 3) + 1; ...

Page | 38

Also we have to check which player has made the move because this routine will process the input from both players.

if sPlaySign = 'O' then begin iOPos[x,y] := 1; iWin := CheckWin(iOPos); end else begin iXPos[x,y] := 1; iWin := CheckWin(iXPos); end;

TLabel(FindComponent('lblCell' + IntToStr(xo_Move))).Caption := sPlaySign;

For example, when the player X places his mark in the top left corner of the playfield, the variables will have the following values:

Take a look at the values when O player makes his move in the middle of the playfield:

Page | 39

After every move we call the CheckWin function to look for the winning combination. If the player succeeds to win, CheckWin will return the number of the winning combination. If there is no winner, CheckWin will return -1. In case that we have the winner the game will end, the current result will be placed on the scoreboard, and a congratulation message will pop up.

Result := iWin;

if iWin >= 0 then begin bGameOver := True; //mark victory

if sPlaySign = 'X' then begin iXScore := iXScore + 1; lblXScore.Caption := IntToStr(iXScore); end else begin iOScore := iOScore + 1; lblOScore.Caption := IntToStr(iOScore); end;

Page | 40

ShowMessage(sPlaySign + ' - Wins!'); end;

If players played nine moves and there is no winner, it is a draw; and the current game is over. On the other hand, if the game is still in progress, we have to allow the other player to make his move.

if (iMove = 9) AND (bGameOver = False) Then begin ShowMessage('It''s a Draw!'); bGameOver := True end;

if sPlaySign = 'O' Then sPlaySign := 'X' else sPlaySign := 'O'; end; //function GamePlay

Ok, we now have the main part of the code logic done, what's left is the CheckWin function. Do we have a winner? After each move we have to check if the game is over. Game could end in three ways: X player wins the game, O player wins the game, or it is a draw. To look for a possible winner we count the number of X's and O's in each row, column and diagonal. If the player manages to put three signs in row, column or diagonal, we have the winner.
Page | 41

Here's a portion of the CheckWin function, for the rest of the function look at the project code. function TfrMain.CheckWin(iPos : TXOPosArray) : Integer; var iScore : Integer; i : Integer; j : Integer; begin Result := -1;

//in rows? iScore := 0; for i := 1 to 3 do begin iScore := 0; Inc(Result); for j := 1 To 3 do Inc(iScore, iPos[i,j]); if iScore = 3 Then Exit end;//for i ...

Page | 42

Note that you will have to add these procedure's headers in the private section of the form declaration, as you did with the InitPlayGround procedure. Now the private section is looking like:

private procedure InitPlayGround; function GamePlay(xo_Move : Integer) : integer; function CheckWin(iPos : TXOPosArray) : integer;

We now move to the last stage of our Tic Tac Toe development, the creation of the event handlers for 'New Game' and 'Reset Score'... Your First Delphi Game: Tic Tac Toe Page 4: New Game?

There are two more handlers we have to code: create a procedure for a new game, and a procedure that will reset the score. New Game We already have the InitPlayGround procedure, therefore creating a new game would be a peace of cake. All we have to do is call the InitPlayGround. Note: a player can click on the btnNewGame button after the game is finished (bGameOver = True) or during a game (bGameOver = False). Because of that it would be nice to ask the user to confirm his decision. In this particular case there would be negligible damage or there would be no damage at all. Just remember that good programming practice implicate getting the user's confirmation. Double click the btnNewGame button to create and add the code for the New Game option:

procedure TfrMain.btnNewGameClick(Sender:
Page | 43

TObject); begin if bGameOver = False then begin if MessageDlg( 'End the current game?', mtConfirmation, mbOKCancel,0) = mrCancel then Exit; end;

InitPlayGround; end; Note: if you are using a Non English Delphi version and want to have dialog buttons and messages on your local language, explore the article: "The fastest path to Delphi localization". Reseting the Scoreboard Recall that the iXScore and iOScore variables hold the number of wins for each player. Oponents read the score form the lblXScore and lblOScore labels. The task is simple: set iXScore and iOScore values to zero, and update lblXScore.Caption and lblOScore.Caption. Off course, get the user's confirmation first.

procedure TfrMain.btnResetScoreClick(Sender: TObject); begin

Page | 44

if MessageDlg( 'Reset the scores?', mtConfirmation, mbOKCancel,0) = mrCancel then Exit;

iXScore := 0; iOScore := 0; lblXScore.Caption := IntToStr(iXScore); lblOScore.Caption := IntToStr(iOScore); end;

Some exercises for you... Please, take a look at that CheckWin function again. Note that in case of a "we have a winner" it returns an integer number from 0 to 8. What is this number for? See the picture:

Page | 45

If we have a winner and the winner has occupied the middle row, the CheckWin will retun 2. Your excersise (for this chapter) is to create a new function to mark (in any way you want) a victory on a playfied. For example, you could change the background color for the three labels making the wining combination. Or, draw an ellipse arround the cells, it's up to you... ;) Anyway be sure to see the full Tic Tac Toe game code.

{ Article: Your First Delphi Game: Tic Tac Toe

http://delphi.about.com/library/library/weekly/aa021803a.htm A Beginners Guide to Delphi Programming: Chapter 10. Designing and developing a real game using Delphi: Tic Tac Toe. Download the ZIPed source. Download only EXE. }

Main.PAS

unit Main; interface


Page | 46

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, StrUtils;

type TXOPosArray = array [1..3, 1..3] of Integer; type TfrMain = class(TForm) lblCell0: TLabel; lblCell1: TLabel; lblCell2: TLabel; lblCell3: TLabel; lblCell4: TLabel; lblCell5: TLabel; lblCell6: TLabel; lblCell7: TLabel; lblCell8: TLabel; gbScoreBoard: TGroupBox; rgPlayFirst: TRadioGroup; lblX: TLabel; lblMinus: TLabel; Label1: TLabel; lblXScore: TLabel; lblColon: TLabel; lblOScore: TLabel; btnNewGame: TButton; btnResetScore: TButton; procedure FormCreate(Sender: TObject); procedure lblCell0Click(Sender: TObject); procedure btnNewGameClick(Sender: TObject); procedure btnResetScoreClick(Sender: TObject); private procedure InitPlayGround; function GamePlay(xo_Move : Integer) : integer; function CheckWin(iPos : TXOPosArray) : integer; public { Public declarations } end;

var frMain: TfrMain; iXPos : TXOPosArray; iOPos : TXOPosArray; sPlaySign : String; bGameOver : Boolean;
Page | 47

iMove : Integer; iXScore : Integer; iOScore : Integer; implementation {$R *.dfm} procedure TfrMain.InitPlayGround; var i, j, k: integer; begin for i := 1 to 3 do begin for j := 1 To 3 do begin k:= (i - 1) * 3 + j - 1; // 0 .. 8 TLabel(FindComponent('lblCell' + IntToStr(k))).Caption := ''; iXPos[i, j] := 0; iOPos[i][j] := 0; end; end; if rgPlayFirst.ItemIndex = 0 then sPlaySign := 'X'; if rgPlayFirst.ItemIndex = 1 then sPlaySign := 'O'; bGameOver := False; iMove := 0; end; procedure TfrMain.FormCreate(Sender: TObject); begin iXScore := 0; iOScore := 0; InitPlayGround; end; function TfrMain.CheckWin(iPos : TXOPosArray) : Integer; var iScore : Integer; i : Integer; j : Integer; begin Result := -1; //in rows? iScore := 0; for i := 1 to 3 do begin iScore := 0;
Page | 48

Inc(Result); for j := 1 To 3 do Inc(iScore, iPos[i,j]); if iScore = 3 Then Exit end;//for i //top-left bottom-right diagonal? iScore := 0; Inc(Result); for i := 1 to 3 do Inc(iScore, iPos[i,i]); if iScore = 3 then Exit; //top-right bottom-left diagonal? iScore := 0; Inc(Result); for i := 1 to 3 do Inc(iScore, iPos[i,4-i]); if iScore = 3 then Exit; //columns? for i := 1 to 3 do begin iScore := 0; Inc(Result); for j := 1 to 3 do Inc(iScore, iPos[j,i]); if iScore = 3 then Exit; end;//for i Result := -1; end; function TfrMain.GamePlay(xo_Move : Integer):integer; var x, y : 1..3; iWin : integer; begin Result := -1; Inc(iMove); x := (xo_Move Div 3) + 1; y := (xo_Move Mod 3) + 1; if sPlaySign = 'O' then begin iOPos[x,y] := 1; iWin := CheckWin(iOPos); end else begin iXPos[x,y] := 1; iWin := CheckWin(iXPos); end;
Page | 49

TLabel(FindComponent('lblCell' + IntToStr(xo_Move))).Caption := sPlaySign; Result := iWin; if iWin >= 0 then begin bGameOver := True; //mark victory if sPlaySign = 'X' then begin iXScore := iXScore + 1; lblXScore.Caption := IntToStr(iXScore); end else begin iOScore := iOScore + 1; lblOScore.Caption := IntToStr(iOScore); end; ShowMessage(sPlaySign + ' - Wins!'); end; if (iMove = 9) AND (bGameOver = False) Then begin ShowMessage('It''s a Draw!'); bGameOver := True end; if sPlaySign = 'O' Then sPlaySign := 'X' else sPlaySign := 'O'; end; procedure TfrMain.lblCell0Click(Sender: TObject); var iWin : integer; CellIndex : 0..8; begin if bGameOver = True Then Exit; if TLabel(Sender).Caption <> '' then begin ShowMessage('Cell ocupied!'); Exit; end; CellIndex := StrToInt(RightStr(TLabel(Sender).Name,1)); iWin := GamePlay(CellIndex); end;
Page | 50

procedure TfrMain.btnNewGameClick(Sender: TObject); begin if bGameOver = False then begin if MessageDlg('End the current game?', mtConfirmation, mbOKCancel,0) = mrCancel then Exit; end; InitPlayGround; end; procedure TfrMain.btnResetScoreClick(Sender: TObject); begin if MessageDlg( 'Reset the scores?', mtConfirmation, mbOKCancel,0) = mrCancel then Exit; iXScore := 0; iOScore := 0; lblXScore.Caption := IntToStr(iXScore); lblOScore.Caption := IntToStr(iOScore); end; end.

frMain.DFM

Select frMain, Select View As Text, Paste the text into Editor, Select View As Form.

object frMain: TfrMain Left = 292 Top = 237 Width = 400 Height = 299 Caption = 'About Delphi Programming: Tic Tac Toe' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText
Page | 51

Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object lblCell0: TLabel Left = 8 Top = 8 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell0' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell1: TLabel Left = 96 Top = 8 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell1' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell2: TLabel Left = 184 Top = 8 Width = 81 Height = 81
Page | 52

Alignment = taCenter AutoSize = False Caption = 'lblCell2' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell3: TLabel Left = 8 Top = 96 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell3' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell4: TLabel Left = 96 Top = 96 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell4' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter
Page | 53

OnClick = lblCell0Click end object lblCell5: TLabel Left = 184 Top = 96 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell5' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell6: TLabel Left = 8 Top = 184 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell6' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell7: TLabel Left = 96 Top = 184 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell7' Color = clCream Font.Charset = DEFAULT_CHARSET
Page | 54

Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object lblCell8: TLabel Left = 184 Top = 184 Width = 81 Height = 81 Alignment = taCenter AutoSize = False Caption = 'lblCell8' Color = clCream Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter OnClick = lblCell0Click end object gbScoreBoard: TGroupBox Left = 272 Top = 104 Width = 113 Height = 97 Caption = 'Score Board' TabOrder = 0 object lblX: TLabel Left = 8 Top = 16 Width = 33 Height = 41 Alignment = taCenter AutoSize = False Caption = 'X' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False
Page | 55

ParentFont = False Layout = tlCenter end object lblMinus: TLabel Left = 48 Top = 16 Width = 25 Height = 41 Alignment = taCenter AutoSize = False Caption = '-' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter end object Label1: TLabel Left = 72 Top = 16 Width = 33 Height = 41 Alignment = taCenter AutoSize = False Caption = 'O' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter end object lblXScore: TLabel Left = 8 Top = 48 Width = 33 Height = 41 Alignment = taCenter AutoSize = False Caption = '0' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText
Page | 56

Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter end object lblColon: TLabel Left = 48 Top = 48 Width = 25 Height = 41 Alignment = taCenter AutoSize = False Caption = ':' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter end object lblOScore: TLabel Left = 72 Top = 48 Width = 33 Height = 41 Alignment = taCenter AutoSize = False Caption = '0' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -21 Font.Name = 'Tahoma' Font.Style = [fsBold] ParentColor = False ParentFont = False Layout = tlCenter end end object rgPlayFirst: TRadioGroup Left = 272 Top = 8 Width = 113 Height = 89 Caption = 'First to play'
Page | 57

ItemIndex = 0 Items.Strings = ( 'X - Player 1' 'O - Player 2') TabOrder = 1 end object btnNewGame: TButton Left = 272 Top = 240 Width = 113 Height = 25 Caption = 'New Game' TabOrder = 2 OnClick = btnNewGameClick end object btnResetScore: TButton Left = 272 Top = 208 Width = 113 Height = 25 Caption = 'Reset Score' TabOrder = 3 OnClick = btnResetScoreClick end end

{ ******************************************** Zarko Gajic About.com Guide to Delphi Programming http://delphi.about.com email: delphi@aboutguide.com free newsletter: http://delphi.about.com/library/blnewsletter.htm forum: http://forums.about.com/ab-delphi/start/ ******************************************** }

Chapter 11 Your First MDI Delphi Project Learn how to create a powerful "multiple document interface" application using
Page | 58

Delphi.

Welcome to the eleventh chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. Learn how to create a "multiple document interface" application using Delphi. It's time for your second Delphi project! This time we'll explore building MDI applications using Delphi. After you have createdyour first Delphi game, you could start developing your own set of Delphi games... Then, why not create one "big" Delphi application to host all your games! Multiple Document Interface Applications In simple words, in an MDI application, more than one document or child window can be opened within a single parent window. This is a common situation in applications such as spreadsheets or word processors - one window, usually called the MDI parent or MDI container, contains many other windows, usually called child forms. In our scenario, MDI parent form would be the host to all your games developed as single form (window) MDI child forms. To start working with MDI interfaces using Delphi, first read the "MDI Development in Delphi" article. Inside the article you'll first find the definition of an MDI application. The article then describes constructing multiple document interface application with Delphi. It also provides a discussion on MDI parent/child relationship, menus and some most important MDI parent form properties. When you a ready, you can proceed to "Creating a multiple document interface graphic file viewer". This article extends the topics described in the first part - letting you expand your MDI application with some real code to open and display most commonly used graphic files (pictures). Extending your MDI development knowledge Ok. By now (I suppose you have read the above two articles!) you should be familiar with the key topics involved in developing MDI applications using Delphi. Another task, you might be interested in, is how to store an MDI child in a DLL. Now, I'll provide two more code samples you could find useful in an MDI application. Create New or Show Already Created Child Form? Assume, for the minute, that you have an MDI application with more than one type of child forms. What I mean, is that you can have your Delphi MDI parent form host one child form designed to be able to display graphic files (TGraphicChildForm) and another form (TMemoChildForm) designed to display some text inside, for example, a Memo component. Now, think of a situation when you have more than a dozen of various types of MDI child forms. What you might want to have in your application, is the ability to decide whether a new instance of the specified child form is to be created or the previous instance should be

Page | 59

brought to front - if one exists. The following code snippet solves the above "problem":

procedure TMainForm.CreateMDIChild(frm : TFormClass); const sMDIChildFormCreated = 'Form "%s" already created. Select OK to switch to it, Yes to create a new form instance.'; var i:integer; bCreated:boolean; f:TForm; begin bCreated:=False; for i:= 0 to MainForm.MDIChildCount-1 do begin if MainForm.MDIChildren[i] is frm then begin f:=MainForm.MDIChildren[i] as frm ; bCreated:=True; Break; end;

Page | 60

end;

if not bCreated then begin frm.Create(Application); end else begin case MessageDlg(Format(sMDIChildFormCreated, [f.caption]), mtConfirmation, [mbOk, mbYes, mbCancel],0) of mrOK: f.Show; mrYes: frm.Create(Application); mrCancel: //do notingg end; //case end; end;

To call the above procedure you can use the following code:

CreateMDIChild(TGraphicChildForm); //or

Page | 61

CreateMDIChild(TMemoChildForm);

Let's see what the above code does... First, you send to the CreateMDIChild procedure the name of the child form class as the parameter. Then, in the CreateMDIChild procedure, the code looks for an already created MDI child form of the provided class, if the one is not found, one gets created. If, on the other hand, the instance (at least one) of the child form is already created, the message dialog asks the user whether to create a new instance or to bring to front that previously created form. Again, more or less a not_for_beginner type of code, but you should be able to understand the way it works. Hide MDI child form when minimized As you know by now, when you try to close an MDI child form, the form only gets minimized. You are however, able to set the Action parameter of the OnClose event method to caFree to really close it; or even set to caNone - to "mark" that the form cannot be closed.

procedure TMDIChild.FormClose (Sender: TObject; var Action: TCloseAction); begin Action:=caNone; end;

However, as stated, the default behavior for MDI child form OnClose event is to become minimized. Now, if you want to hide the form when it gets minimized you can use the next trick:

procedure TMDIChild.WMSize(var M:TWMSIZE); begin if M.SizeType=Size_Minimized then ShowWindow(Handle,Sw_Hide);

Page | 62

end;

Note 1: You'll need to add the procedure header to the private part of the form type declaration:

private procedure WMSize(var M : TWMSIZE); Message WM_Size; Note 2: Even though this procedure relies on Windows Messages and handling them, don't be afraid to use it. The truth is that you are *beginner* developer, but you are trying to become an expert ;) Some exercises for you... As in all the chapters of this course, I'll provide an idea for you to try to use the gathered knowledge in a real situation. This is what you could do: build an MDI application and make your Delphi TicTacToe game from the previous chapter to be the child form of the created MDI project. Chapter 12 Win a copy of Mastering Delphi 7 Page 1: The Delphi Programming TicTacToe Contest Welcome to the twelfth chapter of the FREE online programming course: A Beginner's Guide to Delphi Programming. Delphi Programming Tic Tac Toe Contest - develop your own version of the TicTacToe game and win one copy of the great Mastering Delphi 7 book. A chance to get awarded for your knowledge! It's time to show the World what you can do with Delphi! Two chapters "ago", I've showed you how to design and develop a simple game using your Delphi skills - "Your First Delphi Game: Tic Tac Toe". Now, we are happy to organize an online Delphi Programming contest - and give away 10 copies of the great Mastering Delphi 7 book. Your task is simple: build a computer vs. human version of the TicTacToe Delphi game. Here's how you enter the contest: 1. Read the Official Contest Rules. 2. Write your own version of the TicTacToe game using Delphi
Page | 63

3. Submit your Delphi source code Sybex has been kind enough to provide 10 copies of the "Mastering Delphi 7" - 10 Delphi developers will receive one copy of this great book! 10 copies, worth over 600 $ What to do to win? Use your Delphi knowledge to write your own computer vs. human version of the Tic Tac Toe Delphi game. You are free to use any Delphi version you have - but NO third party components are allowed! You can even create this game as an MDI Delphi application. Of course, use your own imagination to add as many nitty features to the game as you can think of. Chapter 13 Code Completion in Delphi It's time to learn how to let Delphi help you code faster: start using code templates, code insight, code completion, shortcut keys, ... How to code faster and more efficiently So, you've managed to develop several (great) applications using Delphi, but whenever you need to add a try/finally/end block you write the entire "try finally end" command? Do you know that you can do the same by typing "tryf" and than hit Ctrl + J? And how about writing the MessageDlg function? Do you know all the parameters (and their order) this method expects? I do not know, neither do I tend to remember each functions parameter list. I simply write "MessageDlg" and hit Ctrl + Space + Shift: the list of all the parameters gets shown in a tool tip. The answer is: Delphi Code Insight! Delphi, from version 3, provides a very useful functionality called code insight - a set of tools available while you are working in the Code editor. The Code Insight comprises a number of Delphi IDE Code editor features: Code Completion This handy feature automatically suggests methods, properties and events to insert in your code depending on what you are doing. This works for your own classes as well as standard methods etc. What this means, is that when you start a code line by typing the name of an object (Edit1, for example) followed by a period (dot), Delphi will display a list box with all the properties of the edit box you can pick. In general, it lists valid elements that you can select from and add to your code - you can then select the item and press Enter to add it to your code.

Page | 64

Note: you can always invoke Code completion using Ctrl + Space. Another example includes the following situation: If you have defined several string variables and several integer variables in your unit, when you start a command like "Form1.Caption:=" and hit Ctrl + Space, a list of arguments that are valid for the assignment is displayed. The list will contain (among others) string variables which you have defined, but not the integer ones. When the code completion list is displayed, you can hold down the Ctrl key and click on any identifier in the list to browse to its declaration (if you have the sources of Borland Delphi units, you can see "inside" Delphi). Also, in the Code editor, if you hover the mouse pointer over the identifier, a hint window tells where it is declared. You can press Ctrl, point to the identifier in the code and click to move to its declaration. "Unable to invoke Code Completion due to errors in source code"? Uops, the code completion does not work! Now what? The message 'Unable to invoke Code Completion' usually indicates some type of syntax error in your code. It can also indicate that you have not defined the method you are working in. Almost always the problem does exist but you might try recompiling the entire project to see if syntax errors do exist. Code Templates As suggested above, this feature can save you lots of keystrokes. Templates include commonly used programming statements (such as if, while, for and tyr/finally/end statements) - each statement has a shortcut, eg. "forb" is a for loop with begin end. You simply start a statement by typing several characters and press Ctrl+J and pick from a list of predefined code templates.

Page | 65

Another great example includes pressing Ctrl+J on a blank line, typing f, and double-clicking the selected function declaration - the function skeleton code will be inserted automatically at the current cursor position. Code Parameters This tool enables you to see the arguments of a method call as you enter them into your code. After typing a method and a '(', a description of the parameters will be displayed, as each parameter is input the next parameter to be input is highlighted in bold. Use Ctrl+Shift+Space to activate or reactivate if the tooltip disappears.

Tooltip symbol insight, Tooltip expression evaluation Great debugging tool. To test expression evaluation while the program is running "under" IDE, you stop the compiler (enter debug), and you can view the value of a variable by pointing to it with your cursor. Symbol insight feature displays declaration information for any identifier by passing the mouse over it in the Code editor. Class Completion This is a "subset" of the code completion feature, designed to automate the definition of (new) classes by generating skeleton code for the class
Page | 66

members. This is how it works: You write a new property declaration inside a public section of the interface section of your class definition (Form1).

... public property MyProp : integer read fProp write SetProp; CTRL + SHIFT + C ...

When complete hit Ctrl + Shift + C, Delphi automatically adds private read and write specifiers to the declarations, then adds skeleton code in the implementation section. This is what gets added by Delphi:

... private fProp: integer; procedure SetProp(const Value: integer) ... //implementation ... procedure TForm1.SetProp(const Value: integer); begin fProp := Value; end; Code Insight Settings Options for turning Code Insight tools on and off are located on the Code
Page | 67

Insight page of the Tools | Editor Options dialog.

Note that all the Code Insight tools have lot more usage ways. The list can be found on Delphi Help under "Code Insight" when you click Help in the above dialog. Some exercises for you... Hmm, simply start using Code insight features, you will not believe how much time you can save in coding, and of course you can drastically reduce the number of bugs in your code. Chapter 14 Making Forms Work - a Primer Page 1: The place where forms are created...

In just about every Delphi application, we use forms to present and retrieve information from users. Delphi arms us with a rich array of visual tools for creating forms and determining their properties and behaviour. We can set them up at design time using the property editors and we can write code to re-set them dynamically at runtime. Those of us who have used other visual programming languages such as Microsoft Visual Basic appreciate Delphi's many features for working with forms. However, even with all the creature comforts built into Delphi,
Page | 68

there are many things we might want to do which cannot be solved by simply dropping a component onto a form and setting its properties. Over the next several months I will show you some of the simple and more complex tricks for making forms work more efficiently. Starting Simply To get things rolling, let's start off at the simple end with SDI (Single Document Interface) forms. An SDI application normally contains a single document view - as opposite to MDI (multiple document interface). By defualt, every time you create a new (Windows) application in Delphi, you start working in SDI application mode. Starting a new Delphi project generates and displays the first form in the project file. To view the project file code, from the IDE menu go to Project | View Source. The line we are interested in is painted red:

program Project1;

uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.

The method CreateForm prepares the form to be displayed as the program's main form. The form is not displayed until the next line
Page | 69

executes:

Application.Run;

Executing CreateForm utilizes memory and system resources. This is not a problem with an application with a single form, but if the project has many forms and they are all created at program startup you may run into low resource problems down the road. Your development computer probably has plenty of memory installed but the situation may be quite different on users' machines, where your application has to run with much more limited resources and in competition with other applications. The solution is simple: take control of form creation and destruction. Taking Control Let's look at the basics for controlling forms in a new project. Imagine you have an application that has a main form (frmMain) with two subsidiary forms to add a new customer (frmAddNewCustomer - Unit2) and edit customers (frmEditCustomer - Unit3). To add two new forms in the project, simply point to File | New | Form. This will create (and add) a new form named Form2 (+ Unit2) to the project. Rename this form (using the Object Inspector) to 'frmAddNewCustomer'. Repeat the steps for the third form, change the name to 'frmEditCustomer' (leave the name of the unit to be 'Unit3'). When you define the two subsidiary forms Delphi places the following code into the project file:

begin Application.Initialize; Application.CreateForm(TfrmMain, frmMain); Application.CreateForm(TfrmAddNewCustomer, frmAddNewCustomer); Application.CreateForm(TfrmEditCustomer, frmEditCustomer); Application.Run;

Page | 70

end

Each Application.CreateForm... line creates the form object specified by the variable in the second argument as an instance of the class specified in the first. If you leave the code as it is, all three forms will be created in order. The first form in the list (named frmMain here, but it could be any name) is designated as the main form for the project and will be visible once Application.Run has executed. To show either of the other forms you would to apply code to some user-generated event to "show" it (i.e. bring it to the top):

frmAddNewCustomer.Show; //or frmAddNewCustomer.ShowModal;

Ok, so what are the diferences between the Show and ShowModal methods? Making Forms Work - a Primer Page 2: Showing Modal forms - where the application can't continue to run until the (modal) form is closed.

Since we now know how to add more forms to a Delphi (SDI) project, let's see how to call some of those forms dynamically at run time... Modal Delphi Forms Since it is very unlikely that this application would need to have all three forms showing at once, it makes no sense to keep them there hidden and using resources unnecessarily. Our program should create the Add or Edit form only when needed! For example, to display the Add Customer form from the main form: From the Delphi IDE main menu, File -> Use Unit, select Unit2 (frmAddNewCustomer). Note that after this action, 'Unit2' is added to the implementation uses clause (just below the word implementation) of the Unit1 (main form).

Page | 71

Remove the following line from the project file (delete it manually or use Project Options in the IDE):

Application.CreateForm(TfrmAddNewCustomer, frmAddNewCustomer);

Place a TButton component (Name: 'cmdAddNewCustomer') on the form, double click the left mouse button to generate an OnClick event. This drops us down to the code editor were you would amplify the supplied code framework to show the Add New Customer form when the user clicks the button:

procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject); var f:TfrmAddNewCustomer; begin f := TfrmAddNewCustomer.Create(Self); try f.ShowModal; finally f.Release; end; end; The first thing needed is a variable (f, in our example) to create an instance of the "Add New Customer" form, whose type is TfrmAddNewCustomer. Using this variable, the "Add New Customer" form can be initialized using the Create method of our form's ancestor, TCustomForm. The single argument required by Create is the Owner of the form. The above example uses the Self keyword to make the main form the owner. The owner could also be the application itself; or you can specify Nil. Caution is needed with a Nil owner, however. You must take care to destroy the form object yourself, using the Free method. If you try to use Release for a form (or any object) with a Nil owner, your program will crash later with an access violation.
Page | 72

Once the form has been created, the method ShowModal displays it in a modal state by using. During the time the form is shown and terminated there may be problems, so placing the Show method into a tryfinally exception block guarantees that the Release method is called and resources are released once the main form has terminated. In contrast, Free releases the resources immediately the modal form closes, makeing a safer call if the modal form is likely to be invoked more than once during the program. Making Forms Work - a Primer Page 3: Showing Non-modal forms - making them visible and bringing to the front of other forms on the screen.

Ok, now we know how to add more forms to a Delphi (SDI) project, and even how to call a modal form, let's see how to call some of those forms in a non-modal fasion... A Non-Modal Form If the secondary form needs to be non-modal, use Show instead of ShowModal. We can't use exactly similar code because the form would show briefly, then disappear, because Release is called right after showing the form. To correct this problem use the code shown below:

procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject); var f:TfrmAddNewCustomer ; begin f := TfrmAddNewCustomer.Create(Self); f.Show ; end;

Then in the FormClose event for the Add New Customer form add the following (Action caFree frees up all resources for the form and destroys it):

Page | 73

procedure TfrmAddNewCustomer.FormClose(Sender: TObject); var Action: TCloseAction); begin Action := caFree ; end;

Whoops, there is a slight problem with the code! Can you see the problem? Since the secondary form is non-modal the user can go back to the main form and press the Add button again. The program tries to create another Add New Customer form with unfortunate results if you have not planned for it! To prevent this from happening first check to see if the form object already exists. If so, simply show it; if not, create the form and then show it. There are various ways to test for an object's existence, for example:

procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject); var f:TfrmAddNewCustomer ; iFound, i:Integer ; begin iFound := -1; for i := 0 to Screen.FormCount -1 do if Screen.Forms[i] is TfrmAddNewCustomer then iFound := i;

Page | 74

if iFound >= 0 then begin ShowMessage('Add Customer form already created, will now show it') ; Screen.Forms[iFound].Show ; end else begin ShowMessage('Add New Customer form not found, creating...') ; f := TfrmAddNewCustomer.Create(Self) ; f.Show ; end; end;

These methods give your program much better control of resources than auto-created forms, with very little hand-coding. There is a slight performance trade-off with dynamically created forms, but a delay is unlikely to be perceptible unless the called forms are very complex and/or your user has an unreasonably slow machine. Caution: When defining forms for dynamic invocation you should be aware of the need to avoid any named reference to the properties of the object whose variable is declared in the interface section. That variable will never be assigned because the calling form uses its own local variable to invoke it. For example:

procedure TfrmAddNewCustomer.FormCreate(Sender: TObject); begin frmAddNewCustomer.Top := Top + 12 ;

Page | 75

end;

Using our example, the reference to the variable frmAddNewCustomer would cause an access violation. The code below demonstrates how you need to work with properties of the secondary form

procedure TfrmAddNewCustomer.FormCreate(Sender: TObject); begin Top := Top + 12 ; end;

If for some reason you need an explicit reference to the form object, use the identifier Self in front of the property or method.

procedure TfrmAddNewCustomer.FormCreate(Sender: TObject); begin Self.Top := Self.Top + 12 ; end;

The variable in the interface section of can be safely removed if the form is intended only for dynamic invocation. It is scarcely worth doing, though, since it will be optimized out when the project is compiled. For completeness, here is a method for showing a modeless form.

procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject); var

Page | 76

f:TfrmAddNewCustomer ; begin with TfrmAddNewCustomer.Create(Self) do begin Show; end; end;

Once you are finished using the form it can be destroyed by using the following code:

procedure TfrmMain.cmdCloseFormClick(Sender: TObject); var f:TForm; begin f := TForm(FindComponent('frmAddNewCustomer')) ; if f <> nil then f.Release else ShowMessage('Failed to find it') ; end;

All set. Ready to code ... read on and learn about some more from_the_battle_field situations...

Page | 77

Making Forms Work - a Primer Page 4: Form created? How to position a secondary form precisely!

Almost done. After we have added two more forms to a Delphi (SDI) project, and have displayed some of them as modal, andmodeless, we are ready for some real world situations (and solutions). Tidy Habits Use FindComponent to determine if the form exists or not. FindComponent returns an object (if it exists) of type TControl. Since the desired type is TForm, we typecast FindComponent's result to cast the object as TForm and assign a TForm variable to grab the result. Theoretically, if FindComponent returns Nil, the form does not exist - or does it? Try placing the two procedures above into a main form. Provide a command button named 'CmdAddNewCustomer' to create the form. Provide another command button 'CmdCloseForm' to close the form. Compile and run the project. Click the 'CmdAddNewCustomer' button to create the form and then close it using the 'CmdCloseForm' button. Now click "cmdAddNewCustomer" twice followed by click "cmdCloseForm" twice. On the first attempt the child form is found, then closed, the second attempt fails! To get around this problem a unique name needs to be assigned to the child form. Select a meaningful name and append an integer to the end of the name i.e. 'AddCustomer_' + IntToStr(iForm) ; The variable iForm would be a private variable of frmMain which you would initialize during the OnCreate event of frmMain. Here is the altered code for creating the subsidiary form:

procedure TfrmMain.Button1Click(Sender: TObject); var f:TfrmAddNewCustomer ; begin Inc(iForm) ;

Page | 78

with TfrmAddNewCustomer.Create(Self) do begin Name := 'AddCustomer_' + IntToStr(iForm) ; {The caption will be the name of the form given above} Show ; end ; end;

On the first call to this event iForm would be zero, then directly before creating the form we use Inc to increment iForm to one. This gives the next invocation of the form a name of AddCustomer_1, which can be used later to remove any instances of the form:

procedure TfrmMain.cmdCloseFormClick(Sender: TObject); var f:TForm; i:Integer; begin for i := 1 to iForm do begin f := TForm(FindComponent('AddCustomer_' + IntToStr(i))) ; if f <> nil then f.Release else MessageDlg('Failed to locate AddCustomer_' +

Page | 79

IntToStr(i),mtError,[mbOk],0) ; end; end; Right Place, Right Time One more benefit from creating forms dynamically is the ability to position a secondary form precisely, relative to a control on the parent form. Suppose you need to position the Add New Customer form directly on a node in a TreeView displaying accounts. In this example the Add form is displayed when the user double clicks on the TreeView. First place a TreeView on a main form in a project, select the TreeView, press F11 for the Object Inspector, select the property "Items" and double click the left mouse button. Add several new items and sub-items (optional). Finish up by pressing the "OK" button. Next, select the Events page of the Object Inspector while the TreeView is still selected. Double click on the event OnDblClick and enter the following code (which I shall explain in another article):

procedure TfrmMain.TreeView1DblClick(Sender: TObject); var A, R, P : TPoint; TheSelectedNode : TTreeNode; f:TfrmAddNewCustomer ; begin if TreeView1.Selected = nil then Exit; GetCursorPos(P); A := TreeView1.ScreenToClient(P);

Page | 80

TheSelectedNode := TreeView1.GetNodeAt(A.x, A.y); if TheSelectedNode = nil then raise Exception.Create('Please click on an Item in the TreeView!'); GetCursorPos(R) ; P := ClientToScreen(Point(A.x,A.y)); f := TfrmAddNewCustomer.Create(Self) ; try f.Top := R.y ; f.Left := R.x ; f.Caption := 'Add customer to account: "' + TheSelectedNode.Text + '"' ; f.ShowModal ; finally f.Release ; end; end;

Run the project. Select a node in the TreeView and double click the left mouse button. The secondary form should appear with its top left corner positioned on the node which was double clicked. Its caption identifies the item selected in the TreeView. If you had centered the form on the screen, the specific information concerning the item selected in the TreeView would not be accessible. Again, dynamic creation of forms can be of great service to you! I hope this chapter has given some insight into the techniques Delphi puts at your disposal for taking control of form creation and behaviour when developing SDI forms. Next time, more on working with forms. Until then, have fun! Some exercises for you... What happens when you call (create) a non modal Form3 from Form2, and after that call (create) Form2 from Form3?
Page | 81

Chapter 15 Communicating Between Forms Page 1: Finding out how a modal form was closed Finding out how a modal form was closed Modal forms offer specific features that we cannot have when displaying non-modally. Most commonly, we will display a form modally to isolate its processes from anything that might otherwise happen on the main form. Once these processes complete, you might want to know whether the user pressed the Save or Cancel button to close the modal form. You can write some interesting code to accomplish this, but it does not have to be difficult. Delphi supplies modal forms with the ModalResult property, which we can read to tell how the user exited the form. The following code returns a result, but the calling routine ignores it:

var F:TForm2; begin F := TForm2.Create(nil); F.ShowModal; F.Release; ...

The example shown above just shows the form, lets the user do something with it, then releases it. To check how the form was terminated we need to take advantage of the fact that the ShowModal method is a function that returns one of several ModalResult values. Change the line F.ShowModal
Page | 82

to if F.ShowModal = mrOk then We need some code in the modal form to set up whatever it is we want to retrieve. There is more than one way to get the ModalResult because TForm is not the only component having a ModalResult property - TButton has one too. Let us look at TButton's ModalResult first. Start a new project, and add one additional form (Delphi IDE Main menu: File -> New -> Form). This new form will have a 'Form2' name. Next add a TButton (Name: 'Button1') to the main form (Form1), double click the new button and enter the following code:

procedure TForm1.Button1Click(Sender: TObject); var f : TForm2; begin f := TForm2.Create(nil); try if f.ShowModal = mrOk then Caption := 'Yes' else Caption := 'No'; finally f.Release; end; end;

Page | 83

Now select the additional form. Give it two TButtons, labelling one 'Save' (Name : 'btnSave'; Caption: 'Save') and the other 'Cancel' (Name : 'btnCancel'; Caption: 'Cancel'). Select the Save button and press F4 to bring up the Object Inspector, scroll up/down until you find the property ModalResult and set it to mrOk. Go back to the form and select the Cancel button, press F4, select the property ModalResult, and set it to mrCancel. It's as simple as that. Now press F9 to run the project. (Depending on your environment settings, Delphi may prompt to save the files.) Once the main form appears, press the Button1 you added earlier, to show the child form. When the child form appears press the Save button and the form closes, once back to the main form note that it's caption says "Yes". Press the main form's button to bring up the child form again but this time press the Cancel button (or the System menu Close item or the [x] button in the caption area). The main form's caption will read "No". How does this work? To find out take a look at the Click event for TButton (from StdCtrls.pas):

procedure TButton.Click; var Form: TCustomForm; begin Form := GetParentForm(Self); if Form nil then Form.ModalResult := ModalResult; inherited Click; end; What happens is that the Owner (in this case the secondary form) of TButton gets its ModalResult set according to the value of the TButton's ModalResult. If you don't set TButton.ModalResult, then the value is
Page | 84

mrNone (by defaolt). Even if the TButton is placed on another control the parent form is still used to set its result. The last line then invokes the Click event inherited from its ancestor class. To understand what goes on with the Forms ModalResult it is worthwhile reviewing the code in Forms.pas, which you should be able to find in ..\DelphiN\Source (where N represents the version number). In TForm's ShowModal function, directly after the form is shown, a Repeat-Until loop starts, which keeps checking for the variable ModalResult to become a value greater than zero. When this occurs, the final code closes the form. You can set ModalResult at design-time, as described above, but you can also set the form's ModalResult property directly in code at run-time. Now, knowing whether the user wants to accept or reject what occurred on the child form, the calling form can react by using the information from the child form or ignoring it. Let's see how...

Communicating Between Forms Page 2: Getting the values from another form

Now, knowing whether the user wants to accept or reject what occurred on the child form, the calling form can react by using the information from the child form or ignoring it. Let's see how... Events to look at for ModalResult (in Forms.pas): function TCustomForm.ShowModal: Integer; procedure TCustomForm.CloseModal; function TCustomForm.CloseQuery: Boolean; procedure TCustomForm.Close; ModalResult Constants from Controls.pas const mrNone = 0; mrOk = idOk; mrCancel = idCancel;
Page | 85

mrAbort = idAbort; mrRetry = idRetry; mrIgnore = idIgnore; mrYes = idYes; mrNo = idNo; mrAll = mrNo + 1; mrNoToAll = mrAll + 1; mrYesToAll = mrNoToAll + 1; Sometimes you will need to get more than one piece of information back from a form, but let's start simple. The following example shows how to get the value stored in a TEdit (Name : 'Edit1') Text property if the user pressed the Ok button on the child form (Name: 'frmSimpleString').

// Code for main form procedure TfrmMain.ModalResultdemo2Click(Sender: TObject); var f:TfrmSimpleString ; begin f := TfrmSimpleString.Create(nil); try if f.ShowModal = mrOk then ShowMessage(f.Edit1.Text); finally f.Release; end; end;

To keep the example short we are just checking for an Ok response and ignoring negative responses. When the user presses the OK button (its
Page | 86

ModalResult is set to mrOk) the following code reads the value of an edit control, Edit1, on the called form and then destroys the form. In a real application you might need information from several components on the called form. We could have streamlined the code a little more by adding code to let the user choose whether to continue with closing the form or aborting back into the called form:

// Code for main form procedure TfrmMain.ModalResultdemo2Click(Sender: TObject); var f:TfrmSimpleString ; S:String ; begin f := TfrmSimpleString.Create(nil); try f.GetThatString(S); ShowMessage(s); finally f.Release; end; end;

// Code for the called (child) form

Page | 87

type TfrmSimpleString = class(TForm) Button1: TButton; Button2: TButton; Edit1: TEdit;

private { Private declarations } public { Public declarations } procedure GetThatString(var aString: string); end;

var frmSimpleString: TfrmSimpleString;

implementation

{$R *.DFM}

Page | 88

procedure TfrmSimpleString.GetThatString(var aString: string); begin case ShowModal of mrOk: aString := 'OK' ; mrCancel: aString := 'Cancel' ; end; end;

end.

On the called form we add a procedure GetThatString to interrogate the user modally to find out whether the user wants to continue or abort the current operation. For elegance, the Case statement is used rather than a chain of nested IF..THEN..ELSE statements, i.e.

If ShowModal = mrOk then ... else if ShowModal = mrCancel then ... else if ShowModal = mrRetry then ... Retrieving a string is simple enough but, often, you will need much more than a simple string. Methods for getting a lot of information back from a called form include using anarray, a TList, a StringList, or records. The logic used to determine if the user wants to return information remains the same, only the means of storing it varies. To show how this might be done, I will use a record to store user input for retrieval once the user
Page | 89

presses OK in the called form. To make things interesting, we will use an array of recordsso that multiple pieces of information can be returned from the called form! Before digging into an example, lets go through a short lesson of record types for those programmers that may have not worked with records: "Records in Delphi". Communicating Between Forms Page 3: Moving records of data between two forms

Back in the "Records in Delphi" article, a sample code was presented demonstrating pressing a button to populate the array of records with dummy information, then display the records in the memo control. This should give you a starting point to working with record arrays. The main thing to remember about the example is that we created a record type, supplied the name of TPerson, then created a local variable called MyPeople which is an array of type TPerson which can hold three (3) rows of information. Let's use the idea to present a data entry form to a user, allow data to be entered about several people before returning to the main form to process it. Use the next piece of code to call the secondary form for the data entry:

procedure TfrmMain.PersonRecord1Click(Sender: TObject); var f:TForm5; begin f := TForm5.Create(Self); try if f.ShowModal = mrOk then if f.PersonCount > 0 then

Page | 90

ShowMessage('The first person is' + #13 + '"' + f.PersonArray[0].FirstName + '" "' + f. PersonArray[0].LastName + '"'); finally f.Release; end; end;

When the user invokes the event, the main form creates the data entry form, shows it, then checks to see if there were any people entered into the people array. The variable PersonCount in the data entry form is incremented each time a new person is added to the array of records. The following code is placed into a simple unit (Delphi IDE Main Menu: File -> New -> Unit) so that any form in the project can see the People record along with a constant used to limit how many elements (records) can be used in the array of records.

unit kg_Globals; //saved as kg_Globals

interface

uses Windows,Dialogs,SysUtils,Messages,Classes,Forms,FileCtrl;

const

Page | 91

ARRAY_SIZE = 5; {Could be a larger size}

type

{Keep each record member to strings to make the example code easy to understand}

Tkg_People = Record FirstName : String; LastName : String; Street : String; City : String; State : String; ZipCode : String; Phone : String; Email : String; end;

implementation

end.

Page | 92

In the public portion of the called form's declarations we declare an array of records and a variable for keeping track of how many records the user has entered.

... public { Public declarations } PersonArray: Array [0..ARRAY_SIZE] of Tkg_People; PersonCount: Integer; ...

In the Create event of the data entry form the following code is called to clear all edit controls so that the form starts up with a clean slate. Each time the user accepts a screenful of people information the ClearEdit procedure is triggered:

procedure TForm5.ClearEdits; var i:Integer; begin { sets each TEdit control's text property to an empty string } for i := 0 to ComponentCount -1 do if (Components[i] is TEdit) then TEdit(Components[i]).Text := '' ;

Page | 93

end;

The next code fills a row of data in the people array and increments the row counter. The counter is checked to ensure that the number of rows keeps within the fixed boundaries of the array of records.

procedure TForm5.cmdAddPersonClick(Sender: TObject); begin { Populate a record in an element in the array of records with current textbox values }

PersonArray[PersonCount].FirstName := First.Text; PersonArray[PersonCount].LastName := Last.Text; PersonArray[PersonCount].Street := Street.Text; PersonArray[PersonCount].City := City.Text; PersonArray[PersonCount].State := State.Text; PersonArray[PersonCount].ZipCode := Zip.Text; PersonArray[PersonCount].Phone := Phone.Text; PersonArray[PersonCount].Email := Email.Text; Inc(PersonCount);

{ Once the maximim elements are used up,

Page | 94

disable this button.

NOTE: ARRAY_SIZE is a user defined constant created for this demo. The Constant indicates how many records can be accessed in the array. }

TButton(Sender).Enabled := PersonCount < ARRAY_SIZE;

{ If we can still add more persons clear old entries, place focus on "First" name text control }

if TButton(Sender).Enabled then begin ClearEdits; First.SetFocus ; end else

Page | 95

{ Exit time , give them a hint by placing focus on the exit button } cmdCloseForm.SetFocus ; end;

Using this example you can return just about any type of data, in either single or multiple rows. To recap, you can detect how a modal form was closed and get information back in several ways. Keep in mind there are other ways to accomplish returning information from modal forms. Detour time The last thing a programmer needs when searching for assistance in a help file or manuals is an incorrect example of how to do a particular task. I caught one in D4 after writing this article, to do with "Passing additional arguments to forms", listed under "forms" in Delphi help. Shown below are the key pieces:

type TResultsForm = class(TForm) ResultsLabel: TLabel; OKButton: TButton; procedure OKButtonClick(Sender: TObject);

private

public

Page | 96

constructor CreateWithButton(whichButton: Integer; Owner: TComponent); end;

constructor CreateWithButton(whichButton: Integer; Owner: TComponent); begin case whichButton of 1: ResultsLabel.Caption := 'You picked the first button.'; 2: ResultsLabel.Caption := 'You picked the second button.'; 3: ResultsLabel.Caption := 'You picked the third button.'; end; end;

procedure TMainForm.SecondButtonClick(Sender: TObject); var rf: TResultsForm; begin rf := TResultsForm.CreateWithButton(2, self); rf.ShowModal; rf.Free; end;

Don't feel bad if you can not figure out what's missing/wrong with the example. This code was posted on Borland's news group as a solution for
Page | 97

a posted question. Instead of trying to figure out the errors, look at the correct code:

type TResultsForm = class(TForm) ResultsLabel: TLabel; OKButton: TButton;

private

public constructor Create(whichButton: Integer; Owner: TComponent); reintroduce; end;

constructor TResultsForm.Create(whichButton: Integer; Owner: TComponent); begin inherited Create(Owner) ; case whichButton of 1: ResultsLabel.Caption := 'You picked the first button.'; 2: ResultsLabel.Caption := 'You picked the second button.'; 3: ResultsLabel.Caption := 'You picked the third button.'; end;

Page | 98

end;

procedure TMainForm.SecondButtonClick(Sender: TObject); var f: TResultsForm ; begin f := TResultsForm.Create(2, self); try f.ShowModal; finally f.Release ; end; end; Some exercises for you... This time an easy one: download the example Delphi project for this chapter and try out the code without having to type it in yourself. The source units also expand some concepts beyond the scope of this chapter and provide more technical explanations. Chapter 16 No DB support in Delphi Personal edition As you probably know, Delphi Personal edition comes with no database support, in terms of data-aware components. Hopefully, "data world" isn't only about Paradox, Access, Interbase or MySQL. The data your application needs to operate on may not always be stored in a relational database. It may be stored in an ASCII text file or a structured binary file. It may even be stored in an XML file.

Page | 99

As you will learn from this chapter, your application can read data from a file (of *any* structure), update "fields", add new "records", and write data back to the file as if the data were stored in a *real* database. NOTE: flat-file databases are not as scaleable (and safe) as the relational model databases (MySQL, MS SQL Server, Interbase, etc). If you are only working with a small amount of data that is rarely updated then a flat-file database could be your choice. However, if you are looking for a database for more frequent and heavy use then a relational database is probably more suitable. Be sure to check our "Beginner's Guide to Delphi Database Programming"! Files from Delphi perspective Simply put a file is a binary sequence of some type. In Delphi, there a three classes of file: typed, text, and untyped. Typed files are files that contain data of a particular type, such as Double, Integer or previously defined custom Record type. Text files contain readable ASCII characters (organized into lines). Untyped files are used when we want to impose the least possible structure on a file. Building your own database system The next series of articles shows you how to use Delphi to create a simple "database" application that interacts with data in text (ASCII) or binary files: Handling ASCII (text) files from Object Pascal code Simply put, text files contain readable ASCII characters. Although it is possible to make changes within text file, jump around when processing information or add some data to the file other than at the end, it is advisable to use a text file only when we know that we are working with ordinary text and no such operations are necessary.

Your own database Working with binary files from Delphi. Use Delphi to manage writing, reading and updating your own types of files. Writing and reading. Seeking and positioning. Changing and updating. Files with no structure (untyped files) Using Delphi to manage operations on untyped files - direct access to disk files regardless of type and structuring. Various File IO articles and tutorials Including: Understanding what files are and choosing a Delphi file type. What is a File? How are they stored? What format is best for my project (text, files of record or untyped)? Accessing 'Configuration Settings' files (.INI) with Delphi How a simple text file can beat Registry in storing a few pieces of application specific configuration data. File Management Routines A list of Delphi's RTL functions designed to ease file management.

Page | 100

Some exercises for you... Make a plan, create a simple Delphi project, let's say some kind of database (with typed files) to store all your CD's, DVD's... Chapter 17 Working with units Page 1: What is a Delphi unit? The structure of a unit.

Welcome to the seventeenth chapter of the FREE online programming course: A Beginner's Guide to Delphi Programming. While developing a (large) Delphi application, as program becomes more complex, its source code can become very hard to maintain. In this article you will learn about creating your own code modues - Delphi code files that contain logically associated functions and procedures. Along the process we'll briefly discuss using Delphi's built-in routines and how to make all the units of a Delphi application cooperate. Here is an overview of the topics discussed in this chapter:

What is a Delphi unit? The structure of a unit. Using Delphi's built-in unit files (RTL/VCL routines). Creating and using your own code units. The scope (visibility) of a Delphi identifier. What is a Delphi unit? In the fifth chapter of this course, we've opened a discussion on Delphi units. In general, a Delphi program is constructed from source-code modules called units. Each unit is stored in its own file (.pas) and compiled separately; compiled units are linked to create an application. As stated, forms are visible building blocks of a Delphi application. Each form in a Delphi project has an associated unit. The unit contains the source code for any event handlers attached to the events of the form or the components it contains. Much of the work done by an application is performed by event-driven procedures; these provide the responses to the user's actions. Beside the source code that "drives" event handlers for the form and the component on it, you can write custom procedures or functions within a unit that's associated with a form. A custom procedure could be used, for example,

Page | 101

to check whether a date entered in an edit box is valid. However, units don't have to be associated with forms. If you want to reuse the routines that you write, it's better to create a separate unit to contain those routines. By creating stand-alone units that have no associated forms, you can easily make your procedures and functions available to other projects. A "code unit" contains code that is called from other units in the project. When you start building libraries of useful routines, you will probably store them in a code unit. You can have any number of code units in your project so it is a good idea to split up your general-purpose procedures. The idea is to have one unit for date checking routines, another one for text-handling purposes and the third for dealing with databases. Structure of a unit I've already explained a structure of a Delphi unit (form unit and/or code unit) in the fifth chapter of this course, the next "illustration" summarizes what's already been stated:

unit MyUnit; //unit name interface { In this section the routines (types, variables) are specified that are available for use in other units } uses { The list of the unit names whose routines our unit wants to use } implementation { This section contains all the source code that defines all the types listed/defined

Page | 102

in the interface section } uses { The optional list of the unit names with routines our unit wants to use } initialization { This optional section contains all source code needed to be executed when the unit is referenced for the first time - to initialize any data the unit uses} finalization { This optional section is required only if we have the initialization section. Here we write the code we need to be executed when the form is removed from memory. If your unit needs to perform any cleanup when the application terminates, such as freeing any resources allocated in the initialization part; you can add the cleanup code into the finalization section} end. //unit end

Now, that you know (in theory) what goes in a Delphi unit, it's time to see how to create Delphi programs that comprise a (larger) number of code units. "A unit uses B unit" ?! Stop for a minute, note the uses section/clause of the interface (or
Page | 103

implementation) section... "The list of the unit names whose routines (types, variables ...) our unit wants to use". Uh, what a tricky sentence, what exactly does that mean? Who maintains this list? How do you know what unit your unit needs to use? How do you know what exact unit you need to add to the list if you want to use the "MatchesMask" Delphi function? First, let's see how Delphi maintains the uses list automatically as you add components to a form... Working with units Page 2: How Delphi maintains a units source. Using Delphi's RTL routines.

Only understanding the structure of a Delphi unit is unfortunately, not enough for a Delphi beginner to start working on large sized applications. Developers who are new to Delphi often find that they are uncertain about which unit they need to "use" and how to "use" it. Before we move on to creating your own code units, let's see how Delphi generates and maintains a form unit source. Examining a form unit and its uses clause Every time you start Delphi (or create a new project), the Delphi IDE displays a new form for you to customize. In the Code editor, the automatically generated form unit declares a new class type for the form and includes the code that creates the new form instance. In order to run, a Delphi application needs a lot of background information, such as how to interact with the operating system and how to create forms and components, such as buttons and edit boxes. This information is contained in various unit files that are a standard part of Delphi. This collection of units is referred to as the RTL (run time library). Let's begin our examination of the uses list by looking at the autogenerated code:

01: unit Unit1;

Page | 104

02: interface

03: uses 03: Windows, Messages, SysUtils, Variants, Classes, 03: Graphics, Controls, Forms, Dialogs;

04: type 06: TForm1 = class(TForm) 07: private 08: { Private declarations }

09: public 10: { Public declarations }

11: end;

12: var 13: Form1: TForm1;

14: implementation 15: {$R *.dfm}

Page | 105

16: end.

When you create a new form (with its associated unit), Delphi places all necessary units in the (interface) uses clause (line: 03). Although you haven't added any components to the form or written any code, you already have a complete GUI application that you can compile and run. All it does is display a blank form. Note: two more units are also "listed" in the uses list. The System unit and the SysInit unit are used automatically by every application and cannot be listed explicitly in the uses clause. For instance, routines in the System unit implement file I/O, string handling, floating point operations, dynamic memory allocation, and so forth. Placing components on a form... Recall that Delphi comes with more than 200 components for you to "drop on a form" (depending on your Delphi version). It's quite natural that all those components are not defined in only 10 units, after all every thirdparty component (or the one you build on your own) comes "inside" its own unit. A question: should you need to know the exact name of every unit when you want to use some component? Luckily, no. Suppose you add a (standard) button component to this form and write an OnClick event handler that changes the Caption of the form when the user clicks the button. The result might look like this:

01: unit Unit1;

02: interface

03: uses 03: Windows, Messages, SysUtils, Variants, Classes, 03: Graphics, Controls, Forms, Dialogs, StdCtrls;

Page | 106

04: type 06: TForm1 = class(TForm) 07: 08: Button1: TButton; procedure Button1Click(Sender: TObject);

09: private 10: { Private declarations }

11: public 12: { Public declarations }

13: end;

14: var 15: Form1: TForm1;

16: implementation 17: {$R *.dfm}

18: procedure TForm1.Button1Click(Sender: TObject); 19: begin 20: //enter code here: 21: Caption := 'Clicked';

Page | 107

22: end;

23: end.

What happened? Since the button (TButton) control is defined in the StdCtrls unit, Delphi added the reference to it, in the uses list. What this means, is that you, in many cases, do not need know the exact name of the unit your unit needs to use. For every (properly installed) component you drop on a form, Delphi will add the name of the associated unit to the uses list! Note: If you remove that button component, you just dropped on the form Delphi will not remove the reference to the "StdCtrls" unit from the uses list. Using Delphi's built-in unit files (RTL/VCL routines) Run Time Library, or VCL routines are (general purpose) functions and procedure that are built into Delphi. A section on this site, RTL reference, provides a through example-reference to the RTL capabilities of Delphi. For example, the StrUtils unit provides routines for string manipulation; DateUtils includes routines for date manipulation. Now, suppose you have an edit box (Edit1) on a form. When the user clicks a button, the Caption of the form should change to a reversed string of a string specified in the edit box. Here's the code:

procedure TForm1.Button1Click(Sender: TObject); begin Caption:=ReverseString(Edit1.Text); end;

If you try to compile and run the application, you'll get the following
Page | 108

error: Undeclared identifier: 'ReverseString'. What this means is that the compiler could not find the given identifier (ReverseString) - it might be from another unit that is not listed in the uses clause. In other words, when using built-in routines, you'll need to know the exact name of the unit in which they are defined. To fix the above error, you need to manually add the unit "StrUtils" to the uses list.

03: uses 03: Windows, Messages, SysUtils, Variants, Classes, Graphics, 03: Controls, Forms, Dialogs, StdCtrls, StrUtils; But how do you know that Delphi has the ReverseString function? And what if you *know* that there is a function called "MatchesMask", but you simply cannot recall the name of the unit in which the function is defined? One way to help yourself, is to browse through our "Delphi RTL reference" section. Another way is to hit F1 (call Delphi help system) every time an "undeclared identifier..." pops up. If you "forgot" to add a reference to a unit, the Delphi compiler display an error message and places the cursor at he point in the source code that it cannot process. Usually, you will find there is a routine that is declared in a unit that is not listed in the uses list. Delphi's Help system should tell you which unit you need to add. Now that you have seen how Delphi maintains a form unit source (and its uses list), and what needs to be done in order to use Delphi's built-in routines, you are ready to start creating your own code units... Working with units Page 3: Creating and using your own code units

Now that you know the structure of a Delphi unit, and how Delphi helps you maintain a forms unit source, it's time to start creating your own collection of useful functions and procedures.
Page | 109

Depending on your background, creating and using code units can be easy to understand or may be confusing at first. New ... unit The simplest way to create a new unit for your project is to select File New - Unit from the main Delphi IDE menu. The Code Editor will display the newly created unit and its skeleton code:

unit Unit2;

interface

implementation

end.

Naming (saving) a unit The unit heading specifies the unit's name. It consists of the reserved word "unit", followed by a valid identifier, followed by a semicolon. Unit names must be unique within a project. To save a unit under a different name, go to File - Save As, in the Save As dialog box enter a valid file name and click ok. Caution: If you change the name of the unit after, you'll need to change the source (uses list) of all the units that use this unit. Note that the call to a procedure SomeProcedureName, defined in unit named MySuperUnit, consists of a unit name (MySuperUnit) followed by a period (.) and a procedure name. This fact is very important! If in some stage of the development of your project you decide to save MySuperUnit under a different name - you will need to change the call to any procedure inside that unit, since the name of the unit will no longer be MySuperUnit. This is the reason why you should give meaningful name to units in the early stage of unit "development". Creating routines in units I'll now quickly introduce the steps you need to do, in order to create a new routine (procedure or function) in a code unit. To add a general purpose routine (procedure or function) to a code unit,
Page | 110

do the following: 1. Make sure the units source is displayed in the Code Editor 2. Type the routine header in the interface section to make the procedure available to other units that will use this unit. 3. Repeat the routine header in the implementation section and add the routine code Here's an example:

unit Unit2;

interface uses Dialogs, StrUtils;

procedure SayHelloTo(YourName : string);

implementation

procedure ReveredHello(YourName : string); var hello : string; begin hello := YourName + ', hello!'; ShowMessage(hello); end;

procedure SayHelloTo(YourName : string);

Page | 111

var hello : string; begin hello := YourName + ', hello!'; ShowMessage(hello);

ReveredHello(ReverseString(YourName)); end;

end. Let's see what's "inside". We have two procedures: SayHelloTo and ReveredHello. The SayHelloTo accepts a string parameter and displays a "hello" message (using theShowMessage Delphi RTL function). This procedure calls the ReveredHello procedure. The header of the SayHelloTo is added to the interface section, making the procedure available to any unit using this unit. The header of the ReveredHello function is not added to the interface section, the function is "private" to this unit. What this means is that any unit using this unit will only be able to call the SayHelloTo, the ReveredHello is hidden to the "outside world" The scope Code units are not limited to functions and procedures. (The obvious fact, but I needed to write it down). A code unit can be used to define you own class (or type, whatever you prefer), or a component. Several routines in a code unit can share the same "set" of variables, constants, etc. If you define a variable (identifier) inside the interface section the variable will be "visible" to all units using this unit. If you define an identifier inside the implementation section, the identifier is hidden to the outer world.

When designing the overall structure of your application it is essential to have an understanding of the scope (visibility) of identifiers (routines, variables, types) defined inside the unit:

Everything defined in the interface section will be available to all other units using this unit Everything defined in the implementation section is available only

Page | 112

within the unit Using standard (RTL) units When you create your own units, you are responsible for listing the units your unit needs to use. You'll need to add different standard Delphi units, according to which routine you are implementing in your unit, to your uses list In the above example, both procedures use the ShowMessage routine defined in the Dialogs unit. The SayHelloTo procedure uses the ReverseString procedure (StrUtils Delphi unit). This is why our uses clause (in the interface part) lists those two units (Dialogs, StrUtils). Calling custom routines from form units In order to let a form unit use the procedures defined in your own unit, you'll need to add the name of your unit to the uses list. Recall that you can place the uses list in the interface section and in the implementation section of a unit. In general, add a reference to standard Delphi units in the interface section; add a reference to your own code units in the implementation section. Only when identifiers from another unit are used in the interface section is it necessary to list that unit in the interface uses clause. This is how you would call the SayHelloTo (once again: Unit1 cannot call the ReversedHello function) procedure from the unit associated with our main form:

... implementation {$R *.dfm}

uses unit2; //don't miss this

procedure TForm1.Button1Click(Sender: TObject); begin

Page | 113

Unit2.SayHelloTo('Zarko Gajic'); end; ...

And this is the "output":

As you can see two "hello" message boxes are displayed; the first one comes from the SayHelloTo procedure, another one comes immediately after displaying the reversed message. Local, private, public? As you can see, advantages of using code units include code reuse and information hiding. With the use of the appropriate inclusion statement (uses list), all identifiers listed in the interface portion of a unit are available to any program using the unit. A code unit can contain constants, variables, user data types, routines, all of which can be hidden from the calling unit (defined in the implementation section). The calling unit needs only to know the interface syntax to utilize the public functionality contained within the unit - the inner workings of a unit file are for you to take care of. We'll now move to the last part of this article to see what happens if unitA needs to use UnitB and UnitB needs to use UnitA. More on scope, or "now you see me, now you don't!" So, you've created your first code unit. Problems? One thing that might not be so obvious is how to know where to place the variable declaration in order to make a variable public, or private to a unit... Let's start with an example. We'll add another unit to our sample project. Start by creating a new code unit (Unit3). Here's the code:

Page | 114

unit Unit3; interface uses Dialogs; procedure ExposedRoutine var Glob : string implementation var Privat : string; procedure HiddenRoutine; begin ShowMessage('You do not see me!'); end; (*HiddenRoutine* procedure ExposedRoutine; begin ShowMessage('You see me!'); end; (*ExposedRoutine*) end.

Confusing? If you try to access an identifier defined in this unit from unit1, this is what you'll see:

Page | 115

Bravo Delphi! When you write a unit name followed by a dot (.), Delphi will display a list of all identifiers defined in the interface section of a unit, which a calling unit can use/call. That's it. You are now left on your own! If you need any kind of help at this point, please post to the Delphi Programming Forumwhere all the questions are answered and beginners are treated as experts. Some exercises for you... Make a plan, create a simple Delphi project, try adding several code units, make your code as much reusable as you can... Chapter 18 Code Navigation in Delphi How to be even more productive with Delphi IDE (precisely, the code editor): start using code navigation features - quickly jump from a method implementation and a method declaration, locate a variable declaration using tooltip symbol insight features, and more.

Welcome to the eighteenth chapter of the FREE online programming course: A Beginners Guide to Delphi Programming. How to be even more productive with Delphi IDE (precisely, the code editor): start using code navigation features - quickly jump from a method implementation and a method declaration, locate a variable declaration using tooltip symbol insight features, and more. How to code faster and more efficiently - "take two" Back in the thirteenth chapter of this Course, we've discussed using Delphi IDE's features like code templates, code insight, code completion... I hope you are using all those great code editor features every day. However, how many times
Page | 116

were you scrolling the code editor window just to find one method's implementation? How about having more than one class declaration in the interfacesection - how fast can you locate the class implementation? The answer is: Delphi Code Editor Navigation! Code Explorer When you work on a project, you need to navigate around the source code for that entire project, including tracing calls into various units comprising the project. Delphi includes a great tool called the "Code Explorer" (docked to the left of the Code editor), which enables you to navigate through your unit files. The Code Explorer represents a project unit's in a tree-like diagram that shows all the types, classes, properties, methods, global variables, and global routines defined in a unit. If you cannot see the Code Explorer window in the Code editor, make it visible by clicking on View-Code Explorer menu item in the main Delphi IDE. Here are the icon descriptions:

When you select an item in the Code Explorer, the cursor moves to that item's implementation in the Code editor. Once you are "inside" the code, you could be tempted to jump to the item's declaration in the interface section. This is where code (or module) navigation comes handy. Code Browser, Code Navigation The most common way to navigate your unit's source is with the mouse and navigation keys. While you are working your way through the unit, additional tools called Code Insight help with coding. Code insight feature displays declaration information for any identifier by passing the mouse over it in the Code editor.

Page | 117

Now try this: hold down the Ctrl key and move the mouse over an identifier. It will turn into a hyperlink, and the mouse cursor will turn into a pointing hand. Clicking on the hyperlink will take you to the identifiers declaration. You get the same effect by choosing Find declaration from the editor's context menu, or by hitting the Alt + UpKey key combination. Note: you can even browse through the VCL source is you Delphi version has one. Module Navigation And finally, here's my favorite: While working on a method's code, hit the Ctrl + Shift + DownKey or Ctrl + Shift + UpKey to jump between the method declaration (interface section) and implementation. This keystroke combination comes very handy when used with class completion. Some exercises for you... Hmm, simply start using Delphi code navigation features, you will not believe how much time you can save in coding, and of course you can drastically reduce the number of bugs in your code.

Page | 118

S-ar putea să vă placă și