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.

Bohn
źródło
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?

Will Marcouiller
źródło
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.

Philip Daubmeier
źródło
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;
            }
        }
    }
AlexScript
źródło
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");
}
Reza Aghaei
źródło
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();
    }
derloopkat
źródło
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;
                }
            }
        }
    }
}
Tee Shot
źródło
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

lisa
źródło
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

phani kiran
źródło
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
źródło
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
DrMarbuse
źródło
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
}
IV Tak jak
źródło
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
        }
    }
PachecoDt
źródło