2011-01-12 70 views
0

我正在做一個WPF .NET 3.5應用程序,它執行幾個長任務,我想使UI線程分離線程來處理數據,然後在完成更新時UI中的標籤。我遇到的問題是我使用兩個參數的功能,我正在努力解決如何在一個線程中調用帶有多個參數的函數並更新UI。Threaded Function有多個參數並返回數據

我一直在玩弄使用Delegate Sub來調用函數(它位於一個單獨的類),我的代碼也試圖從調用線程的函數返回一個數據集來更新UI,但我不確定這是否是實現此目標的最佳實踐,或者我應該使用調度程序來調用UI來更新UI(反饋將不勝感激)。

我的代碼如下。

Private Delegate Sub WorkHandler(ByVal input1 As String, ByVal input2 As String) 
    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) 
     Dim test_helper As New test_global 
     Dim worker As New WorkHandler(AddressOf test_helper.getWeatherData) 
     worker.BeginInvoke("IDA00005.dat", "Adelaide", AddressOf weatherCallBack, Nothing) 

     ' The following is what I was using prior to attempting to work with threads, do I continue to update the UI here getting the called function to return a dataset, or do I have the called function do the UI updating? 
     'Dim ls As DataSet = test_helper.getWeatherData("IDA00005.dat", "Adelaide") 
     'Dim f_date As String = ls.Tables("weather").Rows(1).Item(3).ToString 
    End Sub 
    Public Sub weatherCallBack(ByVal ia As IAsyncResult) 
     CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, WorkHandler).EndInvoke(ia) 
    End Sub 

而我的作用,我試圖調用如下:

Class test_global 
    Public Sub getWeatherData(ByVal filename As String, ByVal location As String) 'As DataSet 
    ... 
    End Sub 
End Class 

我的問題是,如果我是有調用線程更新UI,我怎麼有被調用線程要返回一個數據集,或者如果被調用的線程要更新UI,我該如何去做到這一點?

更新:

按照提供的recomendations,我impletemented這引起了DoWork的和RunWorkerCompleted事件來獲取數據,並更新了UI,分別一個BackgroundWorker。我更新的代碼如下:

Class Weather_test 
    Implements INotifyPropertyChanged 
    Private WithEvents worker As System.ComponentModel.BackgroundWorker 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 
    Private Sub NotifyPropertyChanged(ByVal info As String) 
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info)) 
    End Sub 
    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) 
    Dim test_helper As New test_global 
    Dim worker = New System.ComponentModel.BackgroundWorker 
    worker.WorkerReportsProgress = True 
    worker.WorkerSupportsCancellation = True 
    Dim str() = New String() {"IDA00005.dat", "Adelaide"} 
    Try 
     worker.RunWorkerAsync(str) 
    Catch ex As Exception 
     MsgBox(ex.Message) 
    End Try 
    End Sub 
    Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork 
    Dim form_Helpder As New test_global 
    Dim ds As DataSet = form_Helpder.getWeatherData(e.Argument(0), e.Argument(1)) 
    e.Result = ds 
    End Sub 
    Private Sub worker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted 
    If e.Error IsNot Nothing Then 
     MsgBox(e.Error.Message) 
    Else 
     ... 
     NotifyPropertyChanged("lbl_minToday") 
     ... 
    End If 
    End Sub 
End Class 

然後我有一個單獨的類我的函數獲取和處理數據。

我能夠調試在Visual Studio 2010中的代碼和窗體顯示,但標籤沒有更新,當我在RunWorkerAsync行放置一個斷點時,該行被調用並且Window_Loaded子完成,但看起來沒有的DoWork或RunWorkerCompleted事件被調用(至少該函數不是)。

任何人都可以提供一些幫助我如何調試代碼,看看爲什麼這些函數不被調用?

另外,上述代碼是在答案中推薦的正確方法嗎?

提供的任何援助將不勝感激。

馬特

+0

我已經更新了這個問題,我已經按照推薦實現了BackgroundWorker,但是我遇到了兩個函數來處理DoWork和RunWorkerCompleted事件的問題。 – Lima 2011-01-13 11:47:35

回答

1

使用BackgroundWorker。實現您的長時間運行的方法,並將參數傳遞給DoWork事件處理函數的參數DoWorkEventArgs中的方法。不要在此方法中直接或間接更新UI(即不要更新視圖模型的屬性)。

使用進度報告在方法運行時更新用戶界面:在長時間運行的方法中調用ReportProgress,傳遞需要在UserState參數中顯示在用戶界面中的任何信息。在ProgressChanged事件處理程序中,從ProgressChangedEventArgs獲取狀態並更新UI(希望通過更新視圖模型的適當屬性並提高PropertyChanged)來更新UI。

由於UserState的類型爲object,因此您需要實施一個類來包含進度報告的用戶狀態。

請注意,您也可以在完成時使用長時間運行方法的結果更新UI。這是以類似於進度報告的方式完成的:執行一個類來包含結果,將DoWorkEventArgsResult屬性設置爲該類的一個實例,當RunWorkerCompleted事件發生時,結果將在WorkCompletedEventArgsResult屬性中可用被提出。

確保您通過檢查WorkCompletedEventArgsError屬性來處理長時間運行的方法引發的任何異常。

2

您應該使用BackgroundWorker component

您應該在DoWork處理程序中調用您的函數,並將e.Result設置爲返回的DataSet。
然後,您可以更新RunWorkerCompleted處理程序中的UI。

1

我對BackgroundWorker沒有太多經驗(我只用過一次),但它絕對是解決您的問題的方法。然而,我總是使用的方法是啓動一個新的線程(而不是通過委託的線程池線程),該線程獲取一個鎖並更新所有的屬性。假如你的類實現了INotifyPropertyChanged,那麼你可以使用數據綁定來讓GUI在任何時候屬性改變時自動更新。採用這種方法,我獲得了很好的結果。

只要將調度程序傳遞給你的線程,我相信你也可以做到這一點。不過,我會輕率地行事,因爲我相信我遇到過這種情況,我認爲我使用的調度程序不再與主線程相關聯。我有一個庫需要調用一個觸及GUI元素的方法(即使對話框可能不顯示),我使用Dispatcher.Invoke解決了這個問題。我能夠保證我使用與主線程相關聯的Dispatcher,因爲我的應用程序使用MEF導出它。

如果您想了解更多關於我發佈的任何內容的詳細信息,請發表評論,我會盡我所能對這些主題進行修飾。

+0

這將無法正常工作;您將從bckground線程更新UI對象。另外,你應該使用ThreadPool。 – SLaks 2011-01-12 17:17:09

+0

當您使用「BackgroundWorker」的進度報告功能時,您可以更新UI對象。很少有必要在WPF應用程序中顯式使用`System.Threading`。 – 2011-01-12 18:06:50