Author: Jonas Bilinkevicius
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):
1 with TSearchform.create(application) do
2 try
3 {... assign start values to the forms properties here, if required}
4 if ShowModal = mrOK then
5 begin
6 {... read values from the form properties here}
7 end
8 else
9 {... user aborted, take appropriate action}
10 finally
11 free;
12 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:
13 datacontainer := TDataContainer.Create;
14 {... set up datacontainers data to fed into the form}
15 searchform := TSearchform.create(application);
16 try
17 searchform.Assign(datacontainer);
18 if searchform.ShowModal = mrOK then
19 datacontainer.Assign(searchform)
20 else
21 {... user aborted, take appropriate action}
22 finally
23 searchform.free
24 end;
25 {... 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:
26 //1. Use "var" parameters:
27
28 procedure TForm2.DoSomething(var Return1, Return2, Return3: Integer);
29 begin
30 Return1 := 1;
31 Return2 := 2;
32 Return3 := 3;
33 end;
34
35 procedure TForm1.Button1Click(Sender: TObject);
36 var
37 X, Y, Z: Integer;
38 begin
39 Form2.DoSomething(X, Y, Z);
40 ShowMessage(IntToStr(X));
41 end;
42
43 //2. Declare a record which aggregates your return values:
44
45 type
46 TReturnRecord = record
47 Value1: Integer;
48 Value2: Integer;
49 Value3: Integer;
50 end;
51
52 function TForm2.DoSomething: TReturnRecord;
53 begin
54 Result.Value1 := 1;
55 Result.Value2 := 2;
56 Result.Value3 := 3;
57 end;
58
59 procedure TForm1.Button1Click(Sender: TObject);
60 var
61 R: TReturnRecord;
62 begin
63 R := Form2.DoSomething;
64 ShowMessage(IntToStr(R.Value1));
65 end;
66
67 //3. Declare a class which aggregates your return values:
68
69 type
70 TReturnClass = class
71 Value1: Integer;
72 Value2: Integer;
73 Value3: Integer;
74 end;
75
76 function TForm2.DoSomething(AReturn: TReturnClass);
77 begin
78 AReturn.Value1 := 1;
79 AReturn.Value2 := 2;
80 AReturn.Value3 := 3;
81 end;
82
83 procedure TForm1.Button1Click(Sender: TObject);
84 var
85 R: TReturnClass;
86 begin
87 R := TReturnClass.Create;
88 try
89 Form2.DoSomething(R);
90 ShowMessage(R.Value1);
91 finally
92 R.Free;
93 end;
94 end;
Or you could use a list or array structure, or you could use an open array parameter.
|