Home > OS >  Reset a single form to its initial state
Reset a single form to its initial state

Time:09-23

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.
  • Related