简体   繁体   中英

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
}

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.

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.

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();

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

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM