2015-10-17 33 views
1

問題很簡單:我需要在處理耗時計算時更新WPF應用程序的進度。在我的嘗試中,我做了一些Google搜索,最後基於這個解決方案的第一個代碼片段:How to update UI from another thread running in another class。這裏是我的代碼:C#WPF應用程序執行任務的更新進度

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

using System.Threading; 

namespace ThreadTest 
{ 
    public class WorkerClass 
    { 
     public int currentIteration; 
     public int maxIterations = 100; 

     public event EventHandler ProgressUpdate; 

     public void Process() 
     { 
      this.currentIteration = 0; 
      while (currentIteration < maxIterations) 
      { 
       if (ProgressUpdate != null) 
        ProgressUpdate(this, new EventArgs()); 

       currentIteration++; 
       Thread.Sleep(100); // some time-consuming activity takes place here 
      } 
     } 
    } 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void btnStart_Click(object sender, RoutedEventArgs e) 
     { 
      WorkerClass wItem = new WorkerClass(); 

      wItem.ProgressUpdate += (s, eArg) => { 
       Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); }); 
      }; 
      Thread thr = new Thread(new ThreadStart(wItem.Process)); 
      thr.Start(); 

      // MessageBox.Show("Job started..."); 

      while (thr.IsAlive == true) 
      { 
       Thread.Sleep(50); 
      } 

      MessageBox.Show("Job is done!"); 
     } 
    } 
} 

的問題是,如果我用Dispatcher.Invoke,比工作線程(THR)進入WaitSleepJoin狀態的第一個週期傳球后並沒有恢復,因此整個應用程序凍結。我已經使用了幾個建議來代替使用Dispatcher.BeginInvoke,但在這種情況下,進度不會更新,直到過程完成工作。我想這個問題與切換線程有關,但不能得到確切的點。

+0

可能重複[如何從另一個線程更新UI](http://stackoverflow.com/questions/9602567/)。 –

+0

@DourHighArch:這個問題清楚地表明OP已經理解如何使用Dispatcher.Invoke()。他有一個不同的(雖然常見)問題。這個問題不是你建議的副本的重複。它可能是其他問題的重複,但我確實看過並找不到一個用於WPF的文件(Winforms有一個數字),並且以易於理解的方式直接解決了這個問題。 –

+0

@DourHighArch,是的。我看到了該線程,並根據解決方案回覆的第一個片段編寫了我的代碼。正如Peter Duniho所說,這個問題更多的是爲什麼它可能不起作用。我在這個問題上花了兩天沒有結果,需要某種地方挖掘。 – Vasiliy

回答

1

這是一個典型的「調用死鎖」場景。堆棧溢出有一些解決WinForms的問題的現有問題,但我只能在WPF上下文(Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish)中找到一個相關的問題,但那不是完全相同的問題,或者至少沒有答案(然而)直接解決你的問題,並且問題和答案本身還不夠完善,我覺得你的問題需要一個新的答案。所以&hellip;


基本的問題是,你已經阻止了你的UI線程等待進程完成。你應該從來沒有做到這一點。你應該從來沒有出於任何原因阻止UI線程。很明顯,如果您在UI線程中運行的代碼中等待任何原因,那麼UI本身無法響應用戶輸入或執行任何類型的屏幕刷新。

有很多可能的方式來解決這個問題,但處理這一當前成語是使用asyncawait,隨着Task運行您的程序。例如:

private async void btnStart_Click(object sender, RoutedEventArgs e) 
{ 
    WorkerClass wItem = new WorkerClass(); 

    wItem.ProgressUpdate += (s, eArg) => { 
     Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); }); 
    }; 

    Task task = Task.Run(wItem.Process); 

    // MessageBox.Show("Job started..."); 

    await task; 

    MessageBox.Show("Job is done!"); 
} 

注意的是,雖然上述聲明async方法void,這是例外。也就是說,通常應該聲明async方法返回TaskTask<T>。例外情況是這種情況,其中方法是一個事件處理程序,因此需要匹配現有的簽名,其中必須聲明方法以返回void

1

我跑你的代碼,因爲它是和註釋這樣的:

while (thr.IsAlive == true) 
      { 
       Thread.Sleep(50); 
      } 

一切工作正常。用戶評論/

/編輯得到通知處理完成的,做這些改變:

一個。 public event EventHandler ProgressCompleted;在你的WorkerClass中。

b。

if (ProgressCompleted != null) 
     ProgressCompleted(this, new EventArgs()); 

在Process()方法中完成之後。

c。在創建線程之前在您的BtnStart_Click中。

wItem.ProgressCompleted += (s1, eArgs1) => 
      { 
       MessageBox.Show("Job is done!"); 
      }; 
+0

AnjumSKhan,謝謝你的回覆。確實有效,但「工作完成了!」消息出現在按鈕被點擊後,而不是線程thr完成後。我需要它在thread thr完成它的工作後出現,因爲會有一些基於wItem.Process()結果的計算。 – Vasiliy

+0

AnjumSKhan,更新後的版本適合我。但是在Worker類中需要多一個事件,並且如果我需要在「作業已完成」消息的同時實現更復雜的邏輯,則會使代碼更加龐大。所以在這種特殊情況下,我選擇了基於TPL的解決方案。無論如何,你的想法對於特定的用例很有意義並且適用,我已經將它放在了財政部的想法中。謝謝! – Vasiliy

+0

@Vasiliy計數任何WPF控件中的事件數。 – AnjumSKhan

相關問題