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


94

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

谢谢大家


2
您是指哪个关闭按钮?
Brian R. Bondy 2010年

例如,以“ ALT + F4”结束
Bohn 2010年


@Oliver不一样的问题。
Ctrl S

Answers:


95

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

要检测用户是单击X还是单击CloseButton,可以通过sender对象获得它。尝试将发送方转换为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或对未保存的更改进行事件检查。在我看来,在这种情况下,我们不需要区别于两个按钮。

ALT+ 关闭F4也会触发FormClosing()事件,因为它将向窗体发送一条消息,要求关闭。您可以通过设置

FormClosingEventArgs.Cancel = true. 

在我们的示例中,这将转化为

e.Cancel = true.

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

当表单收到要关闭的消息,并在关闭消息之前验证它是否有事时,就会发生FormClosing。

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

这有帮助吗?


是的,感谢“发布”的想法,已经在Delphi 7中使用了此技术,但忘记了在C#中做同样的事情
Bohn 2010年

实际上,这是从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子表单的关闭而不是关闭父表单时,我发现这很有用。
史蒂夫·佩蒂弗

1
这没有回答问题,仅进一步列举问题。
罗伯特·科恩克

您如何将事件绑定到方法?
user2924019 '19

43

“ X”按钮注册为,DialogResult.Cancel因此另一个选择是评估DialogResult

如果表单上有多个按钮,则可能已经将不同的DialogResults与每个按钮相关联,这将为您提供分辨每个按钮之间差异的方法。

(实施例: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;
            }
        }
    }

1
就我而言,这比公认的答案有用。由于为“ X”分配了DialogResult.Cancel,因此将其他一些值分配给“取消”按钮很容易区分它们并适当地处理事情。
MickeyfAgain_BeforeExitOfSO

3
就我而言,这不起作用。当按“ X”时,DialogResult保持不变None。可能是什么问题呢?
Bhaskar 2015年

1
@Bhaskar,在实例化对话框时,请确保为对话框中的每个按钮设置适当的DialogResult。我在上面提供了一个示例,但是没有创建代码块来显示Dialog声明。
AlexScript

@Bhaskar:按X品牌DialogResult包含Cancel,不None。分配None给按钮与根本不设置按钮的.DialogResult属性相同,并且如果您form.Close()从按钮的事件处理程序中调用,form.DialogResult将包含Cancel。仅为除所有关闭表单按钮之外的值NoneCancel为其分配值将使您能够进行所需的区分。
mklement0

9

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

您不能依赖于表单关闭事件args的关闭原因,因为如果用户单击标题栏上的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

7

它确定是否关闭了表单(如果您的应用程序未附加到特定表单)何时关闭应用程序。

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

5

我总是在我的应用程序中使用Form Close方法,该方法从退出按钮,alt + f4或启动了另一个关闭表单事件中捕获alt + x 。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;
                }
            }
        }
    }
}


1
if (this.DialogResult == DialogResult.Cancel)
        {

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

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


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中,必须进行类型转换才能获得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
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.