Як дізнатися, що користувач натиснув “X” або “Закрити”?


94

У MSDN я дізнався CloseReason.UserClosing, що користувач вирішив закрити форму, але я думаю, це однаково як для натискання кнопки X, так і для натискання кнопки закриття. То як я можу розрізнити ці два у своєму коді?

Дякую усім.


2
яку кнопку закриття ви маєте на увазі?
Брайан Р. Бонді

наприклад закриття "ALT + F4"
Бон,


@Oliver Не те саме питання.
Ctrl S

Відповіді:


95

Припускаючи, що ви просите WinForms, ви можете використовувати подію FormClosing () . Подія FormClosing () запускається щоразу, коли форма закривається.

Щоб виявити, чи натиснув користувач кнопку X або вашу кнопку CloseButton, ви можете отримати його через об'єкт відправника. Спробуйте відіслати відправника як елемент керування кнопкою та перевірте, можливо, його назву "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.
}

В іншому випадку мені ніколи не потрібно було розрізняти, натиснуто X або CloseButton, оскільки я хотів виконати щось конкретне щодо події FormClosing, наприклад, закрити всі MdiChildren перед закриттям MDIContainerForm або перевірити події на незбережені зміни. За цих обставин нам не потрібно, на мою думку, відрізняти будь-яку кнопку.

Закриття за допомогою ALT+ F4також ініціює подію FormClosing (), оскільки воно надсилає повідомлення до форми із повідомленням про закриття. Ви можете скасувати подію, встановивши

FormClosingEventArgs.Cancel = true. 

У нашому прикладі це означало б

e.Cancel = true.

Зверніть увагу на різницю між подіями FormClosing () та FormClosed () .

Закриття форми відбувається, коли форма отримала повідомлення, яке потрібно закрити, і перевірте, чи має він щось робити до закриття.

FormClosed відбувається тоді, коли форма фактично закрита, тож після її закриття.

Це допомагає?


Так, дякую за ідею "Актори", використовував цю техніку з Delphi 7, але забув зробити те саме в C #
Бон,

Насправді це порт з Delphi до .NET. =) Я рада, що допомогла!
Will Marcouiller

1
Я отримую "Посилання на об'єкт, не встановлене на екземпляр об'єкта", коли я використовую цей код.
Nate S.

33
Це неправильно. Ви не можете передати відправника на кнопку, оскільки це сама форма. Це викликає виняток.
Xtro

1
Зверніть увагу, що це НЕПРАВИЛЬНА відповідь. Будь ласка, не голосуйте за це.
Najeeb

79

CloseReasonПерерахування ви знайшли на MSDN тільки з метою перевірки закрита користувач додаток, або це було пов'язано з простою, або закривається менеджером завдань, і т.д ...

Ви можете робити різні дії відповідно до причини, наприклад:

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
}

Але, як ви вже здогадалися, немає різниці між натисканням кнопки x або натисканням правої кнопки на панелі завдань та натисканням кнопки «закрити», натисканням Alt F4тощо. Все це закінчується CloseReason.UserClosingпричиною.


11
Використання стандартного Close (); схоже, запускає CloseReason.UserClosing для мене. Не знаю, чому.
Dan W

Я знайшов це корисним при спробі заблокувати закриття дочірньої форми MDI дією користувача на форму, але не тоді, коли закривається батьківський.
Steve Pettifer

1
Це не дає відповіді на запитання, лише перелічіть питання далі.
Robert Koernke

Як ви прив'язуєте подію до методу?
user2924019

43

Кнопка "X" реєструється як DialogResult.Cancelінший варіант - оцінити DialogResult.

Якщо у вашій формі є кілька кнопок, ви, мабуть, вже асоціюєте різні DialogResults з кожною, і це забезпечить вам засоби, щоб визначити різницю між кожною кнопкою.

(Приклад: 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
У моєму випадку це корисніше прийнятої відповіді. Оскільки 'X' присвоєно DialogResult.Cancel, присвоєння деяких інших значень кнопці скасування легко їх розрізнити і обробити речі належним чином.
MickeyfAgain_BeforeExitOfSO

3
У моєму випадку це не працює. При натисканні 'X' DialogResultзалишається None. У чому може бути проблема?
Бхаскар,

1
@Bhaskar, коли ви створюєте екземпляр свого діалогового вікна, обов’язково встановіть відповідний DialogResult для кожної кнопки у вашому діалоговому вікні. Я навів приклад вище, але не створив блок коду для відображення оголошення Dialog.
AlexScript

@Bhaskar: Натискання Xзмушує DialogResultстримувати Cancel, а не None. Призначення Noneвашій кнопці те саме, що взагалі не встановлювати її .DialogResultвластивість, і якщо ви телефонуєте form.Close()з обробника подій вашої кнопки, form.DialogResultбуде містити Cancel. Тільки присвоєння значення, відмінного від Noneабо Cancelдля всіх ваших кнопок закриття форми, дозволить вам зробити бажане розрізнення.
mklement0

9

Як визначити, чи закрилася форма, натиснувши кнопку X або ввівши Close()код?

Ви не можете покладатися на близьку причину аргументів події закриття форми, тому що якщо користувач натискає кнопку X у рядку заголовка або закриває форму за допомогою Alt + F4 або використовує системне меню, щоб закрити форму або форму закрити методом виклику Close(), усі у вищезазначених випадках, близька причина буде закрита користувачем, що не є бажаним результатом.

Щоб розрізнити, чи форма закрита кнопкою X або Closeметодом, можна скористатися одним із наведених нижче варіантів:

  • Обробляйте, WM_SYSCOMMANDперевіряйте SC_CLOSEта встановлюйте прапор.
  • Перевірте, StackTraceщоб побачити, чи містить якийсь кадр Closeвиклик методу.

Приклад 1 - Ручка 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()");
}

Приклад 2 - Перевірка 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
Чудово зроблено. Шкода, що ви спізнилися на вечірку - важко скласти конкуренцію старшим, і без того високо оціненим відповідям.
mklement0

1
@ mklement0 Сподіваюся, майбутнім користувачам це буде корисно. Я опублікував відповідь, оскільки жодна з інших відповідей не могла б правильно вирішити проблему, і це досить дивно для питання, що має таку кількість переглядів та високо оцінені (неробочі) відповіді!
Реза Агаей

7

Він визначає, коли слід закрити заявку, якщо форма закрита (якщо ваша заявка не приєднана до певної форми).

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

5

Я завжди використовую метод Закриття форми у своїх програмах, який ловить alt + x від моєї кнопки виходу, alt + f4 або ініційована інша подія закриття форми. Усі мої класи мають назву класу, визначену як приватний рядок, mstrClsTitle = "grmRexcel"у цьому випадку метод Exit, який викликає метод закриття форми та метод закриття форми. У мене також є заява щодо методу закриття форми - this.FormClosing = My Form Closing Form Closing method name.

Код для цього:

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

Ви можете спробувати додати обробник подій з дизайну таким чином: Відкрийте форму у поданні дизайну, відкрийте вікно властивостей або натисніть F4, натисніть кнопку панелі інструментів подій, щоб переглянути події в об’єкті Форми, знайдіть подію FormClosing у групі Поведінка та двічі клацніть на ній. Посилання: 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;
            }
        }

якщо умова буде виконуватися, коли користувач натискає 'X' або кнопку закрити на формі. Інше можна використовувати, коли користувач натискає Alt + f4 з будь-якою іншою метою


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

Я погоджуюсь із рішенням, DialogResultяке є більш прямим.

Однак у VB.NET для отримання параметра CloseReason-Property потрібен typecast

    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

Мені також довелося зареєструвати функцію закриття в методі форми "InitializeComponent ()":

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

Моя функція "FormClosing" схожа на дану відповідь ( 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");
    }
}

Зазначимо ще одне: існує також функція "FormClosed", яка виникає після "FormClosed". Щоб використовувати цю функцію, зареєструйте її, як показано нижче:

this.FormClosed += MainPage_FormClosed;

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

0

Я зробив щось подібне.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.