Bagaimana cara mengetahui pengguna telah mengklik "X" atau tombol "Tutup"?

97

Di MSDN saya CloseReason.UserClosingmengetahui bahwa pengguna telah memutuskan untuk menutup formulir tetapi saya rasa itu sama untuk mengklik tombol X atau mengklik tombol tutup. Jadi bagaimana saya bisa membedakan keduanya dalam kode saya?

Terima kasih semuanya.

Bohn
sumber
2
tombol tutup mana yang kamu maksud?
Brian R. Bondy
misalnya ditutup dengan "ALT + F4"
Bohn
@Oliver Bukan pertanyaan yang sama.
Ctrl S

Jawaban:

96

Dengan asumsi Anda meminta WinForms, Anda dapat menggunakan acara FormClosing () . Acara FormClosing () dipicu setiap saat formulir ditutup.

Untuk mendeteksi apakah pengguna mengklik X atau CloseButton Anda, Anda mungkin mendapatkannya melalui objek pengirim. Cobalah untuk mentransmisikan pengirim sebagai kontrol Tombol, dan verifikasi mungkin untuk namanya "CloseButton", misalnya.

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

Jika tidak, saya tidak pernah perlu membedakan apakah X atau CloseButton diklik, karena saya ingin melakukan sesuatu yang spesifik pada acara FormClosing, seperti menutup semua MdiChildren sebelum menutup MDIContainerForm, atau memeriksa acara untuk perubahan yang belum disimpan. Dalam keadaan ini, menurut saya, kita tidak perlu membedakan tombol mana pun.

Menutup dengan ALT+ F4juga akan memicu acara FormClosing (), karena mengirimkan pesan ke Formulir yang bertuliskan tutup. Anda dapat membatalkan acara dengan menyetel

FormClosingEventArgs.Cancel = true. 

Dalam contoh kami, ini akan diterjemahkan menjadi

e.Cancel = true.

Perhatikan perbedaan antara acara FormClosing () dan FormClosed () .

FormClosing terjadi ketika formulir menerima pesan untuk ditutup, dan memverifikasi apakah ada sesuatu yang harus dilakukan sebelum ditutup.

FormClosed terjadi saat formulir benar-benar ditutup, jadi setelah itu ditutup.

Apakah ini membantu?

Akan Marcouiller
sumber
Ya, terima kasih atas ide "Cast", telah menggunakan teknik ini dengan Delphi 7 tetapi lupa melakukan hal yang sama di C #
Bohn
1
Saya mendapatkan 'Referensi objek tidak disetel ke contoh objek' ketika saya menggunakan kode ini.
Nate S.
34
Ini salah. Anda tidak dapat mentransmisikan pengirim ke tombol karena itu formulir itu sendiri. Ini melempar pengecualian.
Xtro
2
Harap dicatat bahwa ini adalah jawaban yang SALAH. Tolong jangan suka ini.
Najeeb
1
Lihat jawaban oleh pengguna @Reza Aghaei
Najeeb
79

The CloseReasonpencacahan Anda temukan di MSDN hanya untuk tujuan memeriksa apakah pengguna ditutup aplikasi, atau itu karena shutdown, atau ditutup oleh task manager, dll ...

Anda dapat melakukan berbagai tindakan, sesuai dengan alasannya, seperti:

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
}

Tapi seperti yang Anda duga, tidak ada perbedaan antara mengklik tombol x, atau mengklik kanan bilah tugas dan mengklik 'tutup', atau menekan Alt F4, dll. Semuanya berakhir dengan suatu CloseReason.UserClosingalasan.

Philip Daubmeier
sumber
11
Menggunakan standar Close (); tampaknya memicu CloseReason.UserClosing untuk saya. Tidak yakin kenapa.
Dan W
Saya menemukan ini berguna ketika mencoba memblokir penutupan formulir anak MDI oleh tindakan pengguna pada formulir tetapi tidak ketika induknya ditutup.
Steve Pettifer
1
Ini tidak menjawab pertanyaan, hanya menjelaskan masalah lebih lanjut.
Robert Koernke
Bagaimana Anda mengikat acara ke metode?
pengguna2924019
43

Tombol "X" mendaftar DialogResult.Cancelsehingga opsi lainnya adalah mengevaluasi DialogResult.

Jika Anda memiliki beberapa tombol pada formulir Anda, Anda mungkin sudah menghubungkan DialogResulttombol yang berbeda ke masing-masing tombol dan ini akan memberi Anda cara untuk membedakan setiap tombol.

(Contoh: 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
sumber
1
Dalam kasus saya, ini lebih berguna daripada jawaban yang diterima. Karena 'X' ditetapkan ke DialogResult.Cancel, menetapkan beberapa nilai lain ke tombol batal dengan mudah membedakannya dan menanganinya dengan tepat.
MickeyfAgain_BeforeExitOfSO
3
Ini tidak berhasil dalam kasus saya. Saat menekan 'X', DialogResulttetap ada None. Apa masalahnya?
Bhaskar
1
@Bhaskar, ketika Anda membuat contoh dialog Anda, pastikan untuk menyetel DialogResult yang sesuai untuk setiap tombol dalam dialog Anda. Saya telah memberikan contoh di atas tetapi tidak membuat blok kode untuk menampilkan deklarasi Dialog.
AlexScript
@Bhaskar: Menekan Xmembuat DialogResultberisi Cancel, bukan None. Menetapkan Noneke tombol Anda sama dengan tidak menyetel .DialogResultpropertinya sama sekali, dan jika Anda memanggil form.Close()dari event handler tombol Anda, form.DialogResultakan berisi Cancel. Hanya menetapkan nilai selain Noneatau Cancelke semua tombol penutup formulir akan memungkinkan Anda membuat perbedaan yang diinginkan.
mklement0
12

Bagaimana cara mendeteksi jika formulir ditutup dengan mengklik tombol X atau dengan memanggil Close()kode?

Anda tidak dapat mengandalkan alasan penutupan dari argumen acara penutupan formulir, karena jika pengguna mengklik tombol X pada bilah judul atau menutup formulir menggunakan Alt + F4 atau menggunakan menu sistem untuk menutup formulir atau formulir ditutup dengan Close()metode panggilan , semua kasus di atas, alasan penutupan akan ditutup oleh Pengguna yang bukan hasil yang diinginkan.

Untuk membedakan apakah formulir ditutup oleh tombol X atau dengan Closemetode, Anda dapat menggunakan salah satu opsi berikut:

  • Tangani WM_SYSCOMMANDdan periksa SC_CLOSEserta setel bendera.
  • Periksa StackTraceuntuk melihat apakah ada frame yang berisi Closepemanggilan metode.

Contoh 1 - Pegangan 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()");
}

Contoh 2 - Memeriksa 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
sumber
1
Selamat. Sayang sekali Anda terlambat ke pesta - sulit untuk bersaing dengan jawaban yang lebih tua dan sudah mendapat suara terbanyak.
mklement0
1
@ mklement0 Saya berharap pengguna di masa mendatang merasa berguna. Saya memposting jawabannya karena bukan dari jawaban lain yang dapat menyelesaikan masalah dengan benar dan cukup aneh untuk pertanyaan yang memiliki jumlah penayangan ini dan jawaban yang sangat dipilih (tidak berfungsi)!
Reza Aghaei
Terima kasih banyak. Saya telah menggunakan StackTrace()dan itu memecahkan masalah.
Ali Majed HA
@AliajedHA Keren!
Reza Aghaei
7

Ini menentukan kapan harus menutup aplikasi jika formulir ditutup (jika aplikasi Anda tidak dilampirkan ke formulir tertentu).

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

Saya selalu menggunakan metode Tutup Formulir dalam aplikasi saya yang menangkap alt + x dari Tombol keluar saya, alt + f4 atau acara penutupan formulir lainnya dimulai. Semua kelas saya memiliki nama kelas yang didefinisikan sebagai string pribadi mstrClsTitle = "grmRexcel"dalam kasus ini, metode Keluar yang memanggil Metode Penutupan Formulir dan Metode Penutupan Formulir. Saya juga memiliki pernyataan untuk Metode Penutupan Formulir - this.FormClosing = My Form Closing Form Closing method name.

Kode untuk ini:

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;
                }
            }
        }
    }
}
Tembakan Tee
sumber
1
if (this.DialogResult == DialogResult.Cancel)
        {

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

jika kondisi akan dieksekusi ketika pengguna mengklik 'X' atau tombol tutup pada formulir. Yang lain dapat digunakan saat pengguna mengklik Alt + f4 untuk tujuan lain

phani kiran
sumber
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
sumber
1

Saya setuju dengan DialogResult-Solusi sebagai yang lebih lurus ke depan.

Dalam VB.NET bagaimanapun, typecast diperlukan untuk mendapatkan 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
DrMarbuse
sumber
0

Saya juga harus mendaftarkan fungsi penutupan di dalam metode "InitializeComponent ()" formulir:

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

Fungsi "FormClosing" saya terlihat mirip dengan jawaban yang diberikan ( 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");
    }
}

Satu hal lagi untuk disebutkan: Ada juga fungsi "FormClosed" yang muncul setelah "FormClosing". Untuk menggunakan fungsi ini, daftarkan seperti yang ditunjukkan di bawah ini:

this.FormClosed += MainPage_FormClosed;

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

Saya telah melakukan sesuatu seperti ini.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
PachecoDt
sumber