UI の応答性を維持しながら、データをバックグラウンドでロードできるように Windows フォームを変更しています。データの取得とバインドの両方にかなりの時間がかかります。両方をバックグラウンドで実行するのが理想的ですが、バックグラウンド (メイン スレッドの外側など) で実行する必要がある UI の更新の種類についてはあいまいな点があります。バックグラウンドでのデータ取得とデータ バインディングを示す確かな例は、非常に役立ちます。
3 に答える
取得はバックグラウンドスレッドにプッシュすることができ、プッシュする必要がありますが、すべてを適切に配置するためのパターンがいくつかあります。
基本的に、バックグラウンドスレッドを開始してデータを取得します。データを取得したら、UIスレッドにマージして、実際のUI更新を実行する必要があります(スレッド間のUI更新は悪い悪い悪いです)。
探索するためのバックグラウンドスレッドの3つの基本的な方法があります
- 最も簡単/最も制限された(そして風変わりなIMO)はBackgroundWorkerコンポーネントです
- デリゲートとそのBeginInvoke()/ EndInvoke()メソッドを使用すると、使いやすさと柔軟性のバランスがうまく取れます(そして、ThreadPoolスレッドを使用します)。
- 生のThreadオブジェクトを使用すると、ほとんどの制御が可能になりますが、ThreadPoolThreadsよりもセットアップに時間がかかります。
個人的には、Delegatesオプションに傾倒しています。パターンを理解すれば、操作は非常に簡単です。BackgroundWorkerは前もって素晴らしいように見えますが、いくつかの落とし穴と配管が欠落しているため、予想よりも作業が面倒です。Delegateアプローチの短いサンプルを作成しましょう。まもなく更新します...
編集
ここにいくつかのコードがあります、それはVBにありますが、あなたがC#の人なら、書き写すのに十分簡単なはずです。バックグラウンドスレッドの動作方法については、さらにいくつかのオプションがあるため、ここに2つのサンプルがあります。非ブロッキングが私の好みですが、既存のコードに適合させる場合は、ブロッキングの方が簡単かもしれません。
非ブロッキング、バックグラウンドスレッドが完了すると、コールバックメソッド(GetData_Complete)がUIスレッドで呼び出されます
Sub Main()
Console.WriteLine("On the main thread")
Dim dataDelegate As New GetDataCaller(AddressOf GetData)
Dim iar As IAsyncResult
' Non-blocking approach using a callback method
iar = dataDelegate.BeginInvoke(AddressOf GetData_Complete, Nothing)
End Sub
Private Delegate Sub GetData_CompleteCaller(ByVal iar As IAsyncResult)
Private Sub GetData_Complete(ByVal iar As IAsyncResult)
If InvokeRequired Then
Dim invokeDelegate As New GetData_CompleteCaller(AddressOf GetData_Complete)
Invoke(invokeDelegate, New Object() {iar})
Exit Sub
End If
' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)
Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
Dim result As String = dataDelegate.EndInvoke(iar)
Console.WriteLine("On the main thread again, background result is: " + result)
End Sub
Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
Console.WriteLine("On the background thread!")
For index As Integer = 0 To 2
Console.WriteLine("Background thread is working")
Next
Return "Yay, background thread got the data!"
End Function
サブメイン()のブロック
Console.WriteLine("On the main thread")
Dim dataDelegate As New GetDataCaller(AddressOf GetData)
Dim iar As IAsyncResult
' blocking approach; WaitOne() will block this thread from proceeding until the background thread is finished
iar = dataDelegate.BeginInvoke(Nothing, Nothing)
iar.AsyncWaitHandle.WaitOne()
Dim result As String = dataDelegate.EndInvoke(iar)
Console.WriteLine("On the main thread again, background result is: " + result)
End Sub
Private Sub GetData_Complete(ByVal iar As IAsyncResult)
' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)
Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
Dim result As String = dataDelegate.EndInvoke(iar)
Console.WriteLine("On the main thread again, background result is: " + result)
End Sub
Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
Console.WriteLine("On the background thread!")
For index As Integer = 0 To 2
Console.WriteLine("Background thread is working")
Next
Return "Yay, background thread got the data!"
End Function
サーバーからデータを取得したら、UI スレッドを呼び出して、UI がバインドされている UI コントロールまたはデータセットを更新すると、バックグラウンド スレッドから UI を更新しないでください。
この場合、BackgroundWorker を使用すると、イベントを接続するだけで役立ちます。
HTH
フィル
読み込み (「データ ソースからの取得」など) は、デリゲート、バックグラウンド ワーカー、またはその他のプロトコルを使用するかどうかにかかわらず、些細なことかもしれません。しかし、少なくともほとんどのデータ バインドされたコントロールでは、データを非同期に取得することはできますが、準備ができたら、それをバックグラウンドで大きなグリッドにフィードする方法はありますか? それはあなたの質問ですか?もしそうなら、私はあなたが次のいずれかを行うことができると思います:
- ビュー コントロールを作成 (またはサブクラス化) し、非同期ロード用のインターフェイスを提供します。
- レコードのフェッチ/フォーマット中に UI がブロックされないように、一度に N レコードのみを表示するページ ビューを実装します。