如何在线程中打开表单并强制其保持打开状态
在弄清楚如何在这里讨论的单独的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(); } } }
解决方案
在新线程上,调用传递窗体对象的Application.Run,这将使线程在窗口打开时运行其自己的消息循环。
然后,我们可以在该线程上调用.Join以使主线程等待直到UI线程终止,或者使用类似的技巧来等待该线程完成。
例子:
public void StartUiThread() { using (Form1 _form = new Form1()) { Application.Run(_form); } }
我们无法在任何线程中打开GUI表单,因为它将丢失消息泵。
我们必须通过在线程方法中调用Application.Run()来显式启动该线程中的消息泵。如果我们需要执行其他操作,则另一个选择是在循环中调用DoEvents(),因为在Application.Run()之后,该线程将在该执行点等待用户关闭表单。
private void button1_Click(object sender, EventArgs e) { var t = new Thread(RunNewForm); t.Start(); } public static void RunNewForm() { Application.Run(new Form2()); }
我认为问题与这种想法有关:"在名为" UI线程"的单独线程上打开表单"
Windows的工作方式是这样的(请注意,Vista可能会改变其中的一些现实):
有一个重要的线程称为"主线程"或者" UI线程"。该线程是处理Windows消息的线程,例如"嘿,鼠标点击了此像素"。
这些消息进入队列,并且主线程在不忙于执行其他操作时会对其进行处理。
因此,如果我们在主线程上进行函数调用foo(),则花费的时间很长,在此期间不会处理任何Windows消息,因此不会发生用户交互。
主线程还会在屏幕上绘制UI,因此长时间运行的foo()也会阻止应用程序绘制。
除此神圣主线程和特殊主线程外,所有其他线程都是grunt worker线程。这些工作线程可以执行操作,但是它们永远无法直接与用户界面进行交互。
这种现实导致两个问题:
- 摆脱主要威胁:由于我们不希望长时间运行的foo()停止所有用户交互,因此需要将工作交付给工作线程。
- 返回主要内容:长时间运行的foo()完成后,我们可能希望通过在UI中执行某些操作来通知用户,但是我们无法在工作线程中执行此操作,因此我们需要"返回"到主线程。
因此,我相信我们在上述程序中的问题很笼统:目标很不正确,因为除了神圣的主线程外,其他任何线程都不能调用_form.Show()。
我认为仅调用ShowDialog而不是Show会有所帮助。问题似乎是线程在调用Show之后立即完成,在此之后Form的垃圾被回收了。 ShowDialog将暂停线程,但仍在该线程上运行表单事件,因此该线程将一直运行,直到关闭表单为止。
通常我会反过来做。若要启动长时间运行的后台任务,请在启动线程上运行表单,并启动后台线程。
我也阅读了其他问题,但无法弄清楚我们要做什么。 MVP体系结构不需要我们在不同的线程上运行业务逻辑。多线程很难做到正确,因此,如果我确实需要多线程,我只会使用多线程。