จะทราบได้อย่างไรว่าผู้ใช้คลิก“ X” หรือปุ่ม“ ปิด”


97

ใน MSDN ฉันพบCloseReason.UserClosingว่าผู้ใช้ตัดสินใจปิดแบบฟอร์ม แต่ฉันคิดว่ามันเหมือนกันสำหรับทั้งการคลิกปุ่ม X หรือคลิกปุ่มปิด แล้วฉันจะแยกความแตกต่างระหว่างสองสิ่งนี้ในรหัสของฉันได้อย่างไร

ขอบคุณทุกคน


2
คุณหมายถึงปุ่มปิดไหน
Brian R.Bondy

ตัวอย่างเช่นปิดด้วย "ALT + F4"
Bohn


@ โอลิเวอร์ไม่ใช่คำถามเดียวกัน
Ctrl S

คำตอบ:


96

สมมติว่าคุณขอ 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 ()เหตุการณ์

FormClosing เกิดขึ้นเมื่อแบบฟอร์มได้รับข้อความที่จะปิดและตรวจสอบว่ามีสิ่งที่ต้องทำก่อนที่จะปิดหรือไม่

FormClosed เกิดขึ้นเมื่อปิดฟอร์มจริงดังนั้นหลังจากปิดแล้ว

สิ่งนี้ช่วยได้หรือไม่?


ใช่ขอบคุณสำหรับแนวคิด "Cast" เคยใช้เทคนิคนี้กับ Delphi 7 แต่ลืมทำเช่นเดียวกันใน C #
Bohn

1
ฉันได้รับ 'การอ้างอิงวัตถุไม่ได้ตั้งค่าเป็นตัวอย่างของวัตถุ' เมื่อฉันใช้รหัสนี้
Nate S.

34
นี่เป็นสิ่งที่ไม่ถูกต้อง คุณไม่สามารถส่งผู้ส่งไปที่ปุ่มได้เนื่องจากเป็นแบบฟอร์มเอง สิ่งนี้ทำให้เกิดข้อยกเว้น
Xtro

2
โปรดทราบว่านี่เป็นคำตอบที่ไม่ถูกต้อง โปรดอย่าโหวตเพิ่ม
Najeeb

1
ตรวจสอบคำตอบโดยผู้ใช้ @Reza Aghaei
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
การใช้มาตรฐานปิด (); ดูเหมือนว่าจะเรียก CloseReason.UserClosing สำหรับฉัน ไม่แน่ใจว่าทำไม
Dan W

ฉันพบว่าสิ่งนี้มีประโยชน์เมื่อพยายามปิดกั้นการปิดแบบฟอร์มลูก MDI โดยการดำเนินการของผู้ใช้ในแบบฟอร์ม แต่ไม่ใช่เมื่อแม่ถูกปิด
Steve Pettifer

1
นี่ไม่ได้ตอบคำถามเพียง แต่ระบุปัญหาเพิ่มเติม
Robert Koernke

คุณผูกเหตุการณ์เข้ากับวิธีการอย่างไร?
user2924019

43

ปุ่ม "X" จะลงทะเบียนDialogResult.Cancelดังนั้นอีกทางเลือกหนึ่งคือการประเมินไฟล์DialogResult.

หากคุณมีปุ่มหลายปุ่มในแบบฟอร์มของคุณคุณอาจเชื่อมโยงปุ่มต่างๆกับแต่ละปุ่มอยู่แล้วDialogResultและสิ่งนี้จะช่วยให้คุณสามารถบอกความแตกต่างระหว่างแต่ละปุ่มได้

(ตัวอย่าง: 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อะไรคือปัญหา?
Bhaskar

1
@Bhaskar เมื่อคุณสร้างอินสแตนซ์กล่องโต้ตอบของคุณตรวจสอบให้แน่ใจว่าได้ตั้งค่า DialogResult ที่เหมาะสมให้กับแต่ละปุ่มในกล่องโต้ตอบของคุณ ฉันได้ให้ตัวอย่างไว้ข้างต้น แต่ไม่ได้สร้างบล็อกรหัสเพื่อแสดงการประกาศกล่องโต้ตอบ
AlexScript

@Bhaskar: การกดXทำให้DialogResultมีไม่Cancel NoneการกำหนดNoneให้กับปุ่มของคุณเป็นเช่นเดียวกับไม่ได้ตั้งค่าของ.DialogResultทรัพย์สินที่ทั้งหมดและถ้าคุณโทรform.Close()จากตัวจัดการเหตุการณ์ปุ่มของคุณจะมีform.DialogResult Cancelการกำหนดค่าอื่นที่ไม่ใช่NoneหรือCancelให้กับปุ่มปิดแบบฟอร์มทั้งหมดของคุณเท่านั้นที่จะช่วยให้คุณสร้างความแตกต่างที่ต้องการได้
mklement0

12

จะตรวจสอบได้อย่างไรว่าฟอร์มปิดโดยคลิกที่ปุ่ม 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 ฉันหวังว่าผู้ใช้ในอนาคตจะพบว่ามีประโยชน์ ฉันโพสต์คำตอบเพราะไม่มีคำตอบอื่นที่สามารถแก้ปัญหาได้อย่างถูกต้องและมันค่อนข้างแปลกสำหรับคำถามที่มีจำนวนการดูและคำตอบที่ได้รับการโหวตสูง (ไม่ได้ผล)!
Reza Aghaei

ขอบคุณมาก. ฉันได้ใช้StackTrace()และมันแก้ปัญหาได้
Ali Majed HA

@AliMajedHA เจ๋ง!
Reza Aghaei

7

กำหนดว่าจะปิดแอปพลิเคชันเมื่อใดหากปิดแบบฟอร์ม (หากใบสมัครของคุณไม่ได้แนบกับแบบฟอร์มเฉพาะ)

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

5

ฉันมักจะใช้วิธีการปิดฟอร์มในแอปพลิเคชันของฉันที่จับ alt + x จากปุ่มออกของฉัน, alt + f4หรือเหตุการณ์ปิดฟอร์มอื่นเริ่มต้นขึ้น คลาสทั้งหมดของฉันมีชื่อคลาสที่กำหนดเป็นสตริงส่วนตัวmstrClsTitle = "grmRexcel"ในกรณีนี้คือวิธีการออกที่เรียกใช้วิธีการปิดฟอร์มและวิธีการปิดฟอร์ม ฉันยังมีคำสั่งสำหรับแบบฟอร์มการปิดวิธีการ 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-Solution ว่าตรงไปตรงมามากกว่า

อย่างไรก็ตามใน VB.NET จำเป็นต้องใช้ typecast เพื่อรับ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

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" ซึ่งเกิดขึ้นหลังจาก "FormClosing" หากต้องการใช้ฟังก์ชันนี้ให้ลงทะเบียนตามที่แสดงด้านล่าง:

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
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.