PTRUSCOTT
asked on
How to communicate date from a Child Form to a Parent Form in Delphi 7
Dear Experts,
When I was programming in Visual C++ I would sometimes use very bad techniques to move data from a child dialog box to the window that calls it. Let's suppose I have a dialog box that shows a list of items in a single column grid. When the user double clicks on an item, that item is selected and the dialog box closes, returning control to the parent window.
In C++ I could get text from an edit field with a command like this:
form_edit.GetWindowText(an swer_strin g,sizeof(a nswer_stri ng));
In this case the variable answer_string would be a global variable so the user's selection would be available, through the global variable, to the parent window that called the dialog box.
I realize it is considered bad programming to have too many global variables. What is the proper way to transfer data in this situation? After double-clicking my dialog box shuts down so the user's answer has to be stored somewhere before the dialog box's memory is freed.
How do I cleanly shut down the dialog box? In C++, I could use this command:
CDialog::OnOK();
What is the proper way to do this in Delphi?
Sincerely,
Phil Truscott
When I was programming in Visual C++ I would sometimes use very bad techniques to move data from a child dialog box to the window that calls it. Let's suppose I have a dialog box that shows a list of items in a single column grid. When the user double clicks on an item, that item is selected and the dialog box closes, returning control to the parent window.
In C++ I could get text from an edit field with a command like this:
form_edit.GetWindowText(an
In this case the variable answer_string would be a global variable so the user's selection would be available, through the global variable, to the parent window that called the dialog box.
I realize it is considered bad programming to have too many global variables. What is the proper way to transfer data in this situation? After double-clicking my dialog box shuts down so the user's answer has to be stored somewhere before the dialog box's memory is freed.
How do I cleanly shut down the dialog box? In C++, I could use this command:
CDialog::OnOK();
What is the proper way to do this in Delphi?
Sincerely,
Phil Truscott
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
use it like this from a other form:
procedure TFormX.btnSelectMailsClick (Sender: TObject);
begin
Memo1.Lines.Delimiter := ';';
Memo1.Lines.DelimitedText := SelectEmails(Self);
end;
procedure TFormX.btnSelectMailsClick
begin
Memo1.Lines.Delimiter := ';';
Memo1.Lines.DelimitedText := SelectEmails(Self);
end;
I even wrote an article on this approach:
https://www.experts-exchange.com/articles/Programming/Languages/Pascal/Delphi/How-to-keep-your-main-form-uses-clauses-more-compact-with-the-blackbox-technique.html
https://www.experts-exchange.com/articles/Programming/Languages/Pascal/Delphi/How-to-keep-your-main-form-uses-clauses-more-compact-with-the-blackbox-technique.html
I have an approach that I learned from a Certified Delphi Instructor Instructor (i.e. he taught the guys who taught Delphi ;-).
In the unit of forms (other than the mainform), I have a completely public function. (For instance, I have a standard AboutBox form that I use and the function that it has for displaying the AboutBox is called TellAbout.) This function (which, as in the case of my AboutBox, can be over loaded to allow various options), expects to receive certain paramaters which may be passed as constants and/or , as variables. This function then creates the form, fills in any appropriate variables specific to the form, and shows the form (usually, I use ShowModal). When the user closes the form, the function will extract any information from the form that should be returned to the calling form and then releases the form. Also, I generally use a TModalResult for the return value from the function and that means I can use the result from the user closing the form, if aI want to.
By putting this function in the form's unit, you don't have to code the set up, show, and data extraction every place that you want to call the form . . . you just do another function call. It also means that the form/unit is not tightly coupled to the other units in the application and, therefore, it contributes to code reusability (both within and between applications). Finally, since you can over load the function, you can add new parameters for specific applications/calls and, by not requiring them in the old function call (which you leave in the unit), you don't break the contract with any existing applications/calls.
Effectively, you can pass the parameters to the function and receive results back in the parameters and easily solve your problem.
If you want, I can provide you with my AboutBox as an example of the process (it has 2 variations on the TellAbout function).
In the unit of forms (other than the mainform), I have a completely public function. (For instance, I have a standard AboutBox form that I use and the function that it has for displaying the AboutBox is called TellAbout.) This function (which, as in the case of my AboutBox, can be over loaded to allow various options), expects to receive certain paramaters which may be passed as constants and/or , as variables. This function then creates the form, fills in any appropriate variables specific to the form, and shows the form (usually, I use ShowModal). When the user closes the form, the function will extract any information from the form that should be returned to the calling form and then releases the form. Also, I generally use a TModalResult for the return value from the function and that means I can use the result from the user closing the form, if aI want to.
By putting this function in the form's unit, you don't have to code the set up, show, and data extraction every place that you want to call the form . . . you just do another function call. It also means that the form/unit is not tightly coupled to the other units in the application and, therefore, it contributes to code reusability (both within and between applications). Finally, since you can over load the function, you can add new parameters for specific applications/calls and, by not requiring them in the old function call (which you leave in the unit), you don't break the contract with any existing applications/calls.
Effectively, you can pass the parameters to the function and receive results back in the parameters and easily solve your problem.
If you want, I can provide you with my AboutBox as an example of the process (it has 2 variations on the TellAbout function).
8080_Diver,
i don't often consider the overloading but using my previous sample ...
this instructor ... looks like he does the same as i do
i don't often consider the overloading but using my previous sample ...
this instructor ... looks like he does the same as i do
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, StdCtrls, Buttons, ExtCtrls, Grids, DBGrids, DBClient;
type
TfrmSelectEmails = class(TForm)
cds: TClientDataSet;
ds: TDataSource;
grid: TDBGrid;
pnl: TPanel;
btnCancel: TBitBtn;
btnSelectAll: TBitBtn;
btnOk: TBitBtn;
cdsName: TStringField;
cdsemail: TStringField;
procedure btnSelectAllClick(Sender: TObject);
private
function GetSelectedEmails: string;
procedure SelectAll;
procedure RandomData;
public
constructor Create(AOwner: TComponent); override;
property SelectedEmails: string read GetSelectedEmails;
end;
var
frmSelectEmails: TfrmSelectEmails;
function SelectEmails(AOwner: TComponent): string; overload;
procedure SelectEmails(AOwner: TComponent; var Emails: string); overload;
implementation
{$R *.dfm}
function SelectEmails(AOwner: TComponent): string;
begin
Result := '';
frmSelectEmails := TfrmSelectEmails.Create(AOwner);
try
if frmSelectEmails.ShowModal = mrOk then
Result := frmSelectEmails.SelectedEmails;
finally
FreeAndNil(frmSelectEmails);
end;
end;
procedure SelectEmails(AOwner: TComponent; var Emails: string);
begin
Emails := SelectEmails(AOwner);
end;
{ TfrmSelectEmails }
procedure TfrmSelectEmails.btnSelectAllClick(Sender: TObject);
begin
SelectAll;
end;
constructor TfrmSelectEmails.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
cds.Active := True;
RandomData;
end;
function TfrmSelectEmails.GetSelectedEmails: string;
const Delimiter = ';';
var b: TBookmark;
begin
Result := '';
with grid.DataSource.DataSet do
begin
DisableControls;
try
b := GetBookmark;
try
First;
while not Eof do
begin
if grid.SelectedRows.CurrentRowSelected then
Result := Result + FieldByName('email').AsString + Delimiter;
Next;
end;
finally
GotoBookmark(b);
FreeBookmark(b);
end;
finally
EnableControls;
end;
end;
if Result <> '' then
Delete(Result, Length(Result) - Length(Delimiter) + 1, Length(Delimiter));
end;
procedure TfrmSelectEmails.RandomData;
const MaxRandom = 30;
var I: Integer;
begin
for I := 0 to Random(MaxRandom) do
cds.InsertRecord([Format('name ', [i]), Format('email%d@abc.com', [i])]);
end;
procedure TfrmSelectEmails.SelectAll;
var b: TBookmark;
begin
grid.SelectedRows.Clear;
with grid.DataSource.DataSet do
begin
DisableControls;
try
b := GetBookmark;
try
First;
while not Eof do
begin
grid.SelectedRows.CurrentRowSelected := True;
Next;
end;
finally
GotoBookmark(b);
FreeBookmark(b);
end;
finally
EnableControls;
end;
end;
end;
end.
--- dfm ---
object frmSelectEmails: TfrmSelectEmails
Left = 0
Top = 0
Caption = 'Select emails'
ClientHeight = 451
ClientWidth = 692
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object grid: TDBGrid
Left = 0
Top = 0
Width = 692
Height = 392
Align = alClient
DataSource = ds
Options = [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit, dgMultiSelect]
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object pnl: TPanel
Left = 0
Top = 392
Width = 692
Height = 59
Align = alBottom
TabOrder = 1
object btnCancel: TBitBtn
Left = 568
Top = 16
Width = 105
Height = 25
DoubleBuffered = True
Kind = bkCancel
ParentDoubleBuffered = False
TabOrder = 0
end
object btnSelectAll: TBitBtn
Left = 16
Top = 16
Width = 105
Height = 25
Caption = 'Select All'
DoubleBuffered = True
Glyph.Data = {
F2010000424DF201000000000000760000002800000024000000130000000100
0400000000007C01000000000000000000001000000000000000000000000000
80000080000000808000800000008000800080800000C0C0C000808080000000
FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333334433333
3333333333388F3333333333000033334224333333333333338338F333333333
0000333422224333333333333833338F33333333000033422222243333333333
83333338F3333333000034222A22224333333338F33F33338F33333300003222
A2A2224333333338F383F3338F33333300003A2A222A222433333338F8333F33
38F33333000034A22222A22243333338833333F3338F333300004222A2222A22
2433338F338F333F3338F3330000222A3A2224A22243338F3838F338F3338F33
0000A2A333A2224A2224338F83338F338F3338F300003A33333A2224A2224338
333338F338F3338F000033333333A2224A2243333333338F338F338F00003333
33333A2224A2233333333338F338F83300003333333333A2224A333333333333
8F338F33000033333333333A222433333333333338F338F30000333333333333
A224333333333333338F38F300003333333333333A223333333333333338F8F3
000033333333333333A3333333333333333383330000}
NumGlyphs = 2
ParentDoubleBuffered = False
TabOrder = 1
OnClick = btnSelectAllClick
end
object btnOk: TBitBtn
Left = 440
Top = 16
Width = 105
Height = 25
DoubleBuffered = True
Kind = bkOK
ParentDoubleBuffered = False
TabOrder = 2
end
end
object cds: TClientDataSet
Active = True
Aggregates = <>
Params = <>
Left = 576
Top = 40
Data = {
4D0000009619E0BD0100000018000000020000000000030000004D00044E616D
65010049000000010005574944544802000200640005656D61696C0200490000
000100055749445448020002002C010000}
object cdsName: TStringField
FieldName = 'Name'
Size = 100
end
object cdsemail: TStringField
FieldName = 'email'
Size = 300
end
end
object ds: TDataSource
DataSet = cds
Left = 512
Top = 40
end
end
ow i see you need a date ... i thought you meant data
you could do this with a boolean and a TDateTime type
and TDateTime
you could do this with a boolean and a TDateTime type
and TDateTime
unit uSelectDate;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Buttons, ExtCtrls;
type
TfrmDatePick = class(TForm)
pnlButtons: TPanel;
btnOk: TBitBtn;
btnCancel: TBitBtn;
mcPick: TMonthCalendar;
end;
var
frmDatePick: TfrmDatePick;
function SelectDate(AOwner: TComponent; var ADate: TDateTime): Boolean;
implementation
{$R *.dfm}
function SelectDate(AOwner: TComponent; var ADate: TDateTime): Boolean;
var frm: TfrmDatePick;
begin
Result := False;
frm := TfrmDatePick.Create(AOwner);
try
if frm.ShowModal = mrOk then
begin
Result := True;
ADate := frm.mcPick.Date;
end;
finally
FreeAndNil(frm);
end;
end;
end.
is it only 1 value ? -> use a function
is it more 1 value, with a check -> use a function with a out parameter
is it more than 1 value -> use a TStrings or Dataset
this is a sample of returning the selected email adresses in a string field
you could get the separate emails out again using the delimitedtext from TStrings
Open in new window