> WHAT´S WRONG WITH THE FIRST ONCLOSE EVENT?
It isn't necessary to SHOUT.
You've got a big conceptual misunderstanding. You are assuming that an
object reference variable, such as Form1, and the Self identifier are
the same. They aren't. You've also assumed that Form1 could be used to
store multiple object references. It can't.
An object reference variable can only hold one or zero references to
an object. An object instance (represented within a class method by
Self) may have zero, one, or more than one object references scattered
throughout your code, and some of these references may be stored in
composite class instances, such as a TList, instead of simple
variables, such as Form1.
> Well it´s not a very good idea if I want to produce many child forms.
Your proposed solution, besides not compiling, wouldn't solve the
problem. What you need to do is implement a utility routine which
handles creating, managing, and freeing multiple child forms. You can
use a TList to easily store and manage multiple form instances.
--
Rick Rogers (TeamB) | Fenestra Technologies
>
>Hi,
>1)In the open form event have a mistake:
>
>Where you read
>
> procedure TForm1.BlaBlaClick(Sender: TObject);
> begin
> if not assigned(AAFRMItpr) then
> AAFRMItpr:= TAAFRMItpr.create(self);
> end;
>
>is actually
>
> procedure TForm1.OpenForm1Click(Sender: TObject);
> begin
> if not assigned(Form1) then
> Form1:= TForm1.create(self);
> end;
If you need to test whether Form1 already is shown, loop through the
Screen variable, scanning for the form, i.e.
FormReference := nil;
for i := 0 to Screen.FormCount-1 do
if Screen.Forms[i] is TForm1 then
begin
FormReference := Screen.Forms[i];
break;
end;
if FormReference = nil then
with TForm1.Create(Self) do
Show; { or whatever you want}
Do NOT keep a reference to this created form anywhere if you don't
have to. Freeing is no problem, as we have caFree:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
>2) To close the Form1, I use this event
>
> procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
> begin
> action := cafree;
> Form1 := nil;
> end;
>The problem to use this method is:
> -If I rename the Form1, I must to change the code;
Correct.
> -If I put this form in the Repository, for copy or inherited it later,I惻l have to change the code (if I copy) or to forget Form1 := nil and put in the Form1's descendent "Form1Son := nil" (if I inherit).
Correct.
>3) The Delphi helps says "Self can be used as a pointer to the instance through which the method is being called".
>
>If I debbug the FormClose event I惻l find:
> self.name := Form1
>
>So if I put self := nil, is the same of Form1 := nil, because the instance of the TForm1 is Form1.
Wrong. The (implicit) variable Self *contains* the same value as the
the (explicit) variable Form1. They are NOT the same, i.e. if you
modify Self (which is a NO-NO) you do not change the value in Form1.
Imagine that you have this code:
var
F1, F2: TForm;
begin
F1 := TForm.Create()
F2 := F1;
Now, what happens if you set F1 := nil? Nothing interesting: The form
still exists, you still have a reference to it in F2.
Replace F1 and F2 with Self and TForm1 and you see what I mean.
>4) If I write this close event,
>
> procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
> begin
> action := cafree;
> self := nil;
> end;
>
> It compiles, but if I debbug the code, the Delphi愀 debbugger jump the command, and don愒 consider self (or Form1) = nil.
I doubt that the debugger will skip Form1 := nil; I have no idea on
Self := nil since I never tried that.
>5) What愀 wrong with my code?
You need to understand the inner workings of Delphi a bit better.
> How can I solve the problem?
By being a careful developer and by never, ever (did I say never?)
referencing the automatically created variable for the form, e.g.
form1. I personally always and immediately discard of that thing when
I create a form.
What you are trying to do is good on the one hand: set that particular
variable to nil to immediately get an access violation if it is
referenced after the object has been discarded. But the way you want
to do it just does not work.
Your problem is that your a using a global variable to reference the
form:
TMyForm = class(TForm)
end;
var
MyForm: TMyForm;
implementation
MyForm := TMyForm.Create()
.ShowModal;
The better, more resource-friendly, less prone-to-errors way is to
TMyForm = class(TForm)
end;
{ discard!
var
MyForm: TMyForm; }
implementation
var MyFormInstance: TMyForm;
MyFormInstance := TMyForm.Create()
.ShowModal;
This way you will not run into a problem as MyFormInstance is local to
a given code segment and will never be reference by anything else
later.
This will only work, of course, if you do NOT auto-create forms, but
IMHO having forms auto-created by Delphi is a Bad Idea anyway.
It also is bad practice to reference (global) form variables from
within the class itself, i.e. code like
procedure TForm1.SomeStuff;
begin
Form1.SomeMore;
end;
is not good style. First of all you do not *need* the Form1 reference,
you use it just for clarity - and the right way would be to use
procedure TForm1.SomeStuff;
begin
Self.SomeMore;
end;
which is much clearer than the Form1 notation anyway.
To summarize: Never do modify Self. It does not have any (positive -
if at all) effect. There are always better, other ways of
accomplishing what you want to do.
--
mailto:Stefan.Hoffmeister (at) Uni-Passau.de
http://kakadu.rz.uni-passau.de/~w4hoff01/
PGP public key at homepage
Actually, I improved your soluction,
>>>>>>>>>>>>>>>>>>
FormReference := nil;
for i := 0 to Screen.FormCount-1 do
if Screen.Forms[i] is TForm1 then
begin
FormReference := Screen.Forms[i];
break;
end;
if FormReference = nil then
with TForm1.Create(Self) do
Show; { or whatever you want}
>>>>>>>>>>>>>>>>>>
creating the following method:
function TMainForm.IsFormActive(F:tform):boolean;
var
I:integer;
begin
result := false;
for I := 0 to screen.formcount-1 do
try
if screen.forms[I] is F.classtype then
begin
Result := true;
exit;
end;
except
end;
end;
Now I can do it:
procedure TMainForm.OpenFormClick(Sender: TObject);
begin
if not IsFormActive(Form1) then
Form1:= TForm1.create(self);
Form1.show;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
action := cafree;
end;
PS - I created an exception handler in this function, because the function raise an EAccessViolation exception.
I think it happens, because if *F* isn愒 already created, *F* don愒 have a classtype.