HOME > > TUTORIALS TABLE OF CONTENTS > > Part One of this tutorial - - - / - - / - - / - - / - - - Other material for programmers

Delphi tutorial: A Component for Use and Re-use in other programs, part 2 (Level 3).

This is still in a draft form.... it is probably mostly right, but I make no promises just yet!!!

Click here if you want to know more about the source and format of these pages.

IGNORE ANY PERIODS (.) AT THE START OF ANY LINE

There is a Search button at the bottom of the page.


Delphi Tutorial 3c... Part Two (version: 27 Dec 98)

In this and the next part, I'll build up a full 'application' using DD04 as a high scores table for a little game. When you run DD05, it will look to see if an old high scores table was saved. If it was, it will fill the DD04 stringgrid with that data. As the game progresses, the display (DD04) and the data file will be updated whenever someone achieves a new score high enough to rank in the table. This really isn't the best place to learn about file handling... there is a separate tutorial on file handling for you. (The on-disc copy of the high scores table doesn't NEED to be updated each time the high scores data changes... IF you can be certain that the user will exit the game properly, instead of just turning the computer off. For the small price of a little wear and tear on the disc drive, I prefer to guard against users turning off without shutting things down properly. Not to mention the times systems crash thanks to features beyond the user's control.

As a customer of software developers, I resent having things written to my hard disc without my consent or full knowlege. As a software vendor, I have always tried to respect the contents of my customer's hard discs. Hence the 'Do you really want this to happen' tests in the following.

We are now going to go back to DD04 (the High Score Table component used by DD05, the demonstrator game) and make it a little more sophistocated. Save DD05, do File|Close Project, and then File|Open Project to re-load DD04.

I will digress here for a moment: 'When I was a boy...' I was taught (by teachers and bitter experience!) always to plan a program thoroughly before beginning to write code. Flow charts or structure diagrams were enormous helps to getting a project done quickly and well. I have yet to find a satisfactory way to describe what my Delphi programs are going to do. I think the problem arises because Delphi programs run in an event driven environment. It certainly is not due to the Pascal foundations of Delphi. Before you start at the keyboard, it is important to have as clear a view as possible of what your program is going to do. Many programs divide nicely into three parts: The initialisation stage, the using the program stage, and the termination stage. If writing a word processor, for instance, the initialising would entail putting a blank 'sheet of paper' on the screen, or loading a previous document for further work. The using stage would entail entering text, formatting it and printing it out, and the termination stage would entail at least offering to save the current session's fruits. Even if I can't fully flowchart my Delphi projects, it helps to break them down into sections.

Back to work!

In DD04 in the initialisation stage we're going to need:


Is there a file with data of previous high scores?
If so... load it
else... ask if the user wants the data saved
if so, and user understands and accepts the write to disc, create
the file, filled with something to mark the place.
(I think it best to make sure the file can be written now,
even though there's nothing to write yet. Why wait to discover
problems until later when disappointment will be caused if some
good scores get lost?). Set 'OK to save' flag,'SaveScoresToDisc'
else (i.e. user doesn't want data saved) set flag to say 'don't save'

('SaveScoresToDisc' will be a PROPERTY of the object, to express the idea of the flag in Delphi/ OOP terms. It will be of the Boolean type, i.e. can be true or false.)

In this demo, I'm not going to allow any flexibility over the name and location of the datafile. It will be called DD04.dat and held in the root of C:. In the real world, of course, you would want to introduce options for both name and location. When you do that, you will need to add provision for checking whether pre-existing files of the same spec as the user's choice should be overwritten. Even this demo should test the contents of C:\DD04.dat to see that they are valid for DD04's use... but I'm not going to do that!

All of the above will go in the OnCreate eventfor DD04.

DD04 will need a method which will be called from DD05 to check a score to see if it merits a place in the high score table and, if it does, update the screen display and (if enabled) update the on-disc copy of the high scores table. We'll call it ConsiderNewScore (When I say 'method', I mean that there will be a procedure..begin..end block in DD04 to handle the job. 'Method designator' seems to be newspeak for 'procedure declaration')

Are you happy with 'passing parameters to procedures (methods) and functions'? If not, it will repay study!! It is common in modern languages, and powerful. The Delphi system is inherited from Pascal, so either a Delphi or a Pascal text will help you. I'll try to write it all up in a lower level Pascal tutorial someday. Here I'll just say that the ConsiderNewScore method is passed a score and a name. In an enhanced version, it should return information on whether the score made it into the high scores table, but that won't be in this demo.

Just after...
var
DD05f1: TDD05f1;
DD04f1: TDD04f1;

...add...
Right,Wrong:tstringlist;
GameScore:byte;

The only termination tasks which seems necessary for DD04 is dealt with later. When DD05 is terminated, it cleans up after itself, including removing the DD04 form from the screen. Isn't Delphi wonderful?! (Try reading a book about working directly with Windows if you didn't respond 'Halelujiah' to my recent question.)(I am currently not certain that I do all I should to free memory I've made demands on for my program. If any expert reading this sees problems to be dealt with, please don't hesitate to let me know. I'm relying on Delphi to release allocated memory for me during application.terminate.)

Oh yes... I suppose I ought to plan the 'game' for DD05 which will give rise to high scores to go in the table.

It will....
During init: Fill two string lists from two datafiles, C:\DD05r.dat and C:\DD05w.dat. Both can be created with Notepad. (A string list is like an array of strings.)

DD05r.dat will have some RIGHT sentences, e.g. 2+2=4, Paris is the capital of France, Too many boys are not easy to keep quiet.

DD05w.Dat will have some WRONG sentences, e.g. 2+2=5, Berlin is the capital of France, To many boys are not easy too keep quiet.

Go ahead and create those two files now, one sentence per line, but as it is DD04 which is active on your desktop, we'll leave extending DD05 to use those files until later. Be sure that there is nothing in the files after the last character of the last line. (Spaces and carriage returns have a way of accumulating there.)

The game puts a pair of sentences on the screen... one right, one wrong. The user has to identify the one that is okay. There's one point for each correct answer. After 20 pairs of sentences, the round is over. The whole thing can be improved by adding points for answering quickly, but that's beyond the scope of this tutorial... and I offer something like that 'commercially', so if you want it visit Sheepdog Software (tm)

So... we have a plan, as Baldrick would say. (Don't worry if you don't know who he is.) Now to implement it.

Besides showing the necessary code, I'll also show you how I build things up gradually, which gets me to the end faster.

In DD04's FormCreate procedure, start by adding...

(*Load previous high scores, or start datafile if absent*)
if fileexists('C:\DD04.dat') then begin
end (*no ; here*)
else (*there was no old DD04.dat*)
begin
end;(*deal with no previous DD04*)
(*End of 'Load previous..*)


Run the program. It won't do much, but if you've made typos, you haven't far to look for them.

At this stage I was stuck... a StringGrid is a component. Is there an easy way to load data from a file to a StringGrid? I typed 'stringgrid' (without the apostrophes) into my source file and pressed f1. (You don't even have to do a backspace to put the cursor in the word.) Up popped all sorts of useful information about StringGrids. Infortunately, there isn't a built-in LoadStringGrid or SaveStringGrid, so we're going to have to do it by hand....

To be able to use the StringGrid component, we're going to have to store the scores as a string, even though we will be using them as numbers. A minor pain, but worthwhile to make take advantage of the built-in component.

By the way, we could set up a parallel two dimensional array of strings to hold the score/ name data (var ScoresNames:array [1..2,1..10] of string; (though I would prefer the less obvious array [0..1,0..9] of string, which can have advantages... irrelevant here)) It would work, could be used. However, for our needs it is overkill. We can look into the StringGrid any time we need data.

At the moment, we don't have a C:\DD04.dat file, so let's start by writing the part of the program which will create a blank one when necessary....

First, get rid of the...
dd04f1.stringgrid1.cells[1,1]:='Works!'; just after...
stringgrid1.cells[1,0]:=' Name';

Then, to fill the StringGrid with some initial values, just after the...
else (*there was no old DD04.dat*)

...add...

for c1:=1 to 10 do
begin
StringGrid1.cells[0,c1]:=' 0';
StringGrid1.cells[1,c1]:='--empty--';
end;(*fill with starting values*)

Run the program again. Using 10 as I have in the for c1:=... line is a terrible thing to do, by the way. It 'locks in' an assumption that the table has ten entries. It would be much better to have a global constant somewhere, and use that whenever needing to say 'for as many rows as there are in the table.'

Now we're going to build in the check that it is okay to write to the disc, and set the relevant flag.

After the...
DD04f1: TDD04f1;

...in the var section for the whole of DD04, add...
OkToWriteToDisc:boolean;

After the...
end;(*fill with starting values*)

... add ...
OkToWriteToDisc:=false;
if MessageDlg('Is it okay to write C:\DD04.dat to your disc '
+'to store the high scores in? You do not have a file of '
+'that name at the moment. You can say ''no'', but if you '
+'do high scores won''t be remembered from day to day.',
mtConfirmation, [mbYes, mbNo], 0) = mrYes then
OkToWriteToDisc:=true;

Get that running okay, then after it, add...
if OkToWriteToDisc then SaveHighScores;

... and do the following to create the SaveHighScores method...

After...
TDD04f1 = class(TForm)
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);

... add ...
procedure SaveHighScores;

and after...

implementation
{$R *.DFM}

... add ...
procedure TDD04f1.SaveHighScores;
begin
{..}
end;

... and re-run the program to catch typos. (You must have SOMETHING between the begin and end, even if only {..}, or else Delphi will assume that the procedure shell is a useless scrap and remove it and the earlier forward declaration.)

Once the typos are gone, you can flesh out SaveHighScores, as follows. Remember when testing that you must continually delete any DD04.dat to re-test the program's ability to create one. (Alternatively, you could temproarily disable the If FileExists... test. Rewrite will overwrite an existing file without asking you if it should.)


procedure TDD04f1.SaveHighScores;
var df:textfile;
c1:byte;
begin
assignfile(df,'C:\DD04.dat');
rewrite(df);
for c1:=1 to 10 do begin
writeln(df,StringGrid1.cells[0,c1]);
writeln(df,StringGrid1.cells[1,c1]);
end;
closefile(df);
end;


Once you have that working, you should be able to load the resulting DD04.dat into Notepad and see something sensible. (Hint: Associate .dat with Notepad, so you can just double click on DD04.dat to view it with Notepad.)

Now you can insert the code to read old DD04.dat inforamtion into the StringGrid. You already have...

if fileexists('C:\DD04.dat') then begin
end (*no ; here*)

Just after...
procedure TDD04f1.FormCreate(Sender: TObject);
var c1:byte;

... add ...
df:textfile;
sTmp:string;

and after...
if fileexists('C:\DD04.dat') then begin

... add ...

assignfile(df,'C:\DD04.dat');
reset(df);
for c1:=1 to 10 do begin (*fetch old data*) readln(df,sTmp); stringgrid1.cells[0,c1]:=sTmp; readln(df,sTmp); stringgrid1.cells[1,c1]:=sTmp; end; (*fetch old data*) OkToWriteToDisc:=false; if MessageDlg('Is it okay to write C:\DD04.dat to your disc ' +'to store the high scores in? You have a file of ' +'that name at the moment. You can say ''no'', but if you ' +'do, today''s new high scores won''t be remembered.', mtConfirmation, [mbYes, mbNo], 0) = mrYes then OkToWriteToDisc:=true;


The story continues in part three..... click here to go on to part three.

   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: please visit my freeware and shareware page?

Editor's freeware, shareware page.


Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....