2016-01-07 40 views
-1

知道當您需要從其他線程更新gui時使用Invoke方法。但是我怎樣才能實現這個沒有綁定控制代碼?從線程C#更新GUI,無需綁定到UI控件

這裏是我的測試類:

class test 
{ 
    public List<Thread> threads = new List<Thread>(); 
    public int nThreads = 0; 
    public int maxThreads = 5; 

    public void DoWork(object data) 
    { 
     string message = (string)data; 
     //MessageBox.Show(message);   


    } 

    public void CreateThread(object data) 
    { 
     if (nThreads >= maxThreads) 
      return; 
     Thread newThread = new Thread(DoWork); 
     threads.Add(newThread); 
     newThread.IsBackground = true; 
     newThread.Start(data); 
     nThreads++; 


    } 

    public void WindUpThreads() 
    { 
     //MessageBox.Show("count: " + nThreads.ToString()); 
     for(int i = 0; i < threads.Count; i++) 
     { 
      if (threads[i].IsAlive == false) 
      { 
       threads[i].Abort(); 
       threads.RemoveAt(i); 
       //MessageBox.Show("removing at " + i.ToString()); 
      } 

     } 

     nThreads = threads.Count; 
    } 

} 

問題是= I必須按順序使用什麼tecn​​ique更新GUI而不是硬編碼控制成class?我嘗試將代理傳遞給DoWork方法,但這不起作用(http://pastebin.com/VaSYFxPw)。謝謝!

我使用的WinForms,.NET 3.5

這裏的button_click處理程序:

private void button1_Click(object sender, EventArgs e) 
    { 
     button1.Enabled = false; 
     test thTest = new test(); 
     string[] strings; 
     try 
     { 

      strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 
     } 

     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
      return; 
     } 

     bool flag = true; 
     int counter = 0; 
     int dataCount = strings.Length; 

     while (flag == true) 
     { 
      if (counter >= dataCount) 
      { 
       flag = false; 
      } 

      while (thTest.nThreads < thTest.maxThreads) 
      { 
       if (flag == false) 
        break; 

       thTest.CreateThread(strings[counter]); 
       //Data d = new Data(); 
       //d.deleg = AddItem; 
       //d.mess = strings[counter]; 
       //thTest.CreateThread((object)d); 
       //MessageBox.Show(counter.ToString()); 
       counter++; 
      } 

      thTest.WindUpThreads(); 

      if (flag == false) 
      { 
       do 
       { 
        thTest.WindUpThreads(); 

       } while (thTest.nThreads != 0); 
      } 



     } 

     listBox1.Items.Add("Done"); 

    } 

的想法是,我'啓動線程我要處理的每個任務。在我檢查完成任務後,他們將被關閉,並啓動新的任務,直到不再有任務完成。

+0

每個List <>都有一個.Count屬性,你可以用它來代替nThreads。在for循環中,您從列表中刪除項目。這可能是有問題的。想象一下包含2個項目的列表。循環以i = 0開始,然後刪除第0項。現在列表只有1個項目,當它試圖訪問列表[1]時,它將失敗。 –

+0

你使用什麼樣的框架?的WinForms? WPF? ...? –

+0

@DennisKuypers,但我已經在使用.Count。或者你是什麼意思? –

回答

1

而不是讓DoWork負責更新與它執行操作的結果的UI,簡單地使其返回值:

//TODO change the type of the result as appropriate 
public string DoWork(string message) 
{ 
    string output = "output"; 
    //TODO do some work to come up with the result; 
    return output; 
} 

然後使用Task.Run創建一個Task,表示工作正在進行一個線程池線程。然後,您可以通過按鈕點擊處理程序來等待該任務。

private async void button1_Click(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 
    test thTest = new test(); 
    //I'd note that you really should pull out reading in this file from your UI code; 
    //it should be in a separate method, and it should also be reading 
    //the file asynchronously. 
    string[] strings; 
    try 
    { 
     strings = System.IO.File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
     return; 
    } 
    foreach (var line in strings) 
    { 
     var result = await thTest.DoWork(line); 
     listBox1.Items.Add(result); 
    } 
    listBox1.Items.Add("Done"); 
} 

如果你真的想成爲舊學校,你可以使用BackgroundWorker來代替。只需在DoWork處理程序中執行您的工作,在計算結果(通過參數)時設置結果,並使用RunWorkerCompleted事件處理程序中的結果更新UI。這可以讓您保持UI和非UI工作分離,儘管它不如新功能那樣強大,通用且可擴展。

+2

其Dot.Net 3.5有,因爲我知道沒有異步/等待功能 –

+0

@Servy,謝謝你的好例子,但我需要使用只有線程的東西。 –

+0

@MaximFleitling這是C#的一個特性,而不是框架。所以是的,你實際上可以在.NET 3.5中使用'async/await'。 – Servy

0

問題是=我必須使用什麼tecn​​ique來更新gui,但不是硬編碼控制到類中?我試圖通過代理DoWork方法,但這不起作用

這確實是可能的技術之一。它不起作用,因爲在UI線程中有一個阻塞循環 - button1_Click處理程序中的大部分代碼。不要緊,你產生了額外的工作線程 - 該代碼保持UI線程繁忙,因此Control.Invoke/Control.BeginInvoke不起作用,因爲它們由UI線程消息循環處理,在這種情況下,它沒有機會這樣做。最終的結果是經典的死鎖。

因此,您可以使用委託方法,但要使其工作,您需要將該代碼移動到單獨的線程中。像這樣的東西

private void button1_Click(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 
    var worker = new Thread(DoWork); 
    worker.IsBackground = true; 
    worker.Start(); 
} 

private void OnWorkComplete(Exception error) 
{ 
    if (error != null) 
     MessageBox.Show(error.Message); 
    button1.Enabled = true; 
} 

private void DoWork() 
{ 
    Exception error = null; 
    try { DoWorkCore(); } 
    catch (Exception ex) { error = ex; } 
    Invoke(new Action(OnWorkComplete), error); 
} 

private void DoWorkCore() 
{ 
    test thTest = new test(); 
    // NOTE: No try/catch for showing message boxes, this is running on a non UI thread 
    string[] strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 

    bool flag = true; 
    int counter = 0; 
    int dataCount = strings.Length; 
    // The rest of the code... 
    // Pass a delegate to the other threads. 
    // Make sure using Invoke when you need to access/update UI elements 
}