2013-12-17 110 views
2

我在C#/ WPF(.Net框架4.0)中編寫應用程序。我遇到的一個問題是如何在應用程序非常繁忙的情況下更新控件。我認爲我只是在嘗試在網上找到的東西,讓它發揮作用(在極少數情況下)。 該應用程序有四個類,一個主窗口類,一個Worker1類,一個Worker2類和一個靜態的ExtensionMethods類。 Worker2從Worker1中調用並執行大部分工作。運行時CPU爲100%(核心爲100%,系統有四個核心)。 (所有的都在同一個命名空間。)從另一個類更新進度條

public static class ExtensionMethods 
{ 
    private static Action EmptyDelegate = delegate() { }; 

    public static void Refresh(this UIElement uiElement) 
    { 

     uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate); 
    } 
} 

主窗口調用Worker1頂級功能,經過一些設置,然後開始漫長的過程,它是這樣的:

MainWindow mainWindow = App.Current.MainWindow as MainWindow; 
mainWindow.dispatcher_UpdateProgressBarMaximum(maxValue); 
int count=0; 
Worker2 worker2 = new Worker2(); 
while (bytesRead =(readData() !=0) { 
    count++; 
    worker2.doWork(); 
    mainWindow.dispatcher_UpdateProgressBar(count); 

} 

在主窗口類爲:

dispatcher_UpdateProgressBar(int Value) 
{ 
Dispatcher.BeginInvoke(new Action(() => myProgressBar.Value = value), null); 
     myProgressBar.Refresh(); 
} 

(我也嘗試過:myProgressBar.Dispatcher.Begin ...等

但都不重要。對於有2或3次讀取的小文件,我可以看到進度條得到更新。但是對於大文件,直到最後纔會更新。 這也發生在一些標籤控件中,我在其中顯示一些狀態信息。整個工作完成後,我可以選擇顯示所做完成的消息框。對於標籤,當我顯示消息框時,標籤被更新。該應用程序可能會依次處理多個文件。

我想過使用後臺線程,但不知道如何去做。也就是說,worker2.doWork()部分是非常密集的,但如果它在它自己的線程上,mainWindow.dispatcher_Update ...將在線程開始後立即被調用,並且沒有什麼可以阻止下一次讀取發生,對吧?

也許我編寫它的方式是不可能的。如果我使用Backgroundworker,這個類是否會在主窗口或Worker1中實例化?即使是一個或兩個指針也會很棒。

+1

我編輯了你的標題。請參閱:「[應該在其標題中包含」標籤「](http://meta.stackexchange.com/questions/19190/)」,其中的共識是「不,他們不應該」。 –

回答

2

使用BackgroundWorker作爲您的後臺工作,並通過ReportProgress調用更新GUI,並通過服務ProgressChanged event(包含「進度」)處理它 - 只需從那裏更新進度欄即可。

如果您需要在第一個後臺任務完成時啓動另一個後臺任務,您可以從RunWorkerCompleted event開始。

+0

是[WPF](http://stackoverflow.com/tags/wpf/info)中定義的[BackgroundWorker](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) ? – jp2code

+0

它在'System.ComponentModel'中,但我建議不要在WPF中使用它。現在有更好的方法來處理更新進度。 – Erik

3

在.NET 4.5,他們增加了IProgress<T>接口和Progress<T>類,可以讓你從另一個線程報告進度,如果Progress創建對象有它會自動在UI線程上運行回調。

因爲你在.NET 4.0上並不完全不幸,所以Microsoft通過NuGet Package Microsoft.BCL將這些類作爲Out of band update發佈到框架中,這些類將這兩個類支持移植到.NET 4.0中。

一旦你有Progress<T>它很容易使用。

void StartWorker() 
{ 
    var progress = new Progress<int>(UpdateProgressBar); 

    worker1.Start(progress) 
} 


void UpdateProgressBar(int Value) 
{ 
    //This code is invoked on the UI thread 
    myProgressBar.Value = value; 
} 

//Elsewhere in Worker1 
void Start(IProgress<int> progress) 
{ 

    int count=0; 
    Worker2 worker2 = new Worker2(); 
    while (bytesRead =(readData() !=0) 
    { 
     count++; 
     worker2.doWork(); 
     progress.Report(count); 
    } 
} 

然而這一切都這樣說,你應該嘗試重新考慮你的數據模型。「擁抱WPF」並開始使用像MVVM這樣的概念來設置綁定,然後更新數據對象,然後更新您的UI(並且您可以將所有UI編組代碼放入您的ViewModel中的OnPropertyChanged事件中)。

+0

我很希望避免這樣的事情。這個UI更新是事後纔想到的。我會這樣做,無論是通過MVVM還是後臺工作者修補(下圖)。大多數用戶界面不會因應用程序的操作而更新,即在操作過程中。謝謝!!!順便說一下(我知道我不應該爲此使用註釋)。我注意到現在的一切都在一個線程上? – Ron

+0

@Ron如果您使用未列爲答案的解決方案,請發佈自己的答案,解釋您做了什麼並將其標​​記爲已接受,以便每隔幾個月不會自動提升問題作爲沒有被接受答案的問題。至於爲什麼它沒有更新,那是因爲一切到現在都在一個線程上。您可以使用'任務'啓動後臺工作,請注意,如果工作人員需要超過幾秒的時間運行,您應該指定他們['LongRunning'](http://msdn.microsoft.com/zh-cn/我們/庫/ system.threading.tasks.taskcreationoptions%28v = vs.110%29.aspx) –

+0

我第二MVVM的方法,這絕對是要走的路。綁定可以處理所有的線程編組,您可以很容易地將ProgressBar綁定到模型,並在更新屬性時自動更新視圖。 – Erik