点击关闭按钮时隐藏表单而不是关闭

76
当用户在表单上点击X按钮时,我应该如何隐藏它而不是关闭它? 我已经尝试在FormClosing中使用this.hide(),但它仍然会关闭表单。
7个回答

128

就像这样:

private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing) 
    {
        e.Cancel = true;
        Hide();
    }
}

(通过Tim Huffman提供)


如何重新启用 e.cancel = false?这样我就可以稍后关闭表单了。 - iTEgg
1
我想我只需要添加一个标志,让我知道是否真的想要关闭它。 - iTEgg
6
注意!您应该检查e.CloseReason(请查看另一个答案)。否则,在系统关闭或其他事件关闭表单时,表单将无法关闭。 - SQL Police
1
但是我该如何显示在Windows任务栏隐藏图标中的图标? - Esi
对于那些对最后一个问题感到好奇的人,答案是:Icon appIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); - WiiLF

64

我之前已经在另一个回答中留言,但想自己提供一下我的看法。基于你的问题,这段代码与顶部答案类似,但增加了另一个功能。

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing) 
    {
        e.Cancel = true;
        Hide();
    }
}
如果用户只是点击窗口中的X,表单会隐藏;如果使用任务管理器、Application.Exit()或Windows关闭,表单将被正确关闭,因为return语句将被执行。

8

来自MSDN:

要取消窗体的关闭,请将传递给事件处理程序的FormClosingEventArgsCancel属性设置为true

所以先取消再隐藏。


5
根据其他的回复,你可以将它放在你的表单代码中:
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        base.OnFormClosing(e);
        if (e.CloseReason == CloseReason.UserClosing)
        {
            e.Cancel = true;
            Hide();
        }
    }

根据MSDN的说明,override是首选方法:

使用OnFormClosing方法也可让派生类在不附加委托的情况下处理事件。这是在派生类中处理事件的首选技术。


4

如果你想使用显示/隐藏的方法,我其实已经为最近完成的一个游戏的菜单结构做过这个。这是我做的:

创建一个按钮,并为你想要完成的操作编写代码,例如“下一页”按钮,在此示例中,代码如下:

btnNext.Enabled = true; //This enabled the button obviously
this.Hide(); //Here is where the hiding of the form will happen when the button is clicked
Form newForm = new newForm(); //This creates a new instance object for the new form
CurrentForm.Hide(); //This hides the current form where you placed the button.

这是我在游戏中使用的一段代码片段,帮助你理解我想要解释的内容:
    private void btnInst_Click(object sender, EventArgs e) 
    {
        btnInst.Enabled = true; //Enables the button to work
        this.Hide(); // Hides the current form
        Form Instructions = new Instructions(); //Instantiates a new instance form object 
        Instructions.Show(); //Shows the instance form created above
    }

因此,有一个显示/隐藏方法可以用几行代码实现,而不是为一个简单的任务编写大量的代码。我希望这能帮助解决你的问题。


3
请注意,当进行此操作(已发布多个答案)时,您还需要找到一种方法允许用户在他们真正想要关闭表单时关闭它。如果用户尝试在应用程序运行时关闭机器,这将成为一个问题,因为(至少在某些操作系统上)这将导致操作系统无法正常或高效地关闭。
我解决这个问题的方法是检查堆栈跟踪-当用户尝试单击时,与系统尝试结束应用程序以准备关机时存在差异。
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    StackTrace trace = new StackTrace();
    StackFrame frame;
    bool bFoundExitCommand = false;
    for (int i = 0; i < trace.FrameCount; i++)
    {
        frame = trace.GetFrame(i);
        string methodName = frame.GetMethod().Name;
        if (methodName == "miExit_Click")
        {
            bFoundExitCommand = true;
            Log("FormClosing: Found Exit Command ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
        }
        if (methodName == "PeekMessage")
        {
            bFoundExitCommand = true;
            Log("FormClosing: Found System Shutdown ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
        }
        Log("FormClosing: frame.GetMethod().Name = {0}", LogUtilityLevel.Debug4, methodName);
    }
    if (!bFoundExitCommand)
    {
        e.Cancel = true;
        this.Visible = false;
    }
    else
    {
        this.Visible = false;
    }
}

完全同意你所说的。 - Alex
2
这似乎过于复杂了 - 为什么要用这种方式来处理模态表单行为,而不是直接使用无模态表单呢? - John Knoeller
2
@John 我同意,假设使用 .NET 2.0 或更高版本,为什么不直接检查 e.CloseReason == CloseReason.WindowsShutDown ...。http://msdn.microsoft.com/zh-cn/library/system.windows.forms.closereason%28VS.80,loband%29.aspx - LizB
我见过的最糟糕的解决方案。 - QtRoS
@QtRoS,除了已经讨论过的原因之外,能否详细说明为什么这么糟糕?请确保在回复之前阅读所有评论。 - Michael Bray
显示剩余8条评论

2
这是模态表单的行为。当你使用 form.ShowDialog() 时,你在“请求”这种行为。原因是,form.ShowDialog 不会返回,直到表单被隐藏或销毁。因此,当表单被隐藏时,form.ShowDialog 中的消息循环将其销毁以便返回。
如果你想显示和隐藏一个窗体,那么你应该使用非模态对话框模型。详情请参见http://msdn.microsoft.com/en-us/library/39wcs2dh(VS.80).aspx form.Show() 立即返回,你可以随意显示和隐藏此窗口,它不会被销毁,直到你显式地销毁它。
当你使用非模态窗体而不是模态窗体的子级时,你还需要使用 Application.RunApplication.DoEvents 在循环中运行消息循环。如果创建表单的线程退出,则该表单将被销毁。如果该线程不运行消息循环,则它拥有的表单将无响应。
编辑:这听起来像 ApplicationContext 的设计目的之一。详情请参阅http://msdn.microsoft.com/en-us/library/system.windows.forms.applicationcontext.aspx 基本上,你需要从 ApplicationContext 派生一个类,将你的 ApplicationContext 实例作为参数传递给 Application.Run()
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.

MyApplicationContext context = new MyApplicationContext();

// Run the application with the specific context. 
Application.Run(context);

您的应用程序上下文需要知道何时可以退出应用程序,以及隐藏表单时不应退出应用程序。当应用程序退出时,您的应用程序上下文或表单可以调用应用程序上下文的ExitThread()方法来终止消息循环。此时,Application.Run()将返回。

如果不了解您的表单层次结构和决定何时隐藏表单何时退出的规则,就无法更具体地说明。


请问您能详细说明一下 Application.DoEvents 吗?我认为我需要它。在父窗体关闭事件中添加 e.Cancel = false; 也没有起作用。 - iTEgg
抱歉我没有表达清楚。当我点击X时,应用程序会关闭。但是Application.Exit();不起作用。 - iTEgg
如果这是子窗体,并且父窗体是模态的,那么你没问题。如果这是父窗体,则应该使用 Application.Run(MyAppContext) - John Knoeller
也可以使用以下代码实现退出功能:System.Environment.Exit(-1); 可以正常退出,但 Application.Exit() 仍然无法正常工作。 - iTEgg
好的。应用程序无法关闭子窗体(e.Cancel = true),因此application.Exit()无法正常工作!#如何重新启用e.Cancel = false? - iTEgg
显示剩余3条评论

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