2008-10-03 107 views
8

我在解决如何在单独的UI线程中创建winform时遇到问题,我讨论过here如何在线程中打开窗体并强制其保持打开状态

在试图找出这个我写了下面的简单测试程序。我只是想让它在一个名为“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(); 
     } 
    } 
} 
+1

是的,这只是微软未能正确地思考除了单个对话和单线程之外的任何事情。正如下面的解决方案所述,您可以启动一个单独的消息泵,但没有方便的方式从GUI线程启动它,因此无法将第二个窗体作为非模态对话框执行。 – 2012-07-18 23:24:17

+0

公平地说,问题出自4年前,关于一个12岁的技术:) – 2012-11-20 21:13:33

+1

是的,我知道,但抛开这个事实,甚至12年前人们想要这个,甚至没有看我会打赌一大笔钱MS的任何修订/新API都没有改变这种情况。 – 2012-11-22 04:22:26

回答

13

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

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

例子:

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

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

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

我觉得你的问题是这样的想法:“打开一个窗体上名为“UI线程的一个单独的线程”

窗户的工作方式是这样的(PLZ注意Vista可能会改变一些,这些现实):

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

这些消息进入队列,并且主线程在它不忙的时候处理它们

所以,如果你在主线程上调用函数foo(),如果需要很长时间,那么在那段时间内没有处理windows消息,所以不会发生用户交互。

主线程还会在屏幕上绘制UI,所以长时间运行的foo()也会停止绘制应用程序。

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

这一现实导致两个问题:

  1. 下车主线程:既然你不想长时间运行FOO()停止所有用户交互,需要运输的工作开了个工作者线程。回到主线程:当长时间运行的foo()完成时,您可能想通过在UI中执行某些操作来通知用户,但是您不能在工作线程中执行此操作,因此您需要“回到“主线程。

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

1

我认为只是调用ShowDialog而不是Show会有所帮助。问题似乎是线程在调用Show之后完成,之后Form获取垃圾回收。 ShowDialog将暂停线程,但仍然在其上运行窗体事件,以便线程将继续运行,直到窗体关闭。

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

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

0

而不是调用窗体上的show(),它将在窗体上执行,然后在函数StartUiThread()内的线程执行结束时关闭,您可以锁定线程,直到窗体在方法内停止为你只是简单地锁定另一个线程。例如:

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

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

相关问题