Search code examples
c#winformssynchronizationbegininvokeinvokerequired

Why Form.Close wait MessageBox.Show?


look this please:

var form = new Form();

form.Shown += (_, __) =>
{
    var timer = new System.Windows.Forms.Timer { Interval = 1000 };

    timer.Tick += (x, xx) =>
    {
        timer.Stop();
        GC.KeepAlive(timer);
        timer.Dispose();

        form.Close();

        Application.DoEvents(); // no effect 

        // it will cause form keep show
        MessageBox.Show("asdf");

        // but if this, that's fine
        // BeginInvoke(new Action(() => MessageBox.Show("asdf")));
    };

    timer.Start();
};

form.ShowDialog();

form.Close before MessageBox.Show, but form will not close until close msgBox, please help.

--end--

all in code, why need more words? all in code, why need more words? all in code, why need more words?


Solution

  • When you show a form as modal using ShowDialog(), calling Close in fact sends a WM_CLOSE message and then it sets DialogResult to Cancel which acts as a flag for modal message loop to exit the loop.

    So Close will not close or hide the modal dialog immediately. Then after finishing the modal message loop, the modal dialog will be hide (but not destroyed).

    MessageBox method also blocks the execution of code, so codes after the message box will execute just after closing the message box. So now it's clear why after calling Close, first the MessageBox shows then after closing message box, form closes.

    Just to make it easier to understand, here is a pseudo-code which shows what is happening when you call ShowDialog in your code:

    Form Shows
    While Form.DialogResult != None
    {
        Form.Close → Sends WM_CLOSE → Sets Form.DialogResult = Cancel 
        MessageBox.Show and wait until MessageBox closes
    }
    Form Hides
    

    Just keep in mind, Close is not equal to return, it means the code which you have after Close will run as well. Here the code is MessageBox which blocks the loop until after MessageBox closes.

    To hide the dialog immediately, replace the form.Close() with form.Hide(), this way without waiting for the loop, you are commanding the form to get hidden. But it doesn't mean the form has been closed and therefore lines of code which you have after ShowDialog will not run until after the loop finishes.

    For more information about how Close and ShowDialog work, you may want to take a look at a Windows Forms source code, specially the following lines: