2013-07-29 53 views
0

我想作一個專門的類來更新我的應用程序的進度條(在這種情況下,WPF進度)。我做了這樣的事情:專用的C#類更新進度

public class ProgressBarUpdate : IDisposable 
{ 
    private readonly double _delta; 
    private int _current; 
    private int _total; 
    private readonly ProgressBar _pb; 

    public ProgressBarUpdate(ProgressBar pb, int total) 
    { 
     _pb = pb; 
     _total = total; 
     // the pb.Maximum is a double so it doesn`t get truncated 
     _delta = _pb.Maximum/total; 
     _current = 0; 
     _pb.Visibility = Visibility.Visible; 

    } 
    public void Dispose() 
    { 
     _pb.Visibility = Visibility.Collapsed; 
     _current = 0; 
    } 
    public void UpdateProgress() 
    { 
     _pb.Value =(int)_delta * (++_current); 
    } 

,我使用這樣的(在UI線程):

using (var pu = new ProgressBarUpdate(pb, totalCount) 
{ 
    for (x=0; x<totalCount; x++) 
    { 
    // operations here 
    pu.UpdateProgress() 
    } 
} 

但UI,很可能受阻,無法正確更新。顯示所有進度的最佳方式是什麼?

+5

也許它是你的UI是不僅「不更新」,但也阻止了? – TGlatzer

+0

你有調試到它,以確保'_pb.Value'得到改變,因爲你期望'UpdateProgress'調用內? – Tim

+1

*但是這在我的用戶界面中沒有正確更新。*這是什麼意思? –

回答

1

的Winforms/WPF程序是一個Eventing系統。有一個線程可以連續處理事件隊列中的事件。這是它的主要工作,理想情況是它應該做的唯一事情。任何類型的UI活動都會在事件隊列中生成事件 - 就像您將鼠標移動到窗口上或單擊某個窗口或其他某個窗口與窗口重疊,然後當它離開重疊位置時再次一樣。所有這些事件是由UI線程處理並且保持UI 更新所有的時間。

此外,的WinForms/WPF使得有必要訪問和/或更新控制和允許它僅在UI線程他們一個線程安全地進行性能。

如果阻斷這種UI線程或做它的一些其他CPU密集型計算,那麼你的UI 響應更新行爲將受到影響。最糟糕的用戶界面會凍結。

因此,您的正確答案是在另一個工作線程上執行計算循環,並僅使用Dispatcher編組對UI線程的調用來更新進度條UI。

但是,爲了回答你的問題,滿足您的宗教裁判所,這裏的東西是可能的 - 但它是不好的做法和你應該從未執行下列操作...

爲簡單,當您更新進度欄的Value屬性時,它會使進度欄UI失效 - 因此,UI必須更新。因此,可以說事件隊列中會生成一個事件,這將導致一些代碼運行,從而更新UI。但是,您正在UI線程中循環運行 - 因此,線程無法處理此事件,除非您的循環結束。因此你沒有看到任何UI更新。訣竅是讓UI線程處理該事件,然後在進度條的Value上進行下一次更新。您可以通過在事件隊列中強制調用較低優先級的項目來執行此操作,以便在進行下一次迭代之前處理普通和較高優先級的項目。

using (var pu = new ProgressBarUpdate(pb, totalCount)) 
{ 
    for (int x = 0; x < totalCount ; x++) 
    { 
    // operations here 
    pu.UpdateProgress(); 

    Dispatcher.Invoke(DispatcherPriority.Background, new Action(()=>{})); 
    } 
} 
+0

謝謝你的完整回覆,所以如果我把它正確的話,你添加的代碼是好的,但一般應該在另一個線程?將可能移動單獨的類中的線程操作,或者它需要在主UI代碼中? – powernemo

+0

順便說一句,如果我嘗試這我得到「一個對象引用是必需的非靜態字段」 – powernemo

+0

好吧,我發現了:我把它放在UpdateProgress方法,它的工作原理:Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new Action(()=> {}));' – powernemo

0

如果你做你的工作,並調用的UpdateProgress,在UI線程上,那麼它不會更新,直到你完成的工作和UI線程可以做其他工作(如刷新UI)。所以這將永遠不會工作。

如果你正在做後臺線程你的工作,那麼你需要使用調度封送的值設置到UI線程。

下面是http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher

if (!myCheckBox.Dispatcher.CheckAccess()) 
{ 
    myCheckBox.Dispatcher.Invoke(
    System.Windows.Threading.DispatcherPriority.Normal, 
    new Action(
     delegate() 
     { 
     myCheckBox.IsChecked = true; 
     } 
)); 
} 
else 
{ 
    myCheckBox.IsChecked = true; 
} 
+0

感謝您的回覆,工作在UI線程完成。我想創建一個通用類來始終更新主線程中使用的任何進度條。事實上,這個問題從來沒有重繪UI直到循環結束。 – powernemo

+1

永遠不要在主線程中長時間運行工作。不要。 – Akku

+0

好不長工作,我想有一個進度條表示正在工作。只需幾秒鐘即可重新加載某個文件。 – powernemo

0

一個例子試試這個:

public ProgressBarUpdate(ProgressBar pb, int total) 
{ 
    _pb = pb; 
    _total = total; 
    _delta = _pb.MaxValue/((double)total); /make sure you do not truncate delta 
    _current = 0; 
    _pb.Visibility = Visibility.Visible; 

} 
public void Dispose() 
{ 
    _pb.Visibility = Visibility.Collapsed; 
    _current = 0; 
} 
public void UpdateProgress() 
{ 
    _pb.Value = (int)(_delta * (++_current)); //update after the increment 
} 

我建議也在使用float代替double

+0

是固定的,在WPF版本中,MaxValue被稱爲Maximum並且是雙倍的,因此不需要重鑄。謝謝。 – powernemo

0

你一直說你要避免使用線程,我想是因爲你不想不必要的併發症,但它真的不是什麼大不了的事。多線程操作是一件非常簡單的事情。即使對於非常簡短的任務,這也是實現你想要的最直接的方法。使用TPL,它會是這個樣子:

using System.Threading.Tasks; 

... 

Task.Factory.StartNew(() => { 
    for (...) { 
     // operation... 
     progressBar.Dispatcher.BeginInvoke(() => progressBar.Value = ...); 
    } 
}); 
+0

我將在我的下一個項目中嘗試此操作。謝謝! – powernemo