Po czym poznać, że użytkownik kliknął przycisk „X” lub „Zamknij”?


97

W MSDN dowiedziałem CloseReason.UserClosingsię, że użytkownik zdecydował się zamknąć formularz, ale wydaje mi się, że to samo dotyczy zarówno kliknięcia przycisku X, jak i przycisku zamykania. Jak więc mogę rozróżnić te dwa elementy w moim kodzie?

Dziękuje wszystkim.


2
który przycisk zamknięcia masz na myśli?
— Brian R. Bondy

na przykład zamknięcie przez „ALT + F4”
— Bohn


@Oliver To nie to samo pytanie.
— Ctrl S

Odpowiedzi:


96

Zakładając, że pytasz o WinForms, możesz użyć zdarzenia FormClosing () . Zdarzenie FormClosing () jest wyzwalane za każdym razem, gdy formularz ma zostać zamknięty.

Aby wykryć, czy użytkownik kliknął X lub przycisk CloseButton, możesz go uzyskać za pośrednictwem obiektu nadawcy. Spróbuj rzutować nadawcę jako kontrolkę Button i sprawdź, na przykład, jej nazwę „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.
}

W przeciwnym razie nigdy nie musiałem rozróżniać, czy kliknięto X lub CloseButton, ponieważ chciałem wykonać coś konkretnego na zdarzeniu FormClosing, na przykład zamknąć wszystkie MdiChildren przed zamknięciem MDIContainerForm lub sprawdzić zdarzenia pod kątem niezapisanych zmian. W tych okolicznościach nie musimy według mnie odróżniać żadnego z przycisków.

Zamknięcie przez ALT+ F4wyzwoli również zdarzenie FormClosing (), ponieważ wysyła komunikat do Form z informacją o zamknięciu. Możesz anulować wydarzenie, ustawiając

FormClosingEventArgs.Cancel = true. 

W naszym przykładzie oznaczałoby to

e.Cancel = true.

Zwróć uwagę na różnicę między zdarzeniami FormClosing () i FormClosed () .

FormClosing ma miejsce, gdy formularz otrzymał wiadomość do zamknięcia i sprawdź, czy ma coś do zrobienia, zanim zostanie zamknięty.

FormClosed występuje, gdy formularz jest faktycznie zamknięty, a więc po jego zamknięciu.

czy to pomaga?


Tak, dzięki za pomysł „Cast”, użyłem tej techniki w Delphi 7, ale zapomniałem zrobić to samo w C #
— Bohn.

1
Kiedy używam tego kodu, otrzymuję komunikat „Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu”.
— Nate S.

34
To jest źle. Nie możesz przesłać nadawcy do przycisku, ponieważ jest to sam formularz. To zgłasza wyjątek.
— Xtro

2
Pamiętaj, że jest to NIEPRAWIDŁOWA odpowiedź. Proszę nie głosować za tym.
— Najeeb

1
Sprawdź odpowiedź przez użytkownika @Reza Aghaei
— Najeeb

79

CloseReasonWyliczenie znalazłeś na MSDN jest tylko w celu sprawdzenia, czy użytkownik zamknął aplikację, czy to ze względu na zamknięcie lub zamknięte przez menedżera zadań, etc ...

Możesz wykonać różne czynności, w zależności od powodu, na przykład:

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
}

Ale tak jak się domyślasz, nie ma różnicy między kliknięciem przycisku x a kliknięciem prawym przyciskiem myszy na pasku zadań i kliknięciem „zamknij” lub naciśnięciem przycisku Alt F4itp. Wszystko kończy się na CloseReason.UserClosingprzyczynie.


11
Korzystanie ze standardowego Close (); wydaje się wyzwalać CloseReason.UserClosing dla mnie. Nie pewny dlaczego.
— Dan W

Zauważyłem, że jest to przydatne, gdy próbowałem zablokować zamknięcie formularza podrzędnego MDI przez działanie użytkownika na formularzu, ale nie, gdy rodzic jest zamykany.
— Steve Pettifer,

1
To nie odpowiada na pytanie, a jedynie wylicza dalej problem.
— Robert Koernke,

Jak powiązać zdarzenie z metodą?
— user2924019

43

Przycisk „X” rejestruje się jako, DialogResult.Cancelwięc inną opcją jest ocena pliku DialogResult.

Jeśli masz wiele przycisków w swoim formularzu, prawdopodobnie już przypisujesz różne przyciski DialogResultdo każdego z nich, co zapewni Ci sposób odróżnienia każdego przycisku.

(Przykład: 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
W moim przypadku jest to bardziej przydatne niż zaakceptowana odpowiedź. Ponieważ „X” jest przypisane do DialogResult.Cancel, przypisanie innej wartości do przycisku anulowania pozwala łatwo je rozróżnić i odpowiednio obsługiwać.
— MickeyfAgain_BeforeExitOfSO

3
To nie działa w moim przypadku. Po naciśnięciu „X” DialogResultpozostaje None. Jaki może być problem?
— Bhaskar

1
@Bhaskar, podczas tworzenia wystąpienia okna dialogowego upewnij się, że ustawiłeś odpowiedni DialogResult dla każdego przycisku w oknie dialogowym. Podałem powyższy przykład, ale nie utworzyłem bloku kodu, aby wyświetlić deklarację Dialog.
— AlexScript,

@Bhaskar: Naciśnięcie Xpowoduje , że DialogResultzawiera Cancel, a nie None. Przypisanie Nonedo przycisku jest tym samym, co nie ustawianie jego .DialogResultwłaściwości w ogóle, a jeśli wywołasz form.Close()z modułu obsługi zdarzeń przycisku, form.DialogResultbędzie zawierać Cancel. Tylko przypisanie wartości innej niż Nonelub Canceldo wszystkich przycisków zamykających formularz umożliwi dokonanie pożądanego rozróżnienia.
— mklement0

11

Jak sprawdzić, czy formularz został zamknięty poprzez kliknięcie przycisku X lub wywołanie Close()kodu?

Nie można polegać na zamkniętej przyczynie argumentów zdarzenia zamykającego formularz, ponieważ jeśli użytkownik kliknie przycisk X na pasku tytułowym lub zamknie formularz za pomocą Alt + F4 lub użyje menu systemowego do zamknięcia formularza lub formularz zostanie zamknięty przez wywołanie Close()metody, wszystko w powyższych przypadkach, blisko powód zostanie zamknięty przez Użytkownika, co nie jest pożądanym rezultatem.

Aby rozróżnić, czy formularz jest zamknięty przyciskiem X, czy Closemetodą, możesz użyć jednej z następujących opcji:

  • Obsługuj WM_SYSCOMMANDi sprawdź SC_CLOSEi ustaw flagę.
  • Sprawdź, StackTraceczy którakolwiek z ramek zawiera Closewywołanie metody.

Przykład 1 - Uchwyt 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()");
}

Przykład 2 - sprawdzanie 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
Ładnie wykonane. Szkoda, że ​​spóźniłeś się na imprezę - trudno konkurować ze starszymi, już wysoko ocenionymi odpowiedziami.
— mklement0

1
@ mklement0 Mam nadzieję, że przyszli użytkownicy uznają to za przydatne. Opublikowałem odpowiedź, ponieważ żadna inna odpowiedź nie rozwiązałaby problemu poprawnie i jest to dość dziwne, jak na pytanie z taką liczbą wyświetleń i wysoko ocenianymi (niedziałającymi) odpowiedziami!
— Reza Aghaei

Wielkie dzięki. Użyłem StackTrace()i rozwiązałem problem.
— Ali Majed HA

@AliMajedHA Cool!
— Reza Aghaei

7

Określa, kiedy zamknąć wniosek, jeśli formularz jest zamknięty (jeśli wniosek nie jest dołączony do konkretnego formularza).

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

5

Zawsze używam metody Form Close w moich aplikacjach, która przechwytuje alt + x z mojego przycisku wyjścia, alt + f4 lub inne zdarzenie zamykające formularz zostało zainicjowane. Wszystkie moje klasy mają mstrClsTitle = "grmRexcel"w tym przypadku nazwę klasy zdefiniowaną jako Prywatny ciąg , metodę Exit, która wywołuje metodę zamykania formularza i metodę zamykania formularza. Mam również oświadczenie dotyczące metody zamykania formularza - this.FormClosing = My Form Closing Form Closing method name.

Kod do tego:

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

Możesz spróbować dodać obsługę zdarzeń z projektu w następujący sposób: Otwórz formularz w widoku projektu, otwórz okno właściwości lub naciśnij klawisz F4, kliknij przycisk paska narzędzi zdarzenia, aby wyświetlić zdarzenia w obiekcie Form, znajdź zdarzenie FormClosing w grupie Zachowanie i kliknij je dwukrotnie. Źródła: 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;
            }
        }

Jeśli warunek zostanie wykonany, gdy użytkownik kliknie przycisk „X” lub przycisk zamknięcia w formularzu. Tej else można użyć, gdy użytkownik kliknie Alt + f4 w jakimkolwiek innym celu


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

Zgadzam się z rozwiązaniem DialogResult-Solution jako prostszym.

Jednak w VB.NET do uzyskania opcji CloseReason-Property wymagane jest rzutowanie typu

    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

Musiałem również zarejestrować funkcję zamykającą w metodzie „InitializeComponent ()” formularza:

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

Moja funkcja „FormClosing” wygląda podobnie do podanej odpowiedzi ( 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");
    }
}

Jeszcze jedna rzecz, o której warto wspomnieć: istnieje również funkcja „FormClosed”, która występuje po „FormClosing”. Aby użyć tej funkcji, zarejestruj ją, jak pokazano poniżej:

this.FormClosed += MainPage_FormClosed;

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

0

Zrobiłem coś takiego.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.