Jump to content
Ian Branch

Call to create a form to return a value?

Recommended Posts

Hi Team,

Given the following call   -    TCurrentCustJobsForm.Create(Nil).ShowModal;

Can this be made to return a value?  e.g.  iRecord :=   TCurrentCustJobsForm.Create(Nil).ShowModal; 

I figure if it will work I will have to set & return it somehow in the CurrentCustJobsForm.

Just hoping.

 

Regards,

Ian

Share this post


Link to post
Guest

You can implement your own method which will show the form modal and return a value after the form has closed.

 

There is no need to rewrite ShowModal.

 

But keep in mind, that the user can close the form without selecting any value (cancel) so your method should return nothing or the value.

Share this post


Link to post
type
  TCurrentCustJobsForm = class(TForm)
  ....
  public
    class function Execute(out Value: Integer): Boolean;
  end;

......

class function TCurrentCustJobsForm.Execute(out Value: Integer): Boolean;
var
  F: TCurrentCustJobsForm;
begin
  F := TCurrentCustJobsForm.Create(nil);
  try
    Result := IsPositiveResult(F.ShowModal);
    if Result then Value:= F.SomeFieldOrPropertyOrEtc;
  finally
    F.Free;
  end;

......

if TCurrentCustJobsForm.Execute(iRecord) then (* Value selected and valid *) else (* user closed the form without selecting anything *)

 

 

Edited by Alexander Elagin

Share this post


Link to post

Hi Alexander,

That looks exactly like what I am after.

I have implemented as follows..

In the CurrentCustJobForm:

    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnSelectClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    class function Execute(out Value: Integer): Boolean;
  end;

var
  CurrentCustJobsForm: TCurrentCustJobsForm;

implementation

uses JobticketsFrm, GlobalData;

{$R *.dfm}

class function TCurrentCustJobsForm.Execute(out Value: Integer): Boolean;
var
  F: TCurrentCustJobsForm;
begin
  F := TCurrentCustJobsForm.Create(nil);
  try
    Result := IsPositiveResult(F.ShowModal);
    if Result then
      Value := F.CVJobTicketsJobNo.AsInteger;
  finally
    F.Free;
  end;
end;

In the calling Form:

procedure TJobTicketsForm.ShowallJTsforthecurrentJTCustomer1Click(Sender: TObject);
var
  iRecord: Integer;
begin
  // TCurrentCustJobsForm.Create(Nil).ShowModal;
  // if TGlobalData.mivJobTicketNumber <> 0 then dmC.JobTickets.Locate('JobNo', TGlobalData.mivJobTicketNumber, []);
  if TCurrentCustJobsForm.Execute(iRecord) then
    dmC.JobTickets.Locate('JobNo', iRecord, []);
  MessageDlg('iRecord = ' + IntToStr(iRecord), mtInformation, [mbOK], 0);
end;

I am always getting iRecord as 0.

I have obviously done something wrong. 😞

 Thoughts?

Regards & TIA,

Ian

Edited by Sherlock
Changed HTML to PASCAL, you almost got it right this time...

Share this post


Link to post

No sooner sent than I realise that     Result := IsPositiveResult(F.ShowModal); is returning an unexpected False result. :-(

Investigating.

Share this post


Link to post

Please check the controls in TCurrentCustJobsForm. The OK button (or whichever control is closing the form) must have ModalResult property set to mrOk or mrYes.

Share this post


Link to post

Hi Alexander,

It sure is.  This is the action on click.

procedure TCurrentCustJobsForm.btnSelectClick(Sender: TObject);
begin
//  TGlobalData.mivJobTicketNumber := CVJobTicketsJobNo.AsInteger;
//  CVJobTickets.Close;
//  CVCustomers.Close;
  Close;
end;

procedure TCurrentCustJobsForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

Ian

Edited by Sherlock
Code tags...

Share this post


Link to post

Hi Alexander,

OK.  If I remove the Close in the procedure TCurrentCustJobsForm.btnSelectClick(Sender: TObject);

Then it works OK.

After some playing around I ended up with the following..

 

In CurrentCustJobsForm:

procedure TCurrentCustJobsForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

No Select or Close button action.  Select has ModalResult of mrOk & Close button has mrClose.

 

In the calling form I have..

procedure TJobTicketsForm.ShowallJTsforthecurrentJTCustomer1Click(Sender: TObject);
var
  iRecord: Integer;
begin
  if TCurrentCustJobsForm.Execute(iRecord) then
    if iRecord <> 0 then
      dmC.JobTickets.Locate('JobNo', iRecord, []);
end;

All good now.  I learnt some things about various interactions here.

 

Regards & Tks,

Ian

  • Like 1

Share this post


Link to post

Glad that it worked. Btw, now there is no need to have the FormClose handler with Action := caFree - the form will be freed by the calling class function (note the F.Free call), so you can remove this method too.

Share this post


Link to post

So.  I thought I would be sneaky and pass a record so I could retrieve several values.

Well that didn't work. 😞

In the donor form when I try to compile it I get the error..

[dcc32 Error] Unit11.pas(19): E2029 Identifier expected but 'RECORD' found

here..

    { Private declarations }
  public
    { Public declarations }
    class function Execute(out Value: record): Boolean;    <<<<  Error here.
  end;

type
  TCustomer = record
    name: string[30];
    age: byte;
  end;

var
  Form11: TForm11;
  customer: TCustomer;

implementation

{$R *.dfm}

class function TForm11.Execute(out Value: record): Boolean;
var
  F: TForm11;
begin
  F := TForm11.Create(nil);
  try
    Result := IsPositiveResult(F.ShowModal);
    if Result then
      Value := F.customer;
  finally
    F.Free;
  end;
end;

Being called from the caller form by..

type
  TCustomer = record
    name: string[30];
    age: byte;
  end;

var
  Form10: TForm10;
  customer: TCustomer;

implementation

uses Unit11;
{$R *.dfm}

procedure TForm10.Button1Click(Sender: TObject);

begin
  if TForm11.Execute(customer) then
  begin
    MessageDlg('customer.Name = ' + customer.name, mtInformation, [mbOK], 0);
    MessageDlg('customer.Age = ' + IntToStr(customer.age), mtInformation, [mbOK], 0);
  end;
end;

Oh well, it was worth a try. 🙂

 

Ian

Edited by Sherlock
Correct code tags...

Share this post


Link to post

Hi Alexander,

Well.  That cleared the function error, but now it doesn't like F.customer.  Undeclared identifier.

 

Ian

Share this post


Link to post
Just now, Ian Branch said:

Well.  That cleared the function error, but now it doesn't like F.customer.  Undeclared identifier.

That's because the form must have a field or property of this type (like Customer: TCustomer), otherwise there is no place to copy the data from. Alternatively, if the form already has all the necessary data, you can manually fill the resulting record:

....
if Result then begin
  Value.name := F.SomeNameField;
  Value.age := F.SomeAgeField;
end;
....

 

Share this post


Link to post

Hi Alexander,

Appreciate your patience here.

So, even though I had the var customer: TCustomer; in the  called form, F.customer still wasn't recognised.  OK.  So be it.

OK.  So now I have the individual values being set per above, the compiler likes the called unit but the called unit bombs out compiling   if TForm11.Execute(customer) then saying var parameters must be identical.

My first thought was that I needed to change it to   if TForm11.Execute(Tcustomer) then  but this bombs with '(' expected but ')' found.

I am happy to pursue this but I don't want to tie up your time.

 

Ian

Share this post


Link to post
Guest

Please post ALL of your current code, then we can tell you where the error is in your current code.

Share this post


Link to post

Hi Schokohase,

Here you go..

unit Unit10;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm10 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TCustomer = record
    name: string[30];
    age: byte;
  end;

var
  Form10: TForm10;
  customer: TCustomer;

implementation

uses Unit11;
{$R *.dfm}

procedure TForm10.Button1Click(Sender: TObject);
begin
  if TForm11.Execute(customer) then
  begin
    MessageDlg('customer.Name = ' + customer.name, mtInformation, [mbOK], 0);
    MessageDlg('customer.Age = ' + IntToStr(customer.age), mtInformation, [mbOK], 0);
  end;
end;

end.
unit Unit11;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TCustomer = record
    name: string[30];
    age: byte;
  end;
//var
//  customer: TCustomer;

type
  TForm11 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
  private
    { Private declarations }
  public
    { Public declarations }
    class function Execute(out Value: TCustomer): Boolean;
  end;

var
  Form11: TForm11;

implementation

{$R *.dfm}

class function TForm11.Execute(out Value: TCustomer): Boolean;
var
  F: TForm11;
begin
  F := TForm11.Create(nil);
  try
    Result := IsPositiveResult(F.ShowModal);
    if Result then begin
      Value.name := F.Edit1.Text;
      Value.age := StrToInt(F.Edit2.Text);
    end;
  finally
    F.Free;
  end;
end;

end.

 

Unit10 calls unit11.

 

Ian

Edited by Sherlock
Changed quotes to PASCAL source tags - henceforth, please consider using this

Share this post


Link to post
Guest

Well, you have declared two TCustomer types and although they look the same, they are treated as different types by the compiler.

 

Declare the type only once and use that type it in both forms.

 

BTW It would be nice if you could post code within code tags (selecting PASCAL) to increase readability.

Edited by Guest

Share this post


Link to post

OK.  I sorta follow the theory but I don't see how I can make Form11 see the TCustomer from Form10.

Share this post


Link to post
Guest
unit MyApp.Models;

interface

type
  TCustomer = record
    name: string[30];
    age: byte;
  end;
  
implementation

end.
unit Unit11;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  
  MyApp.Models;

type
  TForm11 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
  private
    { Private declarations }
  public
    { Public declarations }
    class function Execute(out Value: TCustomer): Boolean;
  end;

var
  Form11: TForm11;

implementation

{$R *.dfm}

class function TForm11.Execute(out Value: TCustomer): Boolean;
var
  F: TForm11;
begin
  F := TForm11.Create(nil);
  try
    Result := IsPositiveResult(F.ShowModal);
    if Result then begin
      Value.name := F.Edit1.Text;
      Value.age := StrToInt(F.Edit2.Text);
    end;
  finally
    F.Free;
  end;
end;

end.

 

unit Unit10;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  
  MyApp.Models;

type
  TForm10 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form10: TForm10;

implementation

uses Unit11;
{$R *.dfm}

procedure TForm10.Button1Click(Sender: TObject);
var
  customer: TCustomer;
begin
  if TForm11.Execute(customer) then
  begin
    MessageDlg('customer.Name = ' + customer.name, mtInformation, [mbOK], 0);
    MessageDlg('customer.Age = ' + IntToStr(customer.age), mtInformation, [mbOK], 0);
  end;
end;

end.

BTW: Do you notice how beautiful the source can look like if we post the code as code and choosing the right syntax highlighter?

Edited by Guest

Share this post


Link to post
Guest
2 hours ago, Ian Branch said:

still wasn't recognised.  OK.  So be it.

No, no never say "so be it". Instead, pick up a comprehensive volume on the language! Almost all the compiler says has a reason (if you are not pressing new functionality then you might have stumble over a compiler bug). Read through, even lazilly, these basic (hmm, yes) things should be covered.

Work with the compiler and it's messages. Sometimes, messages are produced because of an earlier error, but IMHO this is the way to learn. Why did the compiler say that? Where can i read up on ... records, classes, function parameters et. al?

2 hours ago, Ian Branch said:

Appreciate your patience here.

You should 🙂 and good luck!

Share this post


Link to post

Hi Schokohase,

Ahhh.  I see what you have done.  It is in fact where I started before opening this thread  and looking for an alternative for the return of a variable.

I use this technique a lot.

Thank you for your input.

 

Hi Dany,

Yes you are quite correct and I do read and play around a hell of a lot but sometimes my patience wears thin. ;-)

 

Regards to both,

Ian

 

 

 

Share this post


Link to post

Your previous code set Action := caFree, then the call to Execute did a F := Create ... if aFunc(F.ShowModal) then use stuff in F ... F.Free

 

If you're going to reference ANYTHING in F after calling ShowModal then you want to set Action := caHide and then call F.Free later.

 

The problems with passing a record in are unrelated.

 

You might want to watch my CodeRage 9 session here:

 

https://w5a.com/u/coderage-9-talk

 

Edited by David Schwartz

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×