如何在线程中打开表单并强制它保持打开状态

时间:2008-10-03 15:07:27

标签: .net winforms multithreading

我仍然遇到如何在我讨论的here单独的UI线程中找出如何创建winforms的问题。

在试图解决这个问题时,我编写了以下简单的测试程序。我只是希望它在名为“UI线程”的单独线程上打开一个表单,并且只要表单打开就保持线程运行,同时允许用户与表单交互(旋转是作弊)。我理解为什么以下失败并且线程立即关闭但我不确定我应该做些什么来解决它。

using System;
using System.Windows.Forms;
using System.Threading;

namespace UIThreadMarshalling {
    static class Program {
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var tt = new ThreadTest();
            ThreadStart ts = new ThreadStart(tt.StartUiThread);
            Thread t = new Thread(ts);
            t.Name = "UI Thread";
            t.Start();
            Thread.Sleep(new TimeSpan(0, 0, 10));
        }

    }

    public class ThreadTest {
        Form _form;
        public ThreadTest() {
        }

        public void StartUiThread() {
            _form = new Form1();
            _form.Show();
        }
    }
}

6 个答案:

答案 0 :(得分:13)

在一个新线程上,调用Application.Run传递表单对象,这将使该线程在窗口打开时运行自己的消息循环。

然后你可以在该线程上调用.Join来使你的主线程等到UI线程终止,或使用类似的技巧等待该线程完成。

示例:

public void StartUiThread()
{
    using (Form1 _form = new Form1())
    {
        Application.Run(_form);
    }
}

答案 1 :(得分:3)

private void button1_Click(object sender, EventArgs e)
{
    var t = new Thread(RunNewForm);
    t.Start();
}
public static void RunNewForm()
{
     Application.Run(new Form2());
}

答案 2 :(得分:3)

我认为你的问题是这个想法:“在一个名为'UI thread'的单独线程上打开一个表单”

Windows的工作方式是这样的(请注意Vista可能会改变其中的一些现实):

有一个重要线程称为“主线程”或“ UI线程”。这个线程是处理windows messages的线程,就像“嘿,鼠标点击了这个像素。”

这些消息进入队列,主线程处理它们当它没有忙于做其他事情时

因此,如果在主线程上调用foo()函数,如果需要很长时间,则在此期间不会处理任何Windows消息,因此不会发生用户交互。

主线程也在屏幕上绘制UI,因此长时间运行的foo()也会阻止您的应用程序进行绘制。

除了这个神圣和特殊主线程之外的所有其他线程都是笨拙的工作者线程。这些工作线程可以做某事,但它们永远不能直接与用户界面交互。

这个现实导致了两个问题:

  1. 取消主要线程:由于您不希望长时间运行的foo()停止所有用户交互,因此您需要将该工作交付给工作线程。

  2. 回到主线:当长时间运行的foo()完成时,您可能希望通过在UI中执行某些操作来通知用户,但是您不能在工作线程中执行此操作,因此您需要“回到”主线程。

  3. 所以我相信你在上面的程序中的问题非常普遍:你的目标是不正确的,因为它不应该在任何线程中调用_form.Show(),而是神圣的主线程。

答案 3 :(得分:2)

您无法在任何线程中打开GUI表单,因为它将缺少消息泵。 您必须通过在线程方法中调用Application.Run()在该线程中显式启动消息泵。另一种选择是在循环中调用DoEvents(),如果你需要做其他事情,因为在Application.Run()之后该线程将等待用户在该执行点关闭表单。

答案 4 :(得分:1)

我认为只是调用ShowDialog而不是Show会有所帮助。问题似乎是线程在调用Show之后完成,之后Form收集了垃圾。 ShowDialog将暂停该线程但仍然在其上运行form-events,因此该线程将一直运行直到表单关闭。

通常我会以相反的方式做到这一点。当您想要启动长时间运行的后台任务时,在起始线程上运行表单并启动后台线程。

我也读了你的另一个问题,但无法弄清楚你想要做什么。 MVP架构不要求您在不同的线程上运行业务逻辑。多线程很难做到,所以如果我真的需要它,我只会使用多个线程。

答案 5 :(得分:0)

而不是在窗体上执行show(),而不是在函数StartUiThread()中的线程执行结束时关闭,你可以锁定线程,直到窗体在方法中停止为止只是锁定另一个线程。例如:

public void StartUiThread() {
        _form = new Form1();
        _form.ShowDialog(); //Change Show() to ShowDialog() to wait in thread
    }

这将导致新线程等到对话框关闭。我不知道这是否能解决你的问题,但它解决了我的问题。