2008-10-03 103 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 
    } 

這將導致新線程等待,直到對話框關閉。我不知道這是否會解決你的問題,但它解決了我的問題。