Mahm00d
Mahm00d

Reputation: 3921

How to pass data between forms in Delphi?

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).

Upvotes: 6

Views: 19808

Answers (7)

David
David

Reputation: 121

FWIW, I did a whole presentation on this topic in a CodeRage 9 video. It can be seen here:

https://youtu.be/qqKx8fQTTfI

Upvotes: 0

Wodzu
Wodzu

Reputation: 6979

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, which is built from three fields: username, age and location. You want the user to enter those three things, but when the user closes the form, you should pass this inserted data onto 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.

Upvotes: 10

Despatcher
Despatcher

Reputation: 1725

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

Upvotes: 2

Daniel Luyo
Daniel Luyo

Reputation: 1336

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

Upvotes: 0

Herbert Sitz
Herbert Sitz

Reputation: 22226

[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.

Upvotes: 3

Azarien
Azarien

Reputation: 201

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.

Upvotes: 4

Bharat
Bharat

Reputation: 6866

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

implementation
{$R *.DFM}

Uses <Your Unit>;

end. 

Upvotes: 2

Related Questions