I have multiple forms in one project: frmBooking
, frmWelcome
, frmAdmin
.
I want to reset frmBooking
(i.e., reset it to its initial state, as if it's just created with all the components) by clicking a button.
I tried doing the following:
frmBooking.Destroy;
Application.CreateForm(TForm, frmBooking);
frmBooking.Show;
The result, however, is that it just creates a blank form, not resetting the form to its initial state.
What can I do to reset the form?
CodePudding user response:
Based on your initial approach I put together some code.
var
theOwner: TComponent;
begin
{ make sure that we don't kill ourselves.
can be omitted if we are sure it cannot happen. }
theOwner := Owner;
while theOwner <> nil do begin
if theOwner = frmBooking then
raise Exception.Create('cannot recreate owner form');
theOwner := theOwner.Owner;
end;
{ find out who must be the owner of the newly created instance }
theOwner := frmBooking.Owner;
frmBooking.Free;
frmBooking := TfrmBooking.Create(theOwner);
frmBooking.Show;
end;
CodePudding user response:
To refresh a form that was created by another form, you need to do a couple things.
First, know who owns what. Does the Form1 own Form2 or is Form1 just creating it for Application? If it is embedded in Form1, like a Form1.Panel1 or something, then Self or the Panel can be parent, but if we want it to show as a separate window, then Self is not correct, we likely need Application as our parent which you see in my TForm2.Create(Application)
Second, you need to handle a refresh request from the Form2 or some other service controlling the decision to refresh. For this, the easiest way is to use an EVENT that sends a message to Form1, or if something else makes that decision, the event procedure is handled similarly from that object to the the Form2, which begind the Close and then Form2 triggers a second Event like my example to whomever made Form2 in the first place like Form1! You COULD use sendmessage or postmessage, but that's beyond what is necessary. Forms have built in ones, but let's just make a new TNotifyEvent to show how it is done. An example of multiple Events is: imagine a MainForm having two tab controls, RTab and LTab each filled with 5 Tabs, each Tab is a Form in the source code. RTab2 is edited, it notifies Main with an Event, Main then notifies LTab2 to just reload data and display it in a Grid.
Lastly, the trigger for refresh can be anything and anywhere in Form2. In my example we force it with a button click that's no different than the X button except we followup with the execution of Form1's event handler. That will tell Form1 to begin recreating a new object while the old is destroying itself. Whether you create a new dataobject in your even handler, calling additional Form1 functions and procedures to do that, or just have Form2's Create repopulate is up to you.
If that isn't the case, my example shows on initial create of Form2 how to handle it.
There are no special Destroy's or FreeAndNil's necessary in my example, but you likely will need the override and reintroduced procedures.
All I have are two forms with buttons, so other than setting your button's OnClick, there is nothing special there. It is a very good idea to wrap your event call in a check to see if it is defined to prevent errors, even if it isn't optional...
if Assigned(EventProcedureName) then
begin
EventProcedureName(Self);
end;
TForm1:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FCounter: Integer;
public
procedure RefreshChildEventHandler(Sender: TObject);
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
System.UITypes, Unit2;
{$R *.dfm}
procedure TForm1.RefreshChildEventHandler(Sender: TObject);
var
childForm: TForm2;
frmType: string;
begin
try
childForm := TForm2.Create(Application);
case FCounter of
0:
begin
childForm.Color := TColorRec.White;
FCounter := 1;
end;
1:
begin
childForm.Color := TColorRec.Blue;
FCounter := 2;
end;
2:
begin
childForm.Color := TColorRec.Red;
FCounter := 0;
end;
end;
childForm.RefreshMeEvent := RefreshChildEventHandler;
childForm.Show;
except
raise;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Why not use our procedure on initial create?
RefreshChildEventHandler(Self);
end;
end.
TForm2:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FOnChange: TNotifyEvent;
{ Private declarations }
public
{ Public declarations }
property RefreshMeEvent: TNotifyEvent
read FOnChange
write FOnChange;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
Self.Close;
Self.RefreshMeEvent(Self);
end;
end.