tags:

views:

703

answers:

7

It may seem a little newbie, but I really have got problem with it. I have a form (not the main one)for getting many different data from the user and I want to pass it to a manager class for creating an object with these. The problem is that I can't have this class to use the other unit (getting circle uses) and also it doesn't have access to the manager class instance (which is in main form).

So, what shall I do? I've already considered using public variable but I have a bad feeling about it (regarding OOD patterns).

Any ideas?

thanks in advance.

+1  A: 

To solve circular refrence error, you use that unit in implementation section.

implementation
{$R *.DFM}

Uses <Your Unit>;

end. 
Bharat
I know this method, but I look forward to other possibilities.Thanks anyway.
Flom Enol
+3  A: 

The "manager class" shouldn't be in either form's unit, but in its own. By separating GUI code from bussiness logic you avoid problems such like this.

Azarien
+3  A: 

Hi.

My suggestion is to decouple data from the GUI because this is causing your problem. If you have a form which gathers data from the user then you should distinguish the data from the form(TForm).

For example, let's assume that you have some instance of TForm and a form on which is build from three fields, user name, age and location. You want from user to enter those tree things but when the user closes the form, you should pass inserted data to some object. Form closes, it is freed but the object persist. Then you pass this object to your manager object.

Simple example:

This is your record which will hold the data

type
  TGatheredData = record
    Name: String[40];
    Age: Byte;
    Location: String[40];
end;

Your TForm1 might have an aditional constructor:

constructor TForm1.Create(AOwner: TComponent; var GatheredData: TGatheredData );
begin
  inherited Create(AOwner);
  FGatheredData := GatheredData;
  //you may want to deserialize GatheredData here and show the data in your form controls
end;

You call it, pass GatheredData and then your are showing your form.

Next, when closing form, you pick upd the data from the form controls.

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Self.ModalResult = mrOk then
  begin
    //serialize your object
    FGatheredData.Name := '';//name taken from control f.e. TEdit
    FGatheredData.Age := '';//name taken from control f.e. TSpinButton
    FGatheredData.Location := '';//name taken from control f.e. TEdit
  end;
end;

Having this record of data, you may now pass it in the same manner to your Manager object. You decoupled data from GUI in this way, and you may easly plugin in your record to a number of different forms.

Just remember to declare your record type in external unit and use that unit in your manager unit and forms unit.

Hope this helps a little.

Wodzu
Thank you so much for your detailed and great answer.Maybe I've asked the question wrong or didn't give good details. I have decoupled code (my manager class is in a different unit) and has nothing to do with the form.The problem is that, even if I use a record for storing, how to pass it to manager? It is in the form class and the form has no access to the manager class (I prefer it doesn't!). So, how do you pass it along?I want the manager to have access to various forms and not visa-versa.
Flom Enol
Maybe I do not understand something. You should have atleast three units. 1 - which contains only a definition for the data, 2 - a unit hich holds your Manager class which takes data as a parameter and 3 - a form which takes data as a parameter. What exactly your manager is doing?
Wodzu
It is exactly like that. I have also a main form which is different from the one that receives data. If you know the OOD patterns, my manager is kinda director for a UI which manages the data between the forms and make changes.It is not important what exactly manager does. I ask my question in a different way:Is there a way to pass the data to an instance of a class without having direct access to it (without having the "uses..." in interface)?
Flom Enol
Sure thing, in a number of ways. You can create such method: Manager.PassData(var Data: TData; SomeObject: TSomeClass); in PassData method you could check if SomeObject implements a specific inteface, or if it have specific method and run this method via RTTI. Need a more elaborate example?
Wodzu
+1 for your interest in my not-so-good question!Can you please explain what is RTTI?
Flom Enol
RTTI is an built-in mechanism which allows you to discover information about the object at runtime (when program works). So, you do not have to include a unit to the uses clause in your manager unit.
Wodzu
hmm... it is interesting. I haven't heard of it before but it seems a good idea to use. I try to use it and see what happens.Again thank you so much for your answer and of course your useful comments!
Flom Enol
No problem at all:) There is a lot of information about the RTTI on the web. It is a bit advanced topic if you are working with Delphi version prior to 2010 but it is definitely worth to try of. If you will have problems, just ask :)
Wodzu
+1  A: 

[Edit: I originally put this answer in a comment, but decided to move it out into full answer. TDatamodules are too important and too common in Delphi not to emphasize them and they provide built-in easy-to-use means of seperating gui from logic and data.]

Other people have given good comments about decoupling gui from the logic and data. Surprisingly, I don't think anybody has mentioned that in Delphi TDatamodules are one main means of doing this. You put your data and logic on the Datamodule, then have both forms "use" the Datamodule to get access to its data and methods. Here is brief intro: http://delphi.about.com/od/database/l/aa101601a.htm

Both of your forms (and other forms) can access datasets or other data/datastructures that are located on/in a Datamodule unit. There should be an easy to find sample project out there illustrating the setup, since it is (or at least was) the standard way to construct Delphi apps.

Herbert Sitz
+1 for your good solution. It seems easy to setup and a good substitute for using big, hard-to-understand databases.It is really a good way to separate the date from other components.Thanks.
Flom Enol
@Fiom -- Good to hear. Just to be clear, use of the Datamodule itself has nothing to do with how your data is stored. Your datamodule could include components that access data in a database, datastructures with form components like TStringList, or datastructures that are created entirely in code. Whatever you're doing, it's a good idea to put shared data in a datamodule and have the datamodule unit be used by all the forms that need access to the data.
Herbert Sitz
A: 

Having this 3 units: FormMain FormEdit UnitMyClass

You create your object in FormMain and pass it to the FormEdit in a function like:

class function FormEdit.EditMyObject(AObject: TMyClass): boolean;

this function will showModal the form. The form will do the changes to the object, and finally return True if user pressed OK.

As you can see in UnitMyClass there is no reference to FormMain or FormEdit

Daniel Luyo
+2  A: 

i agree the solution of Wodzu, just added a windows message to send notification if there are change in data..

like emplementing a custom message My_MSG = WM_USER + 3 then using the SendMessage or PostMessage

XBasic3000
+1  A: 

If I understand your question properly then you want the manager to manage the forms (not the forms to access the manger). Right? You can't close the Main Form as if you do you close the application but you CAN Hide it. (unless you create a console app). But it poses a nice little problem :)

So... Splash form (Main Form) is:

. . .

uses
   ManagerU;

type
  TFormSplash = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Manager: TManager;
  end;

var
  FormSplash: TFormSplash;

implementation


{$R *.dfm}

procedure TFormSplash.FormCreate(Sender: TObject);
begin
  Manager := TManager.Create;
end;

procedure TFormSplash.FormDestroy(Sender: TObject);
begin
  Manager.Free;
end;

procedure TFormSplash.FormPaint(Sender: TObject);
begin
  if Visible then
  begin
    Manager.GetData(Self);
    Hide;
    Manager.DoDataStuff;
    Close;
  end;
end;

end.

DaaObject is:

unit DataObjectU;

interface

uses classes;

type TDataObject = class(TObject)
  Data: string;
end;

implementation

end.

Manager is:

interface

uses DataObjectU;

type
  TManager = Class(Tobject)
    MyData: TDataObject;
    constructor Create; virtual;
    destructor Destroy; override;
    procedure GetData(OwnerForm: TForm);
    procedure DoDataStuff;
  end;

implementation

uses DataFormU;

{ TManager }

constructor TManager.Create;
begin
  inherited Create;
  MyData := TDataObject.Create;
end;


destructor TManager.Destroy;
begin
  MyData.Free;
  inherited;
end;

procedure TManager.DoDataStuff;
begin
  // do stuff with data here
end;

procedure TManager.GetData(OwnerForm: TForm);
var
  MyDataForm: TDataForm;
begin
  MyDataForm := TDataForm.Create(OwnerForm);
  try
    if MyDataForm.ShowModal = mrOK then
    begin
     MyData.Data := MyDataForm.Data;
    end;
  finally
    MyDataForm.Free;
  end;
end;

end.

The Dataform is:

type
  TDataForm = class(TForm)
    btnOK: TButton;
    procedure btnOKClick(Sender: TObject);
  private
    function GetData: String;
    { Private declarations }
  public
    { Public declarations }
    MyData: TDataObject;
    property Data: String read GetData;
  end;

var
  DataForm: TDataForm;

implementation

{$R *.dfm}

procedure TDataForm.btnOKClick(Sender: TObject);
begin
  MyData := TDataObject.Create;
  ModalResult := mrOk;
end;

function TDataForm.GetData: String;
begin
  Result := MyData.Data;
end;

You will need to fill in the rest of the unit code and free some stuff but essentially this:

Start Program (Creates Splash)

Splash Creates the manager calls it to get data from the dataform then hides itself

calls manager to manage the data

when manager is done it then closes.

There is no other way to shut it down now except through task manager!)

Tim

Despatcher