2011-01-12 9 views
0

私はWPF .NET 3.5アプリケーションで作業していますが、データを処理するUIスレッドとは別のスレッドを作成したい場合があります。 UIのラベル私が抱えている問題は、私が持っている関数が2つのパラメータを使い、スレッド内の複数のパラメータを持つ関数を呼び出してUIを更新する方法を考えるのに苦労していることです。スレッド関数には複数のパラメータがあり、データを返します

Delegate Subを使用して関数を呼び出す(これは別のクラスにあります)、私のコードは呼び出し元のスレッドの関数からデータセットを返して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 

そして、私は呼び出そうとしています私の機能は以下のとおりである:私はUIを更新するために、呼び出し元のスレッドを持っていた場合

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

私の問題があり、私はと呼ばれる糸を持っていますかデータセットを返すか、呼び出されたスレッドがUIを更新するかどうかを確認するにはどうすればよいですか?

更新:提供アーティスト同士のコラボレーションに続き

、私はDoWorkを上げるのBackgroundWorkerをimpletementedそれぞれ、データを取得し、UIを更新するために、イベントをRunWorkerCompletedています。私の更新されたコードは次のようになります:

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イベントを処理する2つの関数を取得する際に問題が発生しています。 – Lima

答えて

1

BackgroundWorkerを使用してください。長時間実行するメソッドを実装し、イベントハンドラのDoWorkDoWorkEventArgsパラメータのメソッドに引数を渡します。このメソッドでは、UIを直接的または間接的に更新しないでください(ビューモデルのプロパティを更新しないでください)。

メソッドの実行中に進捗報告を使用してUIを更新すると、長期実行メソッドでと呼び出し、UIに表示する必要がある情報をUserStateパラメータに渡します。 ProgressChangedイベントハンドラでは、ProgressChangedEventArgsから状態を取得し、UIを更新します(希望により、ビューモデルの適切なプロパティを更新してPropertyChangedを呼び出すことで)。

UserStateのタイプがobjectなので、進捗報告のためのユーザー状態を含むクラスを実装する必要があります。

長時間実行されているメソッドの結果が完了したら、UIを更新することもできます。結果を格納するクラスを実装し、DoWorkEventArgsのプロパティをプロパティをこのクラスのインスタンスに設定し、RunWorkerCompletedイベントが発生したときにWorkCompletedEventArgsResultプロパティで結果を利用できるようになります上昇する。

WorkCompletedEventArgsErrorプロパティをチェックすることで、長時間実行されるメソッドが発生する例外を処理することを確認してください。

+0

私の更新されたコードを見てください、あなたがINotifyPropertyChangedと同じことを話したPropertyChangedですか? – Lima

+1

'INotifyPropertyChanged'はインタフェースです。 'PropertyChanged'はイベントです。 –

+0

多くの情報を提供し、PropertyChangedイベントを指摘したので、これを答えとしてマークしました。お手伝いいただきありがとうございます。 – Lima

2

あなたはBackgroundWorker componentを使用する必要があります。

DoWorkハンドラで関数を呼び出し、返されたデータセットにe.Resultを設定する必要があります。
RunWorkerCompletedハンドラのUIを更新できます。

1

私はBackgroundWorker(これは一度だけ使用しました)の経験はあまりありませんが、これは間違いなくあなたの問題の解決策です。しかし、私が常に使用するアプローチは、ロックを取得してすべてのプロパティを更新する新しいスレッド(デリゲート経由でThreadPoolスレッドではない)を開始することです。クラスにINotifyPropertyChangedが実装されている場合は、データバインドを使用して、プロパティが変更されたときにGUIが自動的に更新されるようにすることができます。私はこのアプローチで非常に良い結果を得ています。

スレッドにDispatcherを渡す限り、私はあなたもそうすることができると信じています。しかし、私が使用していると思っているDispatcherがもはやメインスレッドに関連付けられていないケースが発生したと思うので、私は軽く踏み込んでいます。私はGUI要素に触れるメソッドを呼び出す必要があるライブラリを持っています(ダイアログは表示されないかもしれませんが)、Dispatcher.Invokeを使ってこの問題を解決しました。私のアプリケーションがMEFを使ってエクスポートするので、メインスレッドに関連付けられたDispatcherを使用していることを保証することができました。

投稿したことの詳細をご希望の場合は、コメントしてください。

+0

それはうまくいかないでしょう。 bckgroundスレッドからUIオブジェクトを更新します。また、ThreadPoolを使用する必要があります。 – SLaks

+0

進捗報告機能を使用して 'BackgroundWorker'を使用しているときにUIオブジェクトを更新できます。 WPFアプリケーションで明示的に 'System.Threading'を使う必要はほとんどありません。 –

+0

@SLaksそれは、より効率的な方法があるように、うまく動作しないということを意味しますか、あるいはそのアプローチが完全に欠陥があり、まったく動作しないことを意味しますか?後者の場合は、バックグラウンドスレッドの更新プロパティを常に持っていて、主GUIからこれらのプロパティにデータバインドするだけで混乱します。スレッドの問題が発生していない(メインスレッド以外からの更新)。なぜそれがThreadPoolでなければならないのですか?それが長時間実行されているプロセスの場合は、バックグラウンドスレッドが適切な方法です。 – Dave

関連する問題