Woher weiß ich, dass der Benutzer auf "X" oder "Schließen" geklickt hat?

93

In MSDN habe ich festgestellt CloseReason.UserClosing, dass der Benutzer beschlossen hat, das Formular zu schließen, aber ich denke, dass dies sowohl für das Klicken auf die X-Schaltfläche als auch für das Klicken auf die Schaltfläche zum Schließen gleich ist. Wie kann ich diese beiden in meinem Code unterscheiden?

Vielen Dank an alle.

Bohn
quelle
2
Welche Schaltfläche zum Schließen meinst du?
Brian R. Bondy
Zum Beispiel schließen mit "ALT + F4"
Bohn
@Oliver Nicht die gleiche Frage.
Strg S

Antworten:

97

Angenommen, Sie fragen nach WinForms, können Sie das Ereignis FormClosing () verwenden . Das Ereignis FormClosing () wird jedes Mal ausgelöst, wenn ein Formular geschlossen werden soll.

Um festzustellen, ob der Benutzer entweder auf X oder auf Ihren CloseButton geklickt hat, können Sie ihn über das Absenderobjekt abrufen. Versuchen Sie, den Absender als Button-Steuerelement zu verwenden, und überprüfen Sie beispielsweise den Namen "CloseButton".

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

Andernfalls musste ich nie unterscheiden, ob auf X oder CloseButton geklickt wurde, da ich für das FormClosing-Ereignis etwas Bestimmtes ausführen wollte, z. B. das Schließen aller MdiChildren vor dem Schließen des MDIContainerForm oder das Überprüfen von Ereignissen auf nicht gespeicherte Änderungen. Unter diesen Umständen müssen wir meiner Meinung nach nicht von beiden Tasten unterscheiden.

Das Schließen mit ALT+ F4löst auch das Ereignis FormClosing () aus, da eine Nachricht an das Formular gesendet wird, die besagt, dass das Formular geschlossen werden soll. Sie können das Ereignis abbrechen, indem Sie die Einstellung

FormClosingEventArgs.Cancel = true. 

In unserem Beispiel würde dies übersetzt werden

e.Cancel = true.

Beachten Sie den Unterschied zwischen den Ereignissen FormClosing () und FormClosed () .

FormClosing tritt auf, wenn das Formular die zu schließende Nachricht empfangen hat und überprüft, ob es etwas zu tun hat, bevor es geschlossen wird.

FormClosed tritt auf, wenn das Formular tatsächlich geschlossen wird, also nachdem es geschlossen wurde.

Hilft das?

Will Marcouiller
quelle
Ja, danke für die "Cast" -Idee, hatte diese Technik mit Delphi 7 verwendet, aber vergessen, dasselbe in C # zu tun
Bohn
Dies ist in der Tat ein Port von Delphi nach .NET. =) Ich bin froh, dass ich geholfen habe!
Will Marcouiller
1
Wenn ich diesen Code verwende, wird "Objektreferenz nicht auf eine Instanz eines Objekts festgelegt" angezeigt.
Nate S.
31
Das ist falsch. Sie können den Absender nicht in eine Schaltfläche umwandeln, da dies das Formular selbst ist. Dies löst eine Ausnahme aus.
Xtro
Ich erhalte eine Button-Cast-Ausnahme, wenn ich meinen Absender in eine Referenzvariable vom Typ "Button" umwandle. Es heißt, dass dies nicht in einen Absender vom Typ "Form" umgewandelt werden kann.
Theburningfire
78

Die CloseReasonAufzählung, die Sie in MSDN gefunden haben, dient nur dazu zu überprüfen, ob der Benutzer die App geschlossen hat oder ob sie aufgrund eines Herunterfahrens oder durch den Task-Manager usw. geschlossen wurde.

Sie können je nach Grund verschiedene Aktionen ausführen, z.

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
}

Aber wie Sie vermutet haben, gibt es keinen Unterschied zwischen dem Klicken auf die Schaltfläche x oder dem Klicken mit der rechten Maustaste auf die Taskleiste und dem Klicken auf "Schließen" oder Drücken von Alt F4usw. Alles endet in einem CloseReason.UserClosingGrund.

Philip Daubmeier
quelle
11
Verwenden des Standards Close (); scheint für mich CloseReason.UserClosing auszulösen. Nicht sicher warum.
Dan W
Ich fand dies nützlich, wenn ich versuchte, das Schließen eines untergeordneten MDI-Formulars durch Benutzeraktion im Formular zu blockieren, aber nicht, wenn das übergeordnete Formular geschlossen wird.
Steve Pettifer
1
Dies beantwortet die Frage nicht, sondern zählt das Problem nur weiter auf.
Robert Koernke
Wie binden Sie das Ereignis an die Methode?
user2924019
43

Die "X" -Taste wird registriert, DialogResult.Cancelsodass eine weitere Option darin besteht, das auszuwerten DialogResult.

Wenn Sie mehrere Schaltflächen in Ihrem Formular haben, ordnen Sie wahrscheinlich bereits unterschiedliche Schaltflächen zu, DialogResultund dies gibt Ihnen die Möglichkeit, den Unterschied zwischen den einzelnen Schaltflächen zu erkennen.

(Beispiel : 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
quelle
1
In meinem Fall ist dies nützlicher als die akzeptierte Antwort. Da dem 'X' DialogResult.Cancel zugewiesen ist, können sie durch Zuweisen eines anderen Werts zur Abbrechen-Schaltfläche leicht unterschieden und entsprechend behandelt werden.
MickeyfAgain_BeforeExitOfSO
3
Das funktioniert in meinem Fall nicht. Wenn Sie 'X' drücken, DialogResultbleibt None. Was könnte das Problem sein?
Bhaskar
1
@Bhaskar, wenn Sie Ihren Dialog instanziieren, stellen Sie sicher, dass für jede Schaltfläche in Ihrem Dialog das entsprechende Dialogergebnis festgelegt ist. Ich hatte oben ein Beispiel angegeben, aber keinen Codeblock zum Anzeigen der Dialogdeklaration erstellt.
AlexScript
@ Bhaskar: Drücken von XMarken DialogResultenthalten Cancel, nicht None. Das Zuweisen Nonezu Ihrer Schaltfläche entspricht dem Festlegen der .DialogResultEigenschaft, und wenn Sie form.Close()von der Ereignisbehandlungsroutine Ihrer Schaltfläche aus aufrufen , form.DialogResultwird diese enthalten Cancel. Wenn Sie nur einen anderen Wert als Noneoder Cancelallen Schaltflächen zum Schließen von Formularen zuweisen , können Sie die gewünschte Unterscheidung treffen.
mklement0
8

Wie kann man feststellen, ob das Formular geschlossen wurde, indem man auf die Schaltfläche X klickt oder Close()Code aufruft ?

Sie können sich nicht auf den Grund für das Schließen von Formularargumenten verlassen, denn wenn der Benutzer auf die Schaltfläche X in der Titelleiste klickt oder das Formular mit Alt + F4 schließt oder das Systemmenü zum Schließen des Formulars verwendet oder das Formular durch Aufrufen der Close()Methode geschlossen wird, werden alle In den oben genannten Fällen wird der nahe Grund vom Benutzer geschlossen, was nicht das gewünschte Ergebnis ist.

Um zu unterscheiden, ob das Formular durch die X-Schaltfläche oder nach der CloseMethode geschlossen wurde, können Sie eine der folgenden Optionen verwenden:

  • Behandeln WM_SYSCOMMANDund überprüfen Sie SC_CLOSEeine Flagge und setzen Sie sie.
  • Überprüfen Sie StackTrace, ob einer der Frames einen CloseMethodenaufruf enthält .

Beispiel 1 - Griff 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()");
}

Beispiel 2 - Überprüfen von 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
quelle
1
Schön gemacht. Schade, dass du zu spät zur Party gekommen bist - schwer mit den älteren, bereits hoch bewerteten Antworten zu konkurrieren.
mklement0
1
@ mklement0 Ich hoffe, zukünftige Benutzer finden es nützlich. Ich habe die Antwort gepostet, weil keine der anderen Antworten das Problem richtig lösen könnte, und es ist ziemlich seltsam für eine Frage mit dieser Anzahl von Ansichten und hoch bewerteten (nicht funktionierenden) Antworten!
Reza Aghaei
6

Es bestimmt, wann die Anwendung geschlossen werden soll, wenn ein Formular geschlossen wird (wenn Ihre Anwendung nicht an ein bestimmtes Formular angehängt ist).

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

Ich verwende in meinen Anwendungen immer eine Formularschlussmethode, die alt + x von meinem Exit-Button abfängt , alt + f4 oder ein anderes Formularschlussereignis wurde initiiert. In allen meinen Klassen ist der Klassenname mstrClsTitle = "grmRexcel"in diesem Fall als private Zeichenfolge definiert , eine Exit-Methode, die die Formularschließmethode aufruft, und eine Formularschließmethode. Ich habe auch eine Erklärung für die Form Closing Method - this.FormClosing = My Form Closing Form Closing method name.

Der Code dafür:

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;
                }
            }
        }
    }
}
Tee Shot
quelle
2

Sie können versuchen, einen Ereignishandler aus dem Entwurf wie folgt hinzuzufügen: Öffnen Sie das Formular in der Entwurfsansicht, öffnen Sie das Eigenschaftenfenster oder drücken Sie F4, klicken Sie auf die Schaltfläche in der Ereignissymbolleiste, um Ereignisse im Formularobjekt anzuzeigen, suchen Sie das FormClosing-Ereignis in der Gruppe Verhalten und doppelklicken Sie darauf. Referenz: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bdee708-db4b-4e46-a99c-99726fa25cfb/how-do-i-add-formclosing-event?forum=csharpgeneral

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

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

Wenn die Bedingung ausgeführt wird, wenn der Benutzer auf 'X' klickt oder die Schaltfläche im Formular schließt. Das else kann verwendet werden, wenn der Benutzer für einen anderen Zweck auf Alt + f4 klickt

Phani Kiran
quelle
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
quelle
1

Ich stimme der DialogResultLösung als der direkteren zu.

In VB.NET ist jedoch eine CloseReasonTypumwandlung erforderlich, um die Eigenschaft -Property abzurufen

    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
DrMarbuse
quelle
0

Ich musste auch die Schließfunktion in der Methode "InitializeComponent ()" des Formulars registrieren:

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

Meine "FormClosing" -Funktion ähnelt der angegebenen Antwort ( 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");
    }
}

Noch etwas zu erwähnen: Es gibt auch eine "FormClosed" -Funktion, die nach "FormClosing" auftritt. Um diese Funktion zu verwenden, registrieren Sie sie wie folgt:

this.FormClosed += MainPage_FormClosed;

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