C#在另一个线程中调用form.show()

26

如果我在另一个线程中调用WinForms对象的form.show()方法,该窗体将会抛出异常。有没有办法在主应用程序线程中添加新的可见窗体?否则,我该如何打开窗体而不停止当前正在执行的线程?

这是我的示例代码。我尝试启动一个线程,然后在该线程内执行一些工作。随着工作的进行,我将显示该窗体。

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
}

“表格不会在实际中显示。” - 我不知道这是什么意思。您是在询问如何显示一个“模态”对话框,同时又不会阻塞吗? - Ed S.
我想展示这个表单,但是我不知道该怎么做。 我的意思是上面的代码会显示表单,但只是短暂的,因为线程会退出并且表单也会随之消失。 如果我使用模态对话框,线程会停止并且表单会被展示。但是在关闭对话框后,下面的代码会继续执行,而这正是我不想要的。 - sczdavos
5个回答

38

尝试使用invoke调用:

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

"invoke"调用告诉表单:“请在你的线程中执行此代码,而不是我的。” 然后,您可以从委托中更改WinForms UI。

有关Invoke的更多文档,请参见: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

编辑:您需要使用已经存在的WinForms对象来调用invoke。 我在这里展示了如何创建全局对象;否则,如果您有任何其他窗口对象,那些也可以工作。


1
这对我不起作用。"在窗口句柄被创建之前,无法在控件上调用Invoke或BeginInvoke。" - sczdavos
1
嗯,好的,让我们稍微编辑一下。我还稍微改了一下你上面的问题,希望它仍然清晰明了。 - Ted Spence
2
使用Lambda表达式代替委托。 - Sergey Metlov
您好,您能否解释一下上面的代码在做什么?我有类似的问题,我想在不中断Form1执行的情况下显示不同的表单,比如Form2。您的解决方案是否也是这样做的? - Arti

13

在调用 form.Show() 后应该调用 Application.Run()。例如:

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

关于为什么的详细信息,请参考 这篇msdn帖子


6
在另一个线程中启动新的消息循环——如果主线程上已经运行了一个消息循环,启动新的可能过度了。 - Simon MᶜKenzie

1
根据我的经验,最好的方法是:


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

好主意 (ReportPre)Application.OpenForms ["ReportPre"] 谢谢您 - Mahmod Abu Jehad

-1
如果您的使用情况是在主GUI线程忙碌时(如加载条)显示GUI,则可以执行以下操作:
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();
}

我的LoadingGUI是一个简单的窗体,其中包含一个公共标签和一个样式设置为Marquee的进度条。


-3
在搜索了互联网并没有找到一个好的解决方案后,我想出了自己的方法:
public async Task FunctionWhereIStartForm( Func<Delegate,object>invoke )
{
  //this is on another thread

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

然后像这样调用:

await FunctionWhereIStartForm(Invoke);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接