75

When a user clicks the X button on a form, how can I hide it instead of closing it?

I have tried this.hide() in FormClosing but it still closes the form.

Danny Beckett
  • 20,529
  • 24
  • 107
  • 134
iTEgg
  • 8,212
  • 20
  • 73
  • 107

7 Answers7

127

Like so:

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

(via Tim Huffman)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Alex
  • 64,178
  • 48
  • 151
  • 180
  • how do i re enable to e.cancel = false? so that i can close the form later? – iTEgg Jan 07 '10 at 17:14
  • 1
    i think i will just have add a flag that lets me now if i want to close it for real or not. – iTEgg Jan 07 '10 at 17:38
  • 6
    **Attention!** You should check `e.CloseReason` (look the other answer). Otherwise, the form will not close when the form is closing by system shutdown or other events. – SQL Police Apr 07 '16 at 14:58
  • 1
    but how can I show icon's that on hidden icon of windows task bar? – Esi Oct 08 '18 at 09:42
  • For those curious about the last question, the answer is: Icon appIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); – WiiLF Dec 21 '21 at 03:26
63

I've commented in a previous answer but thought I'd provide my own. Based on your question this code is similar to the top answer but adds the feature another mentions:

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

If the user is simply hitting the X in the window, the form hides; if anything else such as Task Manager, Application.Exit(), or Windows shutdown, the form is properly closed, since the return statement would be executed.

SQL Police
  • 4,127
  • 1
  • 25
  • 54
LizB
  • 2,193
  • 16
  • 17
8

From MSDN:

To cancel the closure of a form, set the Cancel property of the FormClosingEventArgs passed to your event handler to true.

So cancel then hide.

Danny Beckett
  • 20,529
  • 24
  • 107
  • 134
Jorge Córdoba
  • 51,063
  • 11
  • 80
  • 130
5

Based on other response, you can put it in your form code :

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        base.OnFormClosing(e);
        if (e.CloseReason == CloseReason.UserClosing)
        {
            e.Cancel = true;
            Hide();
        }
    }

According MSDN, the override is preferred:

The OnFormClosing method also allows derived classes to handle the event without attaching a delegate. This is the preferred technique for handling the event in a derived class.

Orace
  • 7,822
  • 30
  • 45
4

If you want to use the show/hide method I've actually done this myself for a menu structure a game I've recently done... This is how I did it:

Create yourself a button and for what you'd like to do, for example a 'Next' button and match the following code to your program. For a next button in this example the code would be:

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.

Here is a snippet of the code I used in my game to help you understand what I'm trying to explain:

    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
    }

So there is a show/hide method few lines of code, rather than doing a massive piece of code for such a simple task. I hope this helps to solve your problem.

yprez
  • 14,854
  • 11
  • 55
  • 70
Craig
  • 41
  • 1
3

Note that when doing this (several answers have been posted) that you also need to find a way to ALLOW the user to close the form when they really want to. This really becomes a problem if the user tries to shut down the machine when the application is running, because (at least on some OS) this will stop the OS from shutting down properly or efficiently.

The way I solved this was to check the stack trace - there are differences between when the user tries to click the X vs when the system tries to end the application in preparation for shutdown.

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;
    }
}
Danny Beckett
  • 20,529
  • 24
  • 107
  • 134
Michael Bray
  • 14,998
  • 7
  • 42
  • 68
  • completely agree with what you've said. – Alex Jan 07 '10 at 16:12
  • im talking about your previous comment not the last one. yes although the above code works on my child form. but because of the e.Cancel i can no longer close down the parent form! any clues? – iTEgg Jan 07 '10 at 16:14
  • for the above code to work i need to add namespace for StackTrace. please help by stating the namespace. – iTEgg Jan 07 '10 at 16:21
  • found: using System.Diagnostics; Error 2 'System.Diagnostics.Log' is a 'type' but is used like a 'variable' C:\Users\iAmjad\Documents\Visual Studio 2008\Projects\BattleShips\Nietzsche.Battleships\NietzscheBattleships\NetworkForm.cs 56 21 NietzscheBattleships – iTEgg Jan 07 '10 at 16:27
  • 2
    this seems unnecessarily complicated - why fight with modal form behavior in this way when you can just use modeless forms instead? – John Knoeller Jan 07 '10 at 16:29
  • im using modeless form. and the application is not closing because of e.cancel = true; – iTEgg Jan 07 '10 at 16:35
  • @ikurtz: for the error with Log - take out my Log(...) statements... that references other code that I posted and I only left it in so that you can see the situation that that code block is handling. – Michael Bray Jan 07 '10 at 16:45
  • i took out the log. but on the parent form Application.Exit(); still does not close the application. – iTEgg Jan 07 '10 at 17:10
  • i tried MessageBox.Show(methodName); and miExit_Click or PeekMessage does not show up in the list. – iTEgg Jan 07 '10 at 17:18
  • 2
    @John I agree, Assuming .NET 2.0 or greater, Why not just check for e.CloseReason == CloseReason.WindowsShutDown .... http://msdn.microsoft.com/en-us/library/system.windows.forms.closereason%28VS.80,loband%29.aspx – LizB Jan 07 '10 at 17:49
  • ikurtz: sorry man, you have to read between the lines... that code is from real production code and as such is somewhat dependent on it. the mi_Exit is a menu item that I use to allow the user to ACTUALLY exit the form when they want to. PeekMessage of course only shows up when you are engaging in a system shutdown. However, reading Shane's comment, I think it is obvious that the method I presented (which was legacy code from .NET 1.1) isn't needed if you are using .NET 2.0, so just use his suggestion (I'm going to update my code, too!!) – Michael Bray Jan 07 '10 at 22:21
  • Worst solution I ever seen. – QtRoS Jan 13 '15 at 08:10
  • @QtRoS care to elaborate on why it's so bad, aside from the reasons already discussed? Please make sure you have read *all* of the comments before responding. – Michael Bray Jan 13 '15 at 16:45
2

This is the behavior of Modal forms. When you use form.ShowDialog() you are asking for this behavior. The reason for this is that form.ShowDialog doesn't return until the form is hidden or destroyed. So when the form is hidden, the pump inside form.ShowDialog destroys it so that it can return.

If you want to show and hide a form, then you should be using the Modeless dialog model http://msdn.microsoft.com/en-us/library/39wcs2dh(VS.80).aspx

form.Show() returns immediately, you can show and hide this window all you want and it will not be destroyed until you explicitly destroy it.

When you use modeless forms that are not children of a modal form, then you also need to run a message pump using Application.Run or Application.DoEvents in a loop. If the thread that creates a form exits, then the form will be destroyed. If that thread doesn't run a pump then the forms it owns will be unresponsive.

Edit: this sounds like the sort of thing that the ApplicationContext is designed to solve. http://msdn.microsoft.com/en-us/library/system.windows.forms.applicationcontext.aspx

Basically, you derive a class from ApplicationContext, pass an instance of your ApplicationContext as an argument to 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);

Your application context will need to know when it's ok to exit the application and when having the form(s) hidden should not exit the application. When it's time for the app to exit. Your application context or form can call the application context's ExitThread() method to terminate the message loop. At that point Application.Run() will return.

Without knowing more about the heirarchy of your forms and your rules for deciding when to hide forms and when to exit, it's impossible to be more specific.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • could you extend on Application.doevents please? i think i need it. adding e.Cancel = false; in parent form closing didnt work either. – iTEgg Jan 07 '10 at 16:38
  • sorry i wasnt clear. the application shuts down when i click the X. but Application.Exit(); doesn't work. – iTEgg Jan 07 '10 at 16:41
  • i have two forms. parent and child though this is not mdi. – iTEgg Jan 07 '10 at 16:43
  • If this is the child form, and the parent form is modal, then you are fine. If this is the parent form, then you should probably use `Application.Run(MyAppContext)` – John Knoeller Jan 07 '10 at 16:46
  • im doing in program: Application.Run(new gameForm()); but that i think is not the issue. for some reason when i do e.cancel = true; on the child form. the parent form takes this value and the application.exit() does not close the parent form. – iTEgg Jan 07 '10 at 16:51
  • also this works: System.Environment.Exit(-1); works and quits but Application.Exit(); still does not work. – iTEgg Jan 07 '10 at 16:56
  • ok. the application.Exit() is not working because the app cannot close the child form (e.Cancel = true)!# how do i re enable the e.Cancel = false? – iTEgg Jan 07 '10 at 17:25
  • forget about the whole e.Cancel = false trick. It's bad advice, you should throw that code out. Just use form.Show instead of form.ShowDialog and your form will stop getting destroyed when you hide it. – John Knoeller Jan 07 '10 at 17:41