Search code examples
delphidelphi-xe6

How to hide main form rather than closing it?


If the user clicks X on my main form, i want the form to hide, rather than close. This sounds like a job for the OnClose form event:

Use OnClose to perform special processing when the form closes. The OnClose event specifies which event handler to call when a form is about to close. The handler specified by OnClose might, for example, test to make sure all fields in a data-entry form have valid contents before allowing the form to close.

A form is closed by the Close method or when the user chooses Close from the form's system menu.

The TCloseEvent type points to a method that handles the closing of a form. The value of the Action parameter determines if the form actually closes. These are the possible values of Action:

  • caNone: The form is not allowed to close, so nothing happens.
  • caHide: The form is not closed, but just hidden. Your application can still access a hidden form.
  • caFree: The form is closed and all allocated memory for the form is freed.
  • caMinimize: The form is minimized, rather than closed. This is the default action for MDI child forms.

Which i test in an empty application with one form:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    Action := caHide;
end;

So now when i click X, (rather than hiding) the form closes and the application terminates:

enter image description here

...which sounds like a job for the OnClose event...

Bonus Reading

Vcl.Forms.pas

procedure TCustomForm.Close;
var
   CloseAction: TCloseAction;
begin
   if fsModal in FFormState then
      ModalResult := mrCancel
   else if CloseQuery then
   begin
      if FormStyle = fsMDIChild then
         if biMinimize in BorderIcons then
            CloseAction := caMinimize 
         else
            CloseAction := caNone
      else
         CloseAction := caHide;

      DoClose(CloseAction);
      if CloseAction <> caNone then
      begin
         if Application.MainForm = Self then //Borland doesn't hate developers; it just hates me
            Application.Terminate
         else if CloseAction = caHide then   
            Hide
         else if CloseAction = caMinimize then 
            WindowState := wsMinimized
         else 
            Release;
      end;
   end;
end;

Bonus Reading


Solution

  • When the user closes a window, it receives a WM_CLOSE message, which triggers TForm to call its Close() method on itself. Calling Close() on the project's MainForm always terminates the app, as this is hard-coded behavior in TCustomForm.Close():

    procedure TCustomForm.Close;
    var
      CloseAction: TCloseAction;
    begin
      if fsModal in FFormState then
        ModalResult := mrCancel
      else
        if CloseQuery then
        begin
          if FormStyle = fsMDIChild then
            if biMinimize in BorderIcons then
              CloseAction := caMinimize else
              CloseAction := caNone
          else
            CloseAction := caHide;
          DoClose(CloseAction);
          if CloseAction <> caNone then
            if Application.MainForm = Self then Application.Terminate // <-- HERE
            else if CloseAction = caHide then Hide
            else if CloseAction = caMinimize then WindowState := wsMinimized
            else Release;
        end;
    end;
    

    Only secondary TForm objects respect the output of the OnClose handler.

    To do what you are asking for, you can either:

    • handle WM_CLOSE directly and skip Close().

      private
        procedure WMClose(var Message: TMessage); message WM_CLOSE;
      
      procedure TForm1.WMClose(var Message: TMessage);
      begin
        Hide;
        // DO NOT call inherited ...
      end;
      
    • have your MainForm's OnClose handler call Hide() directly and return caNone:

      procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
      begin
        Hide;
        Action := caNone;
      end;