[EMAIL PROTECTED] wrote:
> 1. I have a piece of code that is killing me:
> 
> I want to create a MDI application.
> I have a main form that automatically creates a new child form called 
> 'Project Manager'. It can't be close, only minimized. It is 
> automatically destroyed by the application when it terminates.
> I use this 'Project manager' to create other (one or more) child forms 
> called Grids. I should be able to close those forms (caFree) at any time 
> or I can leave them open and close the application to close them for me 
> when it finish. The question is: why I can't pass *nil *as parameter 
> when I create these forms?
> 
> 
> procedure TFormManager.Start;
> begin
>   with TFormGrid.Create(*nil*) DO                             <<< if I 
> pass _Application_ or _Self_, it works!
>      begin
>         ...some code here;
>      end;
> end;
> 
> procedure TFormGrid.FormClose(Sender: TObject; var Action: TCloseAction);
> begin 
>   Action:= caFree;
> end;
> 
> I think until here everything is right. Right?

Well, except for that use of a "with" statement, in which the compiler 
could be resolving names to values different from what you expected, 
yes, what you've shown looks fine.

> When I close the whole application and one or more of these Grid forms 
> are open, it crashes in this procedure:
> 
> 
> procedure TMainForm.FormResize(Sender: TObject);       { <- this is 
> supposed to keep the child forms at the same width with the main form 
> when the user resize the interface }

Maximized windows will do that automatically.

If you really only want to change the width, and not the height, you can 
also try using the Anchors property.

> VAR i: Integer;
> begin                                                           
>  for I:= MDIChildCount-1 DownTo 0 DO
>   begin
>    if (MDIChildren[i] is TFrmGrid) then
>    if (MDIChildren[i] as TFrmGrid).HandleAllocated

You can write that more directly as this:

if (MDIChildren[i] is TFrmGrid) and 
TFrmGrid(MDIChildren[i]).HandleAllocated then

Since everything is guarded by the "is" test, you don't need to use the 
"as" type-casting everywhere. All you're doing is re-checking a type 
cast that you've already checked. However, you really don't need to do 
any type-casting at all. All the properties you're accessing belong to 
the TForm class, which is the type that MDIChildren will return anyway. 
You only need to type-cast if you need to access a property that's 
introduced in TFrmGrid.

Furthermore, when you have two nested conditionals, you can combine them 
with "and" into one conditional statement. The second expression is not 
evaluated if the first is False.

>    then
>      begin
>       if  ((MDIChildren[i] as TFrmGrid).WindowState= wsNormal)       
> <<<< *CRASH HERE*. Exception class C0000005 with message 'access 
> violation at 0x0055bc6b: read of address 0x00000000'.

Turn on the "debug DCUs" compiler option. When the debugger tells you 
about the exception, does it still crash there, or does it occur 
somewhere in the VCL source code? It should be the latter, which should 
provide a more specific source of the problem. Somewhere, your program 
is dereferencing a nil pointer.

>       then
>         begin
>          (MDIChildren[i] as TFrmGrid).Left:= 0;
>          (MDIChildren[i] as TFrmGrid).Width:= MainForm.ClientWidth-4;

What's the magic number 4? Why not 5 or 36?

Don't reference MainForm here. It's a global variable, which refers to 
one particular instance of TMainForm. That instance isn't _necessarily_ 
the same as the instance whose FormResize method is running at this 
point. You're accessing the current form's MDIChildren property, but 
possibly some other form's ClientWidth property. Do this instead:

MDIChilren[i].Width := ClientWidth - 4;

Finally, accessing the MDIChildren property so often is a little 
wasteful. TForm implements its MDIChildren property by looping through 
the Screen.Forms array and counting all the forms that have the 
FormStyle property set to fsMDIChild. When it reaches "i", it returns. 
You're forcing the program to run that loop five times for each MDI 
child which you could do it in one by using a local variable:

var
   Child: TForm;

for i := 0 to Pred(MDIChildCount) do begin
   Child := MDIChildren[i];
   if Child is TFrmGrid and Child.HandleAllocated then begin
     // ...
   end;
end;

>         end;
>      end;
>    end;
> end; 
> 
> 
> I suppose is something with those 'dangling' pointers, so my next 
> question is:
> 
> 
> 2. Why Delphi does only a 'Free' on a object/form when it automatically 
> destroy it instead a FreeAndNil?

What, exactly, should get set to nil? How do you know? How should Delphi 
know?

Have you looked at how FreeAndNil actually works?

http://www.cs.wisc.edu/~rkennedy/freeandnil

> The application works very well with Self or Application instead of Nil 
> but the documentation says that Application is not recommended because 
> it is slow and Self should be used OLNY when the  Create belongs to the 
> main form (and in my case I don't create those Grids in the main form). 
> So the only answer is NIL.

Hardly. You can let the owner be anything you want. Nil, Self, and 
Application are only the most common values. Conceptually, which form 
owns those grids?

-- 
Rob
__________________________________________________
Delphi-Talk mailing list -> Delphi-Talk@elists.org
http://www.elists.org/mailman/listinfo/delphi-talk

Reply via email to