Comment savoir que l'utilisateur a cliqué sur «X» ou sur le bouton «Fermer»?

94

Dans MSDN, j'ai découvert CloseReason.UserClosingque l'utilisateur avait décidé de fermer le formulaire, mais je suppose que c'est la même chose pour les deux en cliquant sur le bouton X ou en cliquant sur le bouton de fermeture. Alors, comment puis-je différencier ces deux dans mon code?

Merci a tous.

Bohn
la source
2
de quel bouton de fermeture parlez-vous?
Brian R. Bondy
par exemple fermeture par "ALT + F4"
Bohn
@Oliver Pas la même question.
Ctrl S

Réponses:

95

En supposant que vous demandez WinForms, vous pouvez utiliser l' événement FormClosing () . L'événement FormClosing () est déclenché à chaque fois qu'un formulaire doit se fermer.

Pour détecter si l'utilisateur a cliqué sur X ou sur votre CloseButton, vous pouvez l'obtenir via l'objet expéditeur. Essayez de convertir l'expéditeur en contrôle Button et vérifiez peut-être son nom "CloseButton", par exemple.

private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
    if (string.Equals((sender as Button).Name, @"CloseButton"))
        // Do something proper to CloseButton.
    else
        // Then assume that X has been clicked and act accordingly.
}

Sinon, je n'ai jamais eu besoin de différencier si X ou CloseButton a été cliqué, car je voulais effectuer quelque chose de spécifique sur l'événement FormClosing, comme fermer tous les MdiChildren avant de fermer le MDIContainerForm, ou vérifier l'événement pour les modifications non enregistrées. Dans ces circonstances, nous n'avons pas besoin, selon moi, de nous différencier des deux boutons.

La fermeture par ALT+ F4déclenchera également l'événement FormClosing (), car il envoie un message au formulaire indiquant de fermer. Vous pouvez annuler l'événement en réglant le

FormClosingEventArgs.Cancel = true. 

Dans notre exemple, cela se traduirait par

e.Cancel = true.

Notez la différence entre les événements FormClosing () et FormClosed () .

FormClosing se produit lorsque le formulaire a reçu le message à fermer et vérifiez s'il a quelque chose à faire avant qu'il ne soit fermé.

FormClosed se produit lorsque le formulaire est effectivement fermé, donc après sa fermeture.

est-ce que cela aide?

Will Marcouiller
la source
Oui, merci pour l'idée "Cast", j'avais utilisé cette technique avec Delphi 7 mais j'ai oublié de faire de même en C #
Bohn
Il s'agit en fait d'un portage de Delphi vers .NET. =) Je suis content d'avoir aidé!
Will Marcouiller le
1
J'obtiens 'Object reference not set to an instance of an object' lorsque j'utilise ce code.
Nate S.
33
C'est faux. Vous ne pouvez pas convertir l'expéditeur en bouton car il s'agit du formulaire lui-même. Cela lève une exception.
Xtro
1
Veuillez noter que c'est une réponse INCORRECTE. Veuillez ne pas voter pour cela.
Najeeb le
79

le CloseReason énumération que vous avez trouvée sur MSDN est juste dans le but de vérifier si l'utilisateur a fermé l'application, ou si elle était due à un arrêt, ou fermée par le gestionnaire de tâches, etc.

Vous pouvez faire différentes actions, selon la raison, comme:

void Form_FormClosing(object sender, FormClosingEventArgs e)
{
    if(e.CloseReason == CloseReason.UserClosing)
        // Prompt user to save his data

    if(e.CloseReason == CloseReason.WindowsShutDown)
        // Autosave and clear up ressources
}

Mais comme vous l'avez deviné, il n'y a aucune différence entre cliquer sur le bouton x, ou cliquer avec le bouton droit sur la barre des tâches et cliquer sur «fermer», ou appuyer sur Alt F4, etc. Tout se termine par une CloseReason.UserClosingraison.

Philippe Daubmeier
la source
11
En utilisant le standard Close (); semble déclencher CloseReason.UserClosing pour moi. Pas certain de pourquoi.
Dan W
J'ai trouvé cela utile lorsque j'ai essayé de bloquer la fermeture d'un formulaire enfant MDI par l'action de l'utilisateur sur le formulaire, mais pas lorsque le parent est en cours de fermeture.
Steve Pettifer
1
Cela ne répond pas à la question, mais énumère simplement le problème.
Robert Koernke
Comment liez-vous l'événement à la méthode?
user2924019
43

Le bouton "X" s'enregistre car DialogResult.Cancelune autre option consiste à évaluer le fichier DialogResult.

Si votre formulaire comporte plusieurs boutons, vous associez probablement déjà différents DialogResult s à chacun et cela vous fournira les moyens de faire la différence entre chaque bouton.

(Exemple: btnSubmit.DialogResult = DialogResult.OK, btnClose.DialogResult = Dialogresult.Abort)

    public Form1()
    {
        InitializeComponent();

        this.FormClosing += Form1_FormClosing;
    }

    /// <summary>
    /// Override the Close Form event
    /// Do something
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_FormClosing(Object sender, FormClosingEventArgs e)
    {
        //In case windows is trying to shut down, don't hold the process up
        if (e.CloseReason == CloseReason.WindowsShutDown) return;

        if (this.DialogResult == DialogResult.Cancel)
        {
            // Assume that X has been clicked and act accordingly.
            // Confirm user wants to close
            switch (MessageBox.Show(this, "Are you sure?", "Do you still want ... ?", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
            {
                //Stay on this form
                case DialogResult.No:
                    e.Cancel = true;
                    break;
                default:
                    break;
            }
        }
    }
AlexScript
la source
1
Dans mon cas, cela est plus utile que la réponse acceptée. Puisque le 'X' est attribué à DialogResult.Cancel, l'attribution d'une autre valeur au bouton d'annulation permet de les distinguer facilement et de gérer les choses de manière appropriée.
MickeyfAgain_BeforeExitOfSO
3
Cela ne fonctionne pas dans mon cas. Lorsque vous appuyez sur «X», DialogResultreste None. Quel pourrait être le problème?
Bhaskar
1
@Bhaskar, lorsque vous instanciez votre boîte de dialogue, assurez-vous de définir le DialogResult approprié sur chaque bouton de votre boîte de dialogue. J'avais fourni un exemple ci-dessus mais je n'ai pas créé de bloc de code pour afficher la déclaration Dialog.
AlexScript
@Bhaskar: Appuyer sur Xfait DialogResultcontenir Cancel, pas None. Assigner Noneà votre bouton revient à ne pas définir du tout sa .DialogResultpropriété, et si vous appelez à form.Close()partir du gestionnaire d'événements de votre bouton, form.DialogResultcontiendra Cancel. Seule l'attribution d'une valeur autre que Noneou Cancelà tous vos boutons de fermeture de formulaire vous permettra de faire la distinction souhaitée.
mklement0
9

Comment détecter si le formulaire s'est fermé en cliquant sur le bouton X ou en appelant Close() code?

Vous ne pouvez pas vous fier à la raison de la fermeture de l'événement de fermeture du formulaire, car si l'utilisateur clique sur le bouton X de la barre de titre ou ferme le formulaire en utilisant Alt + F4 ou utilise le menu système pour fermer le formulaire ou le formulaire se ferme en appelant la Close()méthode, tout ci-dessus, la raison de la fermeture sera fermée par l'utilisateur, ce qui n'est pas le résultat souhaité.

Pour distinguer si le formulaire est fermé par le bouton X ou par la Closeméthode, vous pouvez utiliser l'une des options suivantes:

  • Manipulez WM_SYSCOMMAND, vérifiez SC_CLOSEet définissez un drapeau.
  • Vérifiez StackTracesi l'un des cadres contient un Closeappel de méthode.

Exemple 1 - Poignée WM_SYSCOMMAND

public bool ClosedByXButtonOrAltF4 {get; private set;}
private const int SC_CLOSE = 0xF060;
private const int WM_SYSCOMMAND = 0x0112;
protected override void WndProc(ref Message msg)
{
    if (msg.Msg == WM_SYSCOMMAND && msg.WParam.ToInt32() == SC_CLOSE)
        ClosedByXButtonOrAltF4 = true;
    base.WndProc(ref msg);
}
protected override void OnShown(EventArgs e)
{
    ClosedByXButtonOrAltF4 = false;
}   
protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (ClosedByXButtonOrAltF4)
        MessageBox.Show("Closed by X or Alt+F4");
    else
        MessageBox.Show("Closed by calling Close()");
}

Exemple 2 - Vérification de StackTrace

protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (new StackTrace().GetFrames().Any(x => x.GetMethod().Name == "Close"))
        MessageBox.Show("Closed by calling Close()");
    else
        MessageBox.Show("Closed by X or Alt+F4");
}
Reza Aghaei
la source
1
Bien fait. Dommage que vous soyez en retard à la fête - difficile de rivaliser avec les réponses les plus anciennes, déjà très votées.
mklement0
1
@ mklement0 J'espère que les futurs utilisateurs le trouveront utile. J'ai posté la réponse car aucune des autres réponses ne pourrait résoudre le problème correctement et c'est assez étrange pour une question ayant ce nombre de vues et des réponses hautement votées (non fonctionnelles)!
Reza Aghaei
7

Il détermine quand fermer l'application si un formulaire est fermé (si votre demande n'est pas jointe à un formulaire spécifique).

    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        if (Application.OpenForms.Count == 0) Application.Exit();
    }
derloopkat
la source
5

J'utilise toujours une méthode de fermeture de formulaire dans mes applications qui intercepte alt + x de mon bouton de sortie, alt + f4 ou un autre événement de fermeture de formulaire a été lancé. Toutes mes classes ont le nom de classe défini comme Chaîne privée mstrClsTitle = "grmRexcel"dans ce cas, une méthode Exit qui appelle la méthode de fermeture de formulaire et une méthode de fermeture de formulaire. J'ai également une déclaration pour la méthode de fermeture de formulaire - this.FormClosing = My Form Closing Form Closing method name.

Le code pour cela:

namespace Rexcel_II
{
    public partial class frmRexcel : Form
    {
        private string mstrClsTitle = "frmRexcel";

        public frmRexcel()
        {
            InitializeComponent();

            this.FormClosing += frmRexcel_FormClosing;
        }

        /// <summary>
        /// Handles the Button Exit Event executed by the Exit Button Click
        /// or Alt + x
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExit_Click(object sender, EventArgs e)
        {            
            this.Close();        
        }


        /// <summary>
        /// Handles the Form Closing event
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmRexcel_FormClosing(object sender, FormClosingEventArgs e)
        {

            // ---- If windows is shutting down, 
            // ---- I don't want to hold up the process
            if (e.CloseReason == CloseReason.WindowsShutDown) return;
            {

                // ---- Ok, Windows is not shutting down so
                // ---- either btnExit or Alt + x or Alt + f4 has been clicked or
                // ---- another form closing event was intiated
                //      *)  Confirm user wants to close the application
                switch (MessageBox.Show(this, 
                                    "Are you sure you want to close the Application?",
                                    mstrClsTitle + ".frmRexcel_FormClosing",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                {

                    // ---- *)  if No keep the application alive 
                    //----  *)  else close the application
                    case DialogResult.No:
                        e.Cancel = true;
                        break;
                    default:
                        break;
                }
            }
        }
    }
}
Coup de départ
la source
2

Vous pouvez essayer d'ajouter un gestionnaire d'événements à partir de la conception comme ceci: Ouvrez le formulaire en mode Création, ouvrez la fenêtre des propriétés ou appuyez sur F4, cliquez sur le bouton de la barre d'outils des événements pour afficher les événements sur l'objet Form, recherchez l'événement FormClosing dans le groupe Comportement et double-cliquez dessus. Référence: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bdee708-db4b-4e46-a99c-99726fa25cfb/how-do-i-add-formclosing-event?forum=csharpgeneral

Lisa
la source
1
if (this.DialogResult == DialogResult.Cancel)
        {

        }
        else
        {
            switch (e.CloseReason)
            {
                case CloseReason.UserClosing:
                    e.Cancel = true;
                    break;
            }
        }

si la condition s'exécute lorsque l'utilisateur clique sur «X» ou sur le bouton de fermeture du formulaire. L'autre peut être utilisé lorsque l'utilisateur clique sur Alt + f4 à toute autre fin

phani kiran
la source
1
namespace Test
{
    public partial class Member : Form
    {
        public Member()
        {
            InitializeComponent();
        }

        private bool xClicked = true;

        private void btnClose_Click(object sender, EventArgs e)
        {
            xClicked = false;
            Close();
        }

        private void Member_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (xClicked)
            {
                // user click the X
            } 
            else 
            {
                // user click the close button
            }
        }
    }
}
Dragan Menoski
la source
1

Je suis d'accord avec le DialogResult -Solution comme étant la plus simple.

Cependant, dans VB.NET, la conversion de type est requise pour obtenir la CloseReasonpropriété -Property

    Private Sub MyForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing

        Dim eCast As System.Windows.Forms.FormClosingEventArgs
        eCast = TryCast(e, System.Windows.Forms.FormClosingEventArgs)
        If eCast.CloseReason = Windows.Forms.CloseReason.None Then
            MsgBox("Button Pressed")
        Else
            MsgBox("ALT+F4 or [x] or other reason")
        End If

    End Sub
Dr Marbuse
la source
0

J'ai également dû enregistrer la fonction de fermeture dans la méthode "InitializeComponent ()" du formulaire:

private void InitializeComponent() {
// ...
this.FormClosing += FrmMain_FormClosing;
// ...
}

Ma fonction "FormClosing" ressemble à la réponse donnée ( https://stackoverflow.com/a/2683846/3323790 ):

private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) {
    if (e.CloseReason == CloseReason.UserClosing){
        MessageBox.Show("Closed by User", "UserClosing");
    }

    if (e.CloseReason == CloseReason.WindowsShutDown){
        MessageBox.Show("Closed by Windows shutdown", "WindowsShutDown");
    }
}

Une dernière chose à mentionner: il existe également une fonction "FormClosed" qui se produit après "FormClosing". Pour utiliser cette fonction, enregistrez-la comme indiqué ci-dessous:

this.FormClosed += MainPage_FormClosed;

private void MainPage_FormClosing(object sender, FormClosingEventArgs e)
{
// your code after the form is closed
}
IVIike
la source
0

J'ai fait quelque chose comme ça.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
PachecoDt
la source