2006. február 14., kedd

Call another form and return multiple values


Problem/Question/Abstract:

I have a main form that will call a second form to use for searching on different criteria. How can I return multiple values retrieved in the second form to the main form?

Answer:

Solve 1:

Well, you need to add the second forms Unit to the first ones Uses clause, so you can call up the form. This gives you access to the forms controls and methods. It is usually bad design to access the forms controls from outside, since this tightly couples the outside code to the form. Any change you make to the form may require a change to the code accessing the form. So decouple them. One way to do that is to add properties (public section of the form) for each data item you may need to access from outside. This way the form controls how the data is fetched from the controls or internal fields of the form, usually via Set and Get methods for the properties. The form is then used like this (assuming it is not autocreated):

with TSearchform.create(application) do
try
  {... assign start values to the forms properties here, if required}
  if ShowModal = mrOK then
  begin
    {... read values from the form properties here}
  end
  else
    {... user aborted, take appropriate action}
finally
  free;
end;

This can be taken a step further to avoid the necessity for public properties in the first place (which exposes a kind of working contract to the outside world, which may reveal too much about the form or limit how you could modify it later too much). For that you create a non-visible class, derived from TPersistent, which holds the data items you need to transfer in and out of the form. The form is given overriden Assign and Assignto methods, which, when fed an instance of this data container, will copy the data between the form and the data container. Now all knowledge of how the forms handles the data is internal to the form, all the outside world needs to know is that it can assign a data container to it and vice versa:

datacontainer := TDataContainer.Create;
{... set up datacontainers data to fed into the form}
searchform := TSearchform.create(application);
try
  searchform.Assign(datacontainer);
  if searchform.ShowModal = mrOK then
    datacontainer.Assign(searchform)
  else
    {... user aborted, take appropriate action}
finally
  searchform.free
end;
{... use datacontainer if user entered data and free it when done}

The TDatacontainer class would reside in its own Unit, which is used by both form units.


Solve 2:

You have a variety of options:

1. Use "var" parameters:

procedure TForm2.DoSomething(var Return1, Return2, Return3: Integer);
begin
  Return1 := 1;
  Return2 := 2;
  Return3 := 3;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  X, Y, Z: Integer;
begin
  Form2.DoSomething(X, Y, Z);
  ShowMessage(IntToStr(X));
end;

2. Declare a record which aggregates your return values:

type
  TReturnRecord = record
    Value1: Integer;
    Value2: Integer;
    Value3: Integer;
  end;

function TForm2.DoSomething: TReturnRecord;
begin
  Result.Value1 := 1;
  Result.Value2 := 2;
  Result.Value3 := 3;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: TReturnRecord;
begin
  R := Form2.DoSomething;
  ShowMessage(IntToStr(R.Value1));
end;

3. Declare a class which aggregates your return values:

type
  TReturnClass = class
    Value1: Integer;
    Value2: Integer;
    Value3: Integer;
  end;

function TForm2.DoSomething(AReturn: TReturnClass);
begin
  AReturn.Value1 := 1;
  AReturn.Value2 := 2;
  AReturn.Value3 := 3;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: TReturnClass;
begin
  R := TReturnClass.Create;
  try
    Form2.DoSomething(R);
    ShowMessage(R.Value1);
  finally
    R.Free;
  end;
end;

Or you could use a list or array structure, or you could use an open array parameter.

Nincsenek megjegyzések:

Megjegyzés küldése