Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Form.Show() called from a console freezes the GUI

Tags:

c#

winforms

I am trying to instantiate a Form directly from a Console App.
For some weird reason, when I call Form1.Show() the newly created Form doesn't draw all its controls and Freezes (HourGlass icon). However, when I call ShowDialog(), everything goes fine, except that I need to go back to Console but I can't, so it is not an option...
What should I do to make my Form display correctly ? Am I missing something here ?

OrderControlForm OrderControlBox = new OrderControlForm();
OrderControlBox.BuyEvent += new OrderControl.BuyDelegate(doBuy);
OrderControlBox.SellEvent += new OrderControl.SellDelegate(doSell);
OrderControlBox.Show();

The above code is called in response to a command entered by the Console user.

EDIT : Here is the working code :

        Thread mThread = new Thread(delegate()
        {
            StratControlBox = new StratControl(StratIDs);
            StratControlBox.ShowDialog();
        });

        mThread.SetApartmentState(ApartmentState.STA);

        mThread.Start();

I still don't see why I had to call ShowDialog() rather than Show().
When I use the latter the Form just "disappears" instantly after paint.

like image 220
Mehdi LAMRANI Avatar asked Jul 10 '12 19:07

Mehdi LAMRANI


2 Answers

The reason is because ShowDialog performs its own message loop, whereas Show does not. Instead of calling Show, you would need to call Application.Run, which performs a message loop. However, since it synchronously loops, processing incoming window messages until the form is closed, it will be effectively no different than calling ShowDialog.

Therefore, if you want to show the form asynchronously, you would need to do so from another thread. However, just to be safe, make sure the new thread uses apartment threading by calling newThread.SetApartmentState(ApartmentState.STA);.

Also, I'd recommend only showing one main form from one UI thread. If that main form shows other forms from its own thread, that's fine, but, if you start trying to show multiple forms, each from their own thread, it can cause problems.

Regarding Your Update

The reason that calling Show from the thread does not work is two-fold. First, it is synchronous, so it does not return until the form is closed. That is important because as soon as execution leaves your anonymous method, the thread will terminate. So, when you call Show, it immediately returns, then leaves your method thereby terminating the thread.

Second, even if the form did stay open, it would be unresponsive, just as before, for all the same reasons. WinForms require a message loop that keeps looking for new incoming window messasges and processing them. The message loop calls a method called WndProc. Without a message loop calling the WndProc method to process the incoming window messages, the form will be totally unresponsive to users. For instance, when the mouse driver notifies windows that the user has pressed the mouse button, windows will then posts a WM_MOUSEDOWN message to your application's message queue. If you don't have code somewhere that is constantly looping looking to see if there are any messages in the queue and acting upon them, you'll never get the mouse down event.

As I mentioned above, the ShowDialog method performs its own message loop, so it works, but Show does not. Show assumes that it is being called by an already running message loop. If for some reason you don't want to call ShowDialog, you can call Application.Run(StratControlBox), instead. The Run method will show the form for you and then stay in a message loop until the form is closed. Therefore, it is a synchronous call, just like ShowDialog, so your thread won't terminate until the form is closed.

like image 162
Steven Doggart Avatar answered Oct 02 '22 14:10

Steven Doggart


What should I do to make my Form display correctly ? Am I missing something here ?

The problem is that a Console application is not a Windows Application, and doesn't have the appropriate "plumbing" to process windows messages. Without this, the form can't process items correctly, including the basic messages to "draw", etc.

This is handled normally by using Application.Run to start the message processing. However, this blocks until the form closes (so the console won't continue to "run"). The way to handle this is to move your console code into a separate thread, and use Application.Run with your form like a normal windows application.

like image 26
Reed Copsey Avatar answered Oct 02 '22 16:10

Reed Copsey