开发者

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


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.


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.


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


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


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

implementation
{$R *.DFM}

Uses <Your Unit>;

end. 


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


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

https://youtu.be/qqKx8fQTTfI

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜