在Windows窗体中覆盖标准关闭(X)按钮

79

当用户在Windows窗体应用程序中单击关闭(红色X)按钮时,我该如何更改其操作(使用C#语言编写)?

11个回答

141

您可以重写 OnFormClosing 来实现此功能。只需小心不要做出任何过于意外的行为,因为单击“X”来关闭窗体是一种被广泛理解的行为。

protected override void OnFormClosing(FormClosingEventArgs e)
{
    base.OnFormClosing(e);

    if (e.CloseReason == CloseReason.WindowsShutDown) return;

    // Confirm user wants to close
    switch (MessageBox.Show(this, "Are you sure you want to close?", "Closing", MessageBoxButtons.YesNo))
    {
    case DialogResult.No:
        e.Cancel = true;
        break;
    default:
        break;
    }        
}

3
我猜他想要最小化到托盘,但你得到了我的+1是因为你说了要小心。 - Sam Harwell
4
@Jon B - 你应该检查关闭原因。如果Windows正在关闭,你不想显示消息框。 - Philip Wallace
1
我将它改成了一个switch语句,这样我就不必向右滚动来阅读“==”右侧的内容了。 - Sam Harwell
2
@Philip:那取决于情况。每当有更改时,应该显示“您要保存吗”对话框,并且取消操作应该取消关闭。例如,这就是VS所发生的情况。如果必须显示讨厌的“您要关闭吗”的对话框,则最好不要显示,但如果选择显示,则在关闭时应该将其绕过。 - R. Martinho Fernandes
@Phillip - 这真的是推荐做法吗?我相信大多数应用程序都会询问您是否要保存对修改文件的更改,而不管关闭原因如何。 - vgru
显示剩余3条评论

21

如果您在关闭之前要求用户保存工作,他们是否提供异常处理? - Christopher B. Adkins

18

这些回答中缺乏的一点,也是新手们可能在寻找的,就是虽然拥有事件很好:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    // do something
}

如果您没有注册事件,它将不会做任何事情。请在类构造函数中添加此代码:

this.FormClosing += Form1_FormClosing;

对我来说,即使没有注册事件,它也起作用了,但可能取决于具体情况。 - undefined

10

可以通过覆盖OnFormClosing函数或者注册事件FormClosing来实现。

以下是在派生窗体中覆盖OnFormClosing函数的示例:

protected override void OnFormClosing(FormClosingEventArgs e)
{
   e.Cancel = true;
}

以下是阻止表单关闭事件处理程序的示例,它可以在任何类中:

private void FormClosing(object sender,FormClosingEventArgs e)
{  
   e.Cancel = true;
}

为了更加高级,检查FormClosingEventArgs的CloseReason属性以确保执行适当的操作。如果用户尝试关闭窗体,则您可能只想执行替代操作。


10

正如Jon B所说的那样,但您还需要检查ApplicationExitCallTaskManagerClosing的CloseReason:

protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (  e.CloseReason == CloseReason.WindowsShutDown 
        ||e.CloseReason == CloseReason.ApplicationExitCall
        ||e.CloseReason == CloseReason.TaskManagerClosing) { 
       return; 
    }
    e.Cancel = true;
    //assuming you want the close-button to only hide the form, 
    //and are overriding the form's OnFormClosing method:
    this.Hide();
}

3

在处理MDI容器的窗体时,处理x按钮点击事件非常有用。原因是关闭和关闭后事件首先与子级一起引发,最后与父级引发。因此,在某种情况下,用户单击x按钮关闭应用程序,而MDI父级会要求确认继续。如果他决定不关闭应用程序而继续进行任何操作,则子项将已处理关闭事件可能会丢失信息/工作等。

一种解决方案是拦截来自Windows消息循环的WM_CLOSE消息,例如在您的主应用程序窗体(即关闭、终止应用程序的窗体)中执行以下操作:

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x0010) // WM_CLOSE
        {
            // If we don't want to close this window 
            if (ShowConfirmation("Are you sure?") != DialogResult.Yes) return;
        }

        base.WndProc(ref m);
    }

3

1

这是一个非常常见的问题。这里有一个不错的答案:

VB.NET 在用户单击 X(关闭程序)时重载默认功能


如果您不喜欢将代码放在Form_Closing事件中,我只知道另一个选项就是使用一种我曾经用过一两次的“hack”。虽然不应该必须采用这种hack,但我还是给出了以下方法:


不要使用普通的关闭按钮。相反,创建您的表单,使其没有ControlBox。您可以通过在表单上设置ControlBox = false来实现这一点,在这种情况下,您仍将拥有跨越表单顶部的常规栏,或者您可以将表单的FormBorderStyle设置为“None”。如果您选择第二个选项,则顶部将没有任何栏,也没有其他可见边框,因此您必须通过在表单上绘制或艺术地使用Panel控件来模拟这些内容。
然后,您可以添加一个标准按钮,并使其看起来像一个关闭按钮,并在其中放置您的清理代码。在按钮事件结束时,只需调用this.Close()(C#)或Me.Close()(VB)。

为什么有人不会在处理关闭事件时感到舒适呢?在我看来,添加一个自定义(但令人信服的)关闭按钮并不是那么简单的事情(特别是当你考虑到过渡效果和Aero等内容时,这些通常是无痛获得的)。 - vgru
在我提供的答案中,那个提问者出于某些原因不想使用Form_Closing事件。我不确定他的理由是什么,但他不想使用它。在我使用这个技巧的时候,我只是想要一个不同外观的关闭按钮。 - David
换句话说,另一个答案既有链接(可以理解),又是复制粘贴的,这就是为什么一半的答案与问题无关,其余部分推荐了在此没有任何理由的东西。 - jwg

0

被接受的答案非常有效。我使用过的另一种方法是为主表单创建FormClosing方法。这与覆盖非常相似。我的示例是针对在单击表单上的关闭按钮时最小化到系统托盘的应用程序。

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (e.CloseReason == CloseReason.ApplicationExitCall)
        {
            return;
        }
        else
        {
            e.Cancel = true;
            WindowState = FormWindowState.Minimized;
        }            
    }

这将允许ALT+F4或应用程序中调用的任何内容Application.Exit();正常工作,而单击(X)将最小化应用程序。


0
感谢Jon。我用这个解决方案来处理多个组件的问题。当你关闭主窗体时,有些组件仍然在运行。为了关闭所有东西,我做了以下操作:
  protected override void OnFormClosing(FormClosingEventArgs e)
  {
   Environment.Exit(0);
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接