Come sapere che l'utente ha cliccato sulla "X" o sul pulsante "Chiudi"?


94

In MSDN ho scoperto CloseReason.UserClosingche l'utente aveva deciso di chiudere il modulo ma immagino che sia lo stesso sia facendo clic sul pulsante X che facendo clic sul pulsante di chiusura. Quindi come posso distinguere tra questi due nel mio codice?

Ringrazia tutti.


2
quale pulsante di chiusura intendi?
Brian R. Bondy

ad esempio chiusura con "ALT + F4"
Bohn


@ Oliver Non è la stessa domanda.
Ctrl S

Risposte:


95

Supponendo che tu stia chiedendo WinForms, puoi usare l' evento FormClosing () . L'evento FormClosing () viene attivato ogni volta che un modulo deve essere chiuso.

Per rilevare se l'utente ha cliccato su X o sul tuo CloseButton, puoi ottenerlo tramite l'oggetto mittente. Prova a eseguire il cast del mittente come controllo Button e verifica forse il nome "CloseButton", ad esempio.

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.
}

Altrimenti, non ho mai avuto bisogno di differenziare se X o CloseButton è stato cliccato, poiché volevo eseguire qualcosa di specifico sull'evento FormClosing, come chiudere tutti gli MdiChildren prima di chiudere MDIContainerForm o controllare gli eventi per le modifiche non salvate. In queste circostanze, secondo me, non è necessario distinguere da nessuno dei due pulsanti.

La chiusura con ALT+ F4attiverà anche l'evento FormClosing (), poiché invia un messaggio al Form che dice di chiudere. È possibile annullare l'evento impostando il

FormClosingEventArgs.Cancel = true. 

Nel nostro esempio, questo si tradurrebbe in essere

e.Cancel = true.

Notare la differenza tra FormClosing () e FormClosed () .

FormClosing si verifica quando il modulo ha ricevuto il messaggio da chiudere e verifica se ha qualcosa da fare prima che venga chiuso.

FormClosed si verifica quando il modulo è effettivamente chiuso, quindi dopo che è stato chiuso.

questo aiuta?


Sì, grazie per l'idea "Cast", avevo usato questa tecnica con Delphi 7 ma ho dimenticato di fare lo stesso in C #
Bohn

Questo in effetti è un port da Delphi a .NET. =) Sono contento di aver aiutato!
Will Marcouiller

1
Ottengo "Riferimento oggetto non impostato su un'istanza di un oggetto" quando utilizzo questo codice.
Nate S.

33
Questo è sbagliato. Non puoi trasmettere il mittente al pulsante perché è il modulo stesso. Questo genera un'eccezione.
Xtro

1
Si prega di notare che questa è una risposta ERRATA. Per favore, non votare questo.
Najeeb

79

Il CloseReason enumerazione che hai trovato su MSDN ha solo lo scopo di verificare se l'utente ha chiuso l'app, o se è stato per un arresto, o chiuso dal task manager, ecc ...

Puoi eseguire diverse azioni, a seconda del motivo, come:

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
}

Ma come hai intuito, non c'è differenza tra fare clic sul pulsante x, o fare clic con il pulsante destro del mouse sulla barra delle applicazioni e fare clic su "chiudi", o premere Alt F4, ecc. Tutto finisce in un CloseReason.UserClosingmotivo.


11
Utilizzando lo standard Close (); sembra attivare CloseReason.UserClosing per me. Non so perché.
Dan W

L'ho trovato utile quando si cerca di bloccare la chiusura di un form figlio MDI tramite l'azione dell'utente sul modulo ma non quando il genitore viene chiuso.
Steve Pettifer

1
Questo non risponde alla domanda, si limita a enumerare ulteriormente il problema.
Robert Koernke,

Come leghi l'evento al metodo?
user2924019

43

Il pulsante "X" si registra DialogResult.Cancelcosì un'altra opzione è valutare il file DialogResult.

Se nel modulo sono presenti più pulsanti, probabilmente ne stai già associando diversi DialogResult a ciascuno e questo ti fornirà i mezzi per distinguere tra ogni pulsante.

(Esempio: 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;
            }
        }
    }

1
Nel mio caso questo è più utile della risposta accettata. Poiché la "X" è assegnata a DialogResult.Cancel, l'assegnazione di un altro valore al pulsante Annulla li distingue facilmente e gestisce le cose in modo appropriato.
MickeyfAgain_BeforeExitOfSO

3
Questo non funziona nel mio caso. Quando si preme "X", DialogResultrimane None. Quale potrebbe essere il problema?
Bhaskar

1
@Bhaskar, quando crei un'istanza della tua finestra di dialogo, assicurati di impostare il DialogResult appropriato per ogni pulsante nella tua finestra di dialogo. Avevo fornito un esempio sopra ma non ho creato un blocco di codice per mostrare la dichiarazione di dialogo.
AlexScript

@ Bhaskar: premendo Xfa DialogResultcontenere Cancel, non None. Assegnare Noneal tuo pulsante equivale a non impostarne affatto la .DialogResultproprietà e, se chiami form.Close()dal gestore di eventi del tuo pulsante, form.DialogResultconterrà Cancel. Solo l'assegnazione di un valore diverso da Noneo Cancela tutti i pulsanti di chiusura del modulo ti consentirà di fare la distinzione desiderata.
mklement0

9

Come rilevare se il modulo è stato chiuso facendo clic sul pulsante X o chiamando il Close()codice?

Non puoi fare affidamento sul motivo di chiusura degli argomenti dell'evento di chiusura del modulo, perché se l'utente fa clic sul pulsante X sulla barra del titolo o chiude il modulo usando Alt + F4 o usa il menu di sistema per chiudere il modulo o il modulo viene chiuso chiamando il Close()metodo, tutto tutto casi di cui sopra, il motivo di chiusura sarà Chiuso dall'utente che non è il risultato desiderato.

Per distinguere se il modulo è stato chiuso dal pulsante X o dal Closemetodo, puoi utilizzare una delle seguenti opzioni:

  • Gestisci WM_SYSCOMMANDe controlla SC_CLOSEe imposta una bandiera.
  • Controllare StackTraceper vedere se uno qualsiasi dei frame contiene una Closechiamata al metodo.

Esempio 1 - Maniglia 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()");
}

Esempio 2: controllo di 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");
}

1
Ben fatto. Peccato che tu sia arrivato in ritardo alla festa: difficile competere con le risposte più vecchie e già molto votate.
mklement0

1
@ mklement0 Spero che i futuri utenti lo trovino utile. Ho pubblicato la risposta perché nessuna delle altre risposte potrebbe risolvere il problema correttamente ed è piuttosto strano per una domanda con questo numero di visualizzazioni e risposte altamente votate (non funzionanti)!
Reza Aghaei

7

Determina quando chiudere l'applicazione se un modulo è chiuso (se la domanda non è allegata a un modulo specifico).

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

5

Uso sempre un metodo di chiusura del modulo nelle mie applicazioni che cattura alt + x dal mio pulsante di uscita, alt + f4 o è stato avviato un altro evento di chiusura del modulo. Tutte le mie classi hanno il nome della classe definito come stringa privata mstrClsTitle = "grmRexcel"in questo caso, un metodo Exit che chiama il metodo di chiusura del modulo e un metodo di chiusura del modulo. Ho anche una dichiarazione per il metodo di chiusura del modulo - this.FormClosing = My Form Closing Form Closing method name.

Il codice per questo:

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;
                }
            }
        }
    }
}

2

Puoi provare ad aggiungere il gestore di eventi dal design in questo modo: apri il modulo nella vista struttura, apri la finestra delle proprietà o premi F4, fai clic sul pulsante della barra degli strumenti degli eventi per visualizzare gli eventi sull'oggetto modulo, trova l'evento FormClosing nel gruppo Comportamento e fai doppio clic su di esso. Riferimento: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bdee708-db4b-4e46-a99c-99726fa25cfb/how-do-i-add-formclosing-event?forum=csharpgeneral


1
if (this.DialogResult == DialogResult.Cancel)
        {

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

se la condizione verrà eseguita quando l'utente fa clic sulla "X" o sul pulsante di chiusura del modulo. L'altro può essere utilizzato quando l'utente fa clic su Alt + F4 per qualsiasi altro scopo


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
            }
        }
    }
}

1

Sono d'accordo con la DialogResult-Soluzione come quella più semplice.

In VB.NET, tuttavia, typecast è necessario per ottenere il file CloseReason-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

0

Ho anche dovuto registrare la funzione di chiusura all'interno del metodo "InitializeComponent ()" del modulo:

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

La mia funzione "FormClosing" è simile alla risposta fornita ( 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");
    }
}

Un'altra cosa da menzionare: c'è anche una funzione "FormClosed" che si verifica dopo "FormClosing". Per utilizzare questa funzione, registrarla come mostrato di seguito:

this.FormClosed += MainPage_FormClosed;

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

0

Ho fatto qualcosa di simile.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.