sczdavos
sczdavos

Reputation: 2063

C# calling form.show() from another thread

if I call form.show() on a WinForms object from another thread, the form will throw an exception. Is where any way I can add a new, visible form to the main app thread? Otherwise, how can I open the form without stopping my currently executing thread?

Here is my sample code. I am attempting to start a thread and then execute some work within that thread. As the work progresses, I will show the form.

public void Main()
{
    new Thread(new ThreadStart(showForm)).Start();
    // Rest of main thread goes here...
}

public void showForm() 
{
    // Do some work here.
    myForm form = new myForm();
    form.Text = "my text";
    form.Show();
    // Do some more work here
}

Upvotes: 26

Views: 68067

Answers (6)

Silver
Silver

Reputation: 31

I was about to show message box from constructor. You can replace MessageBox with your windows form.

public Form1()
{
    Thread thread = new Thread(new ThreadStart(() => {
                                MessageBox.Show("Server is connected");
                            }));
    thread.Start();
}

Upvotes: 0

Rowi123
Rowi123

Reputation: 95

After searching the web and not finding a good solution, I came up with my own:

public async Task FunctionWhereIStartForm( Func<Delegate,object>invoke )
{
  //this is on another thread

 invoke( new MethodInvoker( ( ) =>
              {
                new formFoo().Show( );
              } ) );
}

Then call like this:

await FunctionWhereIStartForm(Invoke);

Upvotes: -3

Sam Tigle
Sam Tigle

Reputation: 403

If your use case is to display a GUI while the Main GUI Thread is busy (like loading bar) you can do the following:

private void MethodWithLoadingBar()
{
    Thread t1 = new Thread(ShowLoading);
    t1.Start();
    // Do the Main GUI Work Here
    t1.Abort();
}

private void ShowLoading()
{
    Thread.Sleep(1000); //Uncomment this if you want to delay the display 
                        //(So Loading Bar only shows if the Method takes longer than 1 Second)
    loadingGUI = new LoadingGUI();
    loadingGUI.label1.Text = "Try to Connect...";
    loadingGUI.ShowDialog();
}

My LoadingGUI is a simple Form with a public Label and a ProgressBar with Style set to Marquee

Upvotes: -1

Mohamad Taheri
Mohamad Taheri

Reputation: 11

The best way by my experience:

var ac = (ReportPre)Application.OpenForms["ReportPre"];
Thread shower = new Thread(new ThreadStart(() =>
    {
        if (ac == null)
        {                
            this.Invoke((MethodInvoker)delegate () {
                ac = new ReportPre();
                ac.Show();
            });       
        }
        else
        {
            this.Invoke((MethodInvoker)delegate
            {
                pictureBox1.Visible = true;
            });
            if (ac.InvokeRequired)
            {
                ac.Invoke(new MethodInvoker(delegate {
                    ac.Hide();
                    ac.Show();
                }));                          
            }
        }
    }));
shower.Start();

Upvotes: 1

Hiroshi Maekawa
Hiroshi Maekawa

Reputation: 209

You should call Application.Run() after you call form.Show(). For example:

public void showForm() 
{
    // Do some work here.
    myForm form = new myForm();
    form.Text = "my text";
    form.Show();
    Application.Run();
    // Do some more work here
}

As for the details behind why, this msdn post may help.

Upvotes: 13

Ted Spence
Ted Spence

Reputation: 2678

Try using an invoke call:

public static Form globalForm;

void Main()
{
    globalForm = new Form();
    globalForm.Show();
    globalForm.Hide();
    // Spawn threads here
}

void ThreadProc()
{
    myForm form = new myForm();
    globalForm.Invoke((MethodInvoker)delegate() {
        form.Text = "my text";
        form.Show();
    });
}

The "invoke" call tells the form "Please execute this code in your thread rather than mine." You can then make changes to the WinForms UI from within the delegate.

More documentation about Invoke is here: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

EDIT: You will need to use a WinForms object that already exists in order to call invoke. I've shown here how you can create a global object; otherwise, if you have any other windows objects, those will work as well.

Upvotes: 38

Related Questions