如何知道用户已点击“X”或“关闭”按钮?

How to know user has clicked "X" or the "Close" button?

提问人:Bohn 提问时间:4/21/2010 最后编辑:CronoBohn 更新时间:6/26/2023 访问量:383777

问:

在MSDN中,我发现用户已决定关闭表单 但我想单击 X 按钮或单击关闭按钮都是一样的。 那么,如何在代码中区分这两者呢?CloseReason.UserClosing

谢谢大家。

C# .NET Win窗体

评论

2赞 Brian R. Bondy 4/21/2010
你指的是哪个关闭按钮?
0赞 Bohn 4/21/2010
例如,以“ALT+F4”结束
2赞 Oliver 4/22/2010
复制:stackoverflow.com/questions/1623756/...
0赞 Reza Aghaei 11/26/2019
使用 X 按钮/Alt+F4/系统菜单或使用 Close 方法检测窗体是否已关闭

答:

109赞 Will Marcouiller 4/21/2010 #1

假设您要求 WinForms,则可以使用 FormClosing() 事件。每当窗体关闭时,都会触发事件 FormClosing()。

若要检测用户是否单击了 X 或 CloseButton,可以通过发送方对象获取它。例如,尝试将发送方强制转换为 Button 控件,并验证其名称“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 事件执行特定操作,例如在关闭 MDIContainerForm 之前关闭所有 MdiChildren,或者检查未保存的更改。在这种情况下,根据我的说法,我们不需要与任何一个按钮区分开来。

关闭方式 + 也会触发 FormClosing() 事件,因为它会向 Form 发送一条消息,提示关闭。您可以通过设置ALTF4

FormClosingEventArgs.Cancel = true. 

在我们的示例中,这将翻译为

e.Cancel = true.

请注意 FormClosing() 和 FormClosed() 事件之间的区别。

当窗体收到要关闭的消息时,将发生 FormClosure,并在关闭之前验证它是否有事情要做。

FormClosed 发生在窗体实际关闭时,因此在窗体关闭之后。

这有帮助吗?

评论

1赞 Nate S. 4/20/2015
当我使用此代码时,我得到“对象引用未设置为对象的实例”。
46赞 Xtro 5/7/2015
这是错误的。您不能将发件人强制转换为按钮,因为它是表单本身。这会引发异常。
3赞 mklement0 11/26/2019
@WillMarcouiller:请考虑修复有关能够投射到按钮的错误信息,因为您的答案是公认的,并且有如此多的赞成票。
0赞 Xan-Kun Clark-Davis 7/26/2023
我不明白?SO 被机器人接管了吗?一个明显错误的答案怎么能被这么多人点赞。还是我错过了什么?
89赞 Philip Daubmeier 4/21/2010 #2

您在 MSDN 上找到的枚举只是为了检查用户是否关闭了应用程序,或者是由于关机,还是被任务管理器关闭等......CloseReason

您可以根据原因执行不同的操作,例如:

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按钮,或右键单击任务栏并单击“关闭”,或按,等等,这两者之间没有区别。这一切都以 CloseReason.UserClosing 原因告终。AltF4

评论

13赞 Dan W 3/3/2013
使用标准的 Close();似乎为我触发了 CloseReason.UserClosering。不知道为什么。
0赞 Steve Pettifer 9/16/2014
我发现这在尝试通过用户对窗体的操作阻止关闭 MDI 子窗体时很有用,但在关闭父窗体时则不然。
2赞 Robert Koernke 9/21/2017
这并不能回答问题,只是进一步列举问题。
57赞 AlexScript 12/2/2011 #3

“X”按钮注册,因此另一个选项是评估 .DialogResult.CancelDialogResult

如果您的窗体上有多个按钮,您可能已经将不同的按钮与每个按钮相关联,这将为您提供区分每个按钮的方法。DialogResult

(例如: ,btnSubmit.DialogResult = DialogResult.OKbtnClose.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;
            }
        }
    }

评论

2赞 MickeyfAgain_BeforeExitOfSO 2/8/2013
就我而言,这比公认的答案更有用。由于“X”被分配了 DialogResult.Cancel,因此为取消按钮分配一些其他值可以很容易地区分它们并适当地处理事情。
5赞 Bhaskar 3/17/2015
这在我的情况下不起作用。当按“X”时,保持 .可能是什么问题?DialogResultNone
1赞 AlexScript 4/11/2017
@Bhaskar,在实例化对话框时,请确保为对话框中的每个按钮设置相应的 DialogResult。我在上面提供了一个示例,但没有创建一个代码块来显示 Dialog 声明。
1赞 mklement0 11/26/2019
@Bhaskar:按 make contain ,而不是 。分配给按钮与根本不设置其属性相同,如果从按钮的事件处理程序调用,则将包含 .只有为所有表单关闭按钮以外的值分配一个值,才能做出所需的区分。XDialogResultCancelNoneNone.DialogResultform.Close()form.DialogResultCancelNoneCancel
1赞 phani kiran 1/12/2012 #4
if (this.DialogResult == DialogResult.Cancel)
        {

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

如果条件将在用户单击表单上的“X”或关闭按钮时执行。当用户出于任何其他目的单击 Alt+f4 时,可以使用 else

8赞 derloopkat 10/20/2012 #5

它确定在窗体关闭时何时关闭应用程序(如果您的应用程序未附加到特定窗体)。

    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        if (Application.OpenForms.Count == 0) Application.Exit();
    }
2赞 lisa 8/25/2016 #6

您可以尝试从设计中添加事件处理程序,如下所示:在设计视图中打开窗体,打开属性窗口或按 F4,单击事件工具栏按钮查看 Form 对象上的事件,在“行为”组中找到 FormClosing 事件,然后双击它。 参考: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bdee708-db4b-4e46-a99c-99726fa25cfb/how-do-i-add-formclosing-event?forum=csharpgeneral

5赞 Tee Shot 5/15/2018 #7

我总是在我的应用程序中使用 Form Close 方法,该方法从我的退出按钮、alt + f4 或其他表单关闭事件中捕获 alt + x。在本例中,我的所有类都具有定义为 Private string 的类名,即调用 Form Closing 方法和 Form Closing 方法的 Exit 方法。我还有一个关于表单关闭方法的声明 - 。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赞 Dragan Menoski 12/2/2018 #8
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赞 DrMarbuse 11/9/2019 #9

我同意 -Solution 作为更直接的解决方案。DialogResult

但是,在 VB.NET 中,需要类型转换才能获取 -PropertyCloseReason

    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
36赞 Reza Aghaei 11/10/2019 #10

如何通过单击 X 按钮或调用代码来检测表单是否关闭?Close()

您不能依赖表单关闭事件参数的关闭原因,因为:
如果用户单击标题栏上的 X 按钮或使用 Alt + F4 关闭表单或使用系统菜单关闭表单或


通过调用 Close() 方法关闭表单,

对于上述所有情况,关闭原因将是 Closed by User (),这不是期望的结果。CloseReason.UserClosing

若要区分窗体是按 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");
}
2赞 IVIike 2/27/2020 #11

我还必须在表单的“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
}
1赞 Pacheco 8/21/2020 #12

我做过这样的事情。

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

评论

1赞 Kai Wang 10/27/2022
对我有用,建议
0赞 testtest 6/26/2023 #13

这种方法是无效的。

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");
}

本页参考。