2011-03-04 113 views
6

我一直在尋找並發現執行後臺工作和更新GUI的好方法是使用後臺工作人員。但是,做這個(愚蠢的)小任務(從1到10000),它不更新標籤內容,但打印到調試! (這僅僅是當然的另一項目標溶液...)使用BackgroundWorker更新GUI

下面的代碼:

public partial class MainWindow : Window 
{ 
    BackgroundWorker bw = new BackgroundWorker(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, RoutedEventArgs e) 
    { 

     bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
     bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); 
     bw.WorkerReportsProgress = true; 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
     bw.RunWorkerAsync(); 

    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     MessageBox.Show("DONE"); 

    } 

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     label1.Content = "going here: "+e.ProgressPercentage; 
     Debug.WriteLine(e.ProgressPercentage); 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     for (int i=0; i < 10000; i++) 
     { 
      bw.ReportProgress((i*100)/10000); 
     } 
    } 

} 

回答

12

ProgressChanged事件引發的UI線程上,而不是工作線程。在你的代碼中,工作線程幾乎什麼也不做(只是循環從0到10000,並且調用ReportProgress),大部分工作都是在UI線程上完成的。基本上,你發送了太多的進度通知。正因爲如此,UI線程幾乎總是很忙,沒有時間去渲染標籤的新內容。

當您更改控件的屬性時,不會立即執行在WPF中的呈現,而是在單獨的調度程序框架上完成,基於任務的優先級,調度程序沒有任何更緊急的事情要處理該框架。用於渲染的優先級值爲7(DispatcherPriority.Render);將ProgressChanged事件編組到優先級爲9的UI線程(DispatcherPriority.Normal),as specified on MSDN。因此,ProgressChanged通知的優先級總是高於渲染的優先級,並且由於它們不斷出現,調度程序從來沒有時間處理渲染任務。

如果你只是降低了通知的頻率,你的應用程序應該正常工作(目前你發送100個通知各百分比值,它是無用的):

void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     for (int i = 0; i < 10000; i++) 
     { 
      if (i % 100 == 0) 
       bw.ReportProgress(i/100); 
     } 
    } 
+1

+1。Very good。還加入'Thread.Sleep(10);'會有幫助。 – Aliostad 2011-03-04 11:41:33

+0

相信或者沒有,我在看到你的評論之前就想到了它!調用... – 2011-03-04 11:58:18

+2

當然,更容易綁定到一個屬性並從工作線程更新。 – Stimul8d 2011-03-04 12:10:50

0

試圖改變使用womething這樣的標籤:

string Text = "going here: "+ e.ProgressPercentage"; 
this.Invoke((MethodInvoker)delegate { 
    label1.Content = newText; 
}); 

請,請注意,我不確定它會起作用。我現在無法測試它。如果它不起作用,請告訴我,我將刪除答案。

謝謝。

如果你需要一個規範的方式做你想要什麼,看看這個職位的Hath答案:How to update the GUI from another thread in C#?

+0

嗯...你的答案可以工作,如果我正在使用Windows窗體...在WPF中我沒有控件的Invoke屬性...只有控件的調度器。我一直在試圖「搞砸」它,但沒有成功:( – 2011-03-04 10:16:23

+1

你可以在WPF中用Dispatcher.Invoke做同樣的事情,但是這無關緊要:ProgressChanged事件在UI線程中引發,因此Invoke是no – 2011-03-04 10:34:51

4
this.Dispatcher.BeginInvoke((Action) delegate(){ 
    label1.Content = "going here: "+e.ProgressPercentage; 
});