Ian Branch 128 Posted September 12, 2019 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 Posted September 12, 2019 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
Alexander Elagin 143 Posted September 12, 2019 (edited) 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 September 12, 2019 by Alexander Elagin Share this post Link to post
Ian Branch 128 Posted September 12, 2019 (edited) 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 September 12, 2019 by Sherlock Changed HTML to PASCAL, you almost got it right this time... Share this post Link to post
Ian Branch 128 Posted September 12, 2019 No sooner sent than I realise that Result := IsPositiveResult(F.ShowModal); is returning an unexpected False result. :-( Investigating. Share this post Link to post
Alexander Elagin 143 Posted September 12, 2019 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
Ian Branch 128 Posted September 12, 2019 (edited) 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 September 12, 2019 by Sherlock Code tags... Share this post Link to post
Ian Branch 128 Posted September 12, 2019 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 1 Share this post Link to post
Alexander Elagin 143 Posted September 12, 2019 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
Ian Branch 128 Posted September 12, 2019 Duly noted and amended. Tks again Alexander. Ian Share this post Link to post
Ian Branch 128 Posted September 12, 2019 (edited) 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 September 12, 2019 by Sherlock Correct code tags... Share this post Link to post
Ian Branch 128 Posted September 12, 2019 Hi Schokohase, In my ignorance of what can and can't be done in in this area - No. Share this post Link to post
Alexander Elagin 143 Posted September 12, 2019 It should have been class function Execute(out Value: TCustomer): Boolean; and TCustomer declared before the form, then everything would work. Share this post Link to post
Ian Branch 128 Posted September 12, 2019 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
Alexander Elagin 143 Posted September 12, 2019 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
Ian Branch 128 Posted September 12, 2019 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 Posted September 12, 2019 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
Ian Branch 128 Posted September 12, 2019 (edited) 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 September 12, 2019 by Sherlock Changed quotes to PASCAL source tags - henceforth, please consider using this Share this post Link to post
Guest Posted September 12, 2019 (edited) 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 September 12, 2019 by Guest Share this post Link to post
Ian Branch 128 Posted September 12, 2019 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 Posted September 12, 2019 (edited) 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 September 12, 2019 by Guest Share this post Link to post
Guest Posted September 12, 2019 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
Ian Branch 128 Posted September 12, 2019 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
David Schwartz 430 Posted September 22, 2019 (edited) 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 September 23, 2019 by David Schwartz Share this post Link to post