Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Saber cómo se cierra el formulario

si se pulsa en la 'x' del formulario
(o en cerrar del menú del formulario)

Publicado el 23/Jun/2004
Actualizado el 17/Ene/2006
Autor: Guillermo 'guille' Som

 

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)

 

Introducción:

En Visual Basic 6 (y anteriores) podíamos saber cual era la razón de que se cerrara un formulario usando el evento QueryUnload y comprobando el valor del parámetro UnloadMode.
Este parámetro nos informaba (y sigue informando en los proyectos de VB clásico) de cual era la razón del cierre. Entre esos valores está vbFormControlMenu, que es el que se indica cuando el usuario ha pulsado en la "x" del formulario o seleccionando la opción Cerrar del menú del formulario (también conocido como system menu), los valores de las constantes para VB y un ejemplo, lo puedes ver en el truco de VB clásico: Comprobar cómo se cierra una aplicación.

Nota 17/Ene/06:
Lo aquí explicado es válido para las versiones de Visual Studio .NET anteriores a la 2005.
Si quieres ver el código para Visual Studio 2005 (.NET 2.0) para Visual Basic y C#, pulsa este link.

 

En Visual Basic .NET no existe el evento QueryUnload ni hay forma de saber cómo se cierra el formulario.
Para poder simular la misma funcionalidad de VB6 debemos echar mano de llamadas al ciclo de mensajes que reciben los formularios, averiguar si se ha pulsando en el menú del sistema (menú del formulario) y saber si la opción seleccionada es la de cerrar. Hay que tener en cuenta que las opciones del menú del sistema del formulario son las mismas que los botones cerrar, minimizar y maximizar/restaurar.

En el código que voy a mostrar, vamos a detectar si se ha pulsado en esa opción y de ser así, minimizaremos la aplicación en lugar de cerrarla, por otro lado tendremos un botón Cerrar que realmente cerrará la aplicación.

Si no te gusta el código que aquí se muestra, porque no quieras entrar en el bucle de mensajes del formulario, puedes hacer las comprobaciones por tu cuenta, pero ten cuidado al cancelar el cierre del formulario desde el evento Closing, ya que te puedes encontrar con el problemilla de que el Windows tampoco se va a cerrar, tal como te comento en: Precaución al cancelar el cierre del formulario en el evento Closing...

 

¿Cómo saber si se cierra el formulario mediante la "x"?

El truco está (o el código a usar) en interceptar los mensajes que recibe el formulario, esto se hace usando el método WndProc de la clase base (Form), por tanto tenemos que reemplazar esa función en nuestro formulario y detectar si se ha seleccionado el menú del sistema, en caso de que así sea, tenemos que averiguar si se ha pulsado en Cerrar.
Mirando la documentación del API de Windows, nos encontramos con el mensaje WM_SYSCOMMAND, el cual nos dice que se debe usar de esta forma:

WM_SYSCOMMAND
uCmdType = wParam;      // type of system command requested
xPos = LOWORD(lParam); // horizontal position, in screen coordinates
yPos = HIWORD(lParam); // vertical position, in screen coordinates '

De estos parámetros el que nos interesa es uCmdType que viene en el parámetro wParam, el cual debemos comprobar si es SC_CLOSE, en ese caso es que se está ha pulsado en cerrar.

El código de VB .NET sería este:

      Const WM_SYSCOMMAND As Integer = &H112
      Const SC_CLOSE As Integer = &HF060
      '
      Select Case m.Msg
         Case WM_SYSCOMMAND
            If m.WParam.ToInt32 = SC_CLOSE Then
               esFormControlMenu = True
            End If
      End Select

Aquí lo que hacemos es asignar un valor verdadero a una variable declarada a nivel de formulario la cual usaremos en el evento Closing, de forma que si tiene ese valor, cancelamos el cierre del formulario:

      If esFormControlMenu Then
         e.Cancel = True

         WindowState = FormWindowState.Minimized
         esFormControlMenu = False
      End If

Aquí lo que hacemos es cancelar el cierre y minimizar el formulario, además de asignar un valor falso a esa misma variable para que no se siga manteniendo, sino... nunca se cerraría el formulario.

Es importante que después de interceptar el mensaje (en el Sub WndProc) llamemos al método de la clase base para que se intercepte el mensaje y se procese, sino no llegaría a producirse el evento Closing. Para ello llamamos a ese método de la clase base: MyBase.WndProc(m)

 

Y esto es todo...
Para que tengas todo el código, que como es costumbre desde hace tiempo, te lo muestro un poco más abajo tanto en VB como en C#.
Y al final de la página tienes el link al código completo tanto de VB como de C#.

 

¡Espero que te sea de utilidad!

Nos vemos.
Guillermo


Código para Visual Basic.NET (VB.NET)El código para VB .NET

'------------------------------------------------------------------------------
' comoFormControlMenuVB                                  (23/Jun/04)
' Saber si se cierra al pulsar en la "x" del formulario (menú cerrar)
'
' ©Guillermo 'guille' Som, 2004
'
' Para lograr esto se utiliza la función WndProc de la clase base (Form)
'------------------------------------------------------------------------------
Imports System.Security.Permissions

Public Class Form1
   Inherits System.Windows.Forms.Form
   '
#Region " Código generado por el Diseñador de Windows Forms "
    '...
#End Region
   '
   Private esFormControlMenu As Boolean = False
   '
   '
   Private Sub Form1_Load(ByVal sender As Object, _
                     ByVal e As System.EventArgs) _
                     Handles MyBase.Load
      Label1.Text = _
         "Si pulsas en la 'x' del formulario o en la opción cerrar del menú del formulario," & _
         " la aplicación se minimizará." & vbCrLf & _
         "Para cerrar pulsa en el botón Cerrar."
   End Sub
   '
   Private Sub btnCerrar_Click(ByVal sender As Object, _
                        ByVal e As System.EventArgs) _
                        Handles btnCerrar.Click
      ' cerrar la aplicación
      esFormControlMenu = False
      Me.Close()
   End Sub
   '
   Private Sub Form1_Closing(ByVal sender As Object, _
                       ByVal e As System.ComponentModel.CancelEventArgs) _
                       Handles MyBase.Closing
      If esFormControlMenu Then
         ' se está cerrando porque se ha pulsado en la "x"
         ' o en la opción Cerrar del menú del formulario
         ' En este caso se cancela el cierre de la aplicación y se minimiza
         e.Cancel = True
         WindowState = FormWindowState.Minimized
         esFormControlMenu = False
      End If
   End Sub
   '
   '
   'WM_SYSCOMMAND 
   'uCmdType = wParam;      // type of system command requested 
   'xPos = LOWORD(lParam);   // horizontal position, in screen coordinates 
   'yPos = HIWORD(lParam);   // vertical position, in screen coordinates    '
   ' Usar este Imports System.Security.Permissions
   <PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
   Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
      ' comprobar si se cierra la ventana al pulsar en la "x"    (22/Jun/04)
      Const WM_SYSCOMMAND As Integer = &H112
      Const SC_CLOSE As Integer = &HF060
      '
      Select Case m.Msg
         Case WM_SYSCOMMAND
            If m.WParam.ToInt32 = SC_CLOSE Then
               esFormControlMenu = True
            End If
      End Select
      '
      MyBase.WndProc(m)
   End Sub
End Class

 


Código para C Sharp (C#)El código para C#

//-----------------------------------------------------------------------------
// comoFormControlMenuCS                                 (23/Jun/04)
// Saber si se cierra al pulsar en la "x" del formulario (menú cerrar)
//
// ©Guillermo 'guille' Som, 2004
//
// Para lograr esto se utiliza la función WndProc de la clase base (Form)
//-----------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Security.Permissions;

namespace comoFormControlMenuCS
{
   public class Form1 : System.Windows.Forms.Form
   {
     //...
     [STAThread]
     static void Main() 
     {
       Application.Run(new Form1());
     }

     //
     private bool esFormControlMenu = false;
     //
     //
     private void Form1_Load(System.Object sender, System.EventArgs e) 
     {
       Label1.Text = "Si pulsas en la 'x' del formulario o en la opción cerrar del menú del formulario," + 
         " la aplicación se minimizará.\n\rPara cerrar pulsa en el botón Cerrar.";
     }  
     //
     private void btnCerrar_Click(System.Object sender, System.EventArgs e) 
     {
       // cerrar la aplicación
       esFormControlMenu = false;
       this.Close();
     }  
     //
     private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
     {
       if( esFormControlMenu )
       {
         // se está cerrando porque se ha pulsado en la "x"
         // o en la opción Cerrar del menú del formulario
         // En este caso se cancela el cierre de la aplicación y se minimiza
         e.Cancel = true;
         WindowState = FormWindowState.Minimized;
         esFormControlMenu = false;
       }
     }  
     //
     //
     //WM_SYSCOMMAND
     //uCmdType = wParam;      // type of system command requested
     //xPos = LOWORD(lParam);   // horizontal position, in screen coordinates
     //yPos = HIWORD(lParam);   // vertical position, in screen coordinates    '
     // Usar este Imports System.Security.Permissions
     [PermissionSetAttribute(SecurityAction.Demand, Name="FullTrust")]
     protected override void WndProc(ref System.Windows.Forms.Message m) 
     {
       // comprobar si se cierra la ventana al pulsar en la "x"    (22/Jun/04)
       const int WM_SYSCOMMAND = 0x112;
       const int SC_CLOSE = 0xF060;
       //
       switch(m.Msg)
       {
         case WM_SYSCOMMAND:
            if( m.WParam.ToInt32() == SC_CLOSE )
              esFormControlMenu = true;
            break; 
       }
       //
       base.WndProc(ref m);
     }
   }
}

 


El código completo tanto para Visual Basic como para C#: comoQueryUnload.zip 21.6 KB
(creado con el Visual Studio .NET (.NET Framework 1.0))

 


la Luna del Guille o... el Guille que está en la Luna... tanto monta...