I gave a previous answer but after some thinking and scratching I found a better way to solve the circular reference problem. Here my first unit who need a pointer on an object TB define in unit B.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, b, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FoB: TB;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
FoB := TB.Create(Self);
showmessage(FoB.owner.name);
end;
end.
Here the code of the Unit B where TB has a pointer on TForm1.
unit B;
interface
Uses
dialogs, Forms;
type
TForm1 = class(TForm);
TB = class
private
FaOwner: TForm1;
public
constructor Create(aOwner: TForm);
property owner: TForm1 read FaOwner;
end;
implementation
uses unit1;
Constructor TB.create(aOwner: TForm);
Begin
FaOwner := TForm1(aOwner);
FaOwner.Left := 500;
End;//Constructor
end.
And here why it compiles. First Unit B declare the use of Unit1 in the implementation section. Resolving immediately the circular reference unit between Unit1 et Unit B.
But to allow Delphi to compile, I need to give him something to chew on the declaration of FaOwner: TForm1. So, I add stub class name TForm1 who match the declaration of TForm1 in Unit1.
Next, when come the time to call the constructor, TForm1 is able to pass itself has the parameter. In the constructor code, I need to typecast the aOwner parameter to Unit1.TForm1. And voilà, FaOwner his set to point on my form.
Now, if the class TB need to use FaOwner internally, I don't need to typecast it every time
to Unit1.TForm1 because both declaration are the same. Note that you could set the declaration of to constructor to
Constructor TB.create(aOwner: TForm1);
but when TForm1 will call the constructor and pass itself has a parameter, you will need to typecast it has b.TForm1. Otherwise Delphi will throw an error telling that both TForm1 are not compatible. So each time you call the TB.constructor you will need to typecast to the appropriate TForm1. The first solution, using a common ancestor, his better. Write the typecast once and forget it.
After I posted it, I realized that I made a mistake telling that both TForm1 were identical. They are not Unit1.TForm1 has components and methods that are unknown to B.TForm1. Has long TB doesn't need to use them or just need to use the commonality given by TForm you're okay. If you need to call something particular to UNit1.TForm1 from TB, you will need to typecast it to Unit1.TForm1.
I try it and test it with Delphi 2010 and it compiled and worked.
Hope it will help and spare you some headache.