2011-12-14 14 views
1

TextBoxのLostFocusで非同期プロセスを呼び出すアプリケーションで問題が発生すると、非同期プロセスはメインフォームUIを更新する必要があります(またはダイアログを表示する必要があります)メインフォームのUIから)非同期に実行します。他のスレッドからUIを更新する

私たちはコールバックについて考えてきましたが、それは本当に非同期ですが、正しい順序で実行するためにはすべてが必要です。これは、データ入力の速度と正確さが重要なデータ入力システム用です。

例のコードは、何をしようとしているのかを示しています。BeginInvokeに切り替えると、処理の順序が正しくありません。

パブリック・クラスのForm1

Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.LostFocus 

    ' execute the server subroutine 
    Dim dlgt As New MethodInvoker(AddressOf Me.AsyncProcess) 

    TextBox1.Text = "1" 
    ' textbox should say 1 

    ' call the server subroutine asynchronously, so the main thread is free 
    Dim ar As IAsyncResult = dlgt.BeginInvoke(Nothing, Nothing) 

    While ar.IsCompleted = False 
     ' Application.DoEvents() 
    End While 
    ' textbox should now say 2 

    TextBox1.Text = "3" 
    ' textbox should now say 3 
End Sub 

Public Sub AsyncProcess() 
    UpdateTextBox() 
End Sub 

Public Sub UpdateTextBox() 
    If Me.InvokeRequired Then 
     Me.Invoke(New MethodInvoker(AddressOf UpdateTextBox), "2") 
    Else 
     TextBox1.Text = "2" 
    End If 
End Sub 

エンドクラス

は、誰もが、我々はLostFocusイベントを処理している間はまだ忙しいですメインフォームのスレッドで何かを呼び出すことができる方法を知っていますか?

ありがとうございます。

+0

このコードはDoEventsなしでデッドロックしますが、UIスレッドがIsCompletedで回転している間にInvoke()呼び出しを完了できません。 BeginInvoke()を代わりに使用すると、 "3"の後に常に "2"が表示されます。コードは、代替案を提案するにはあまりにも不思議です。 –

+0

なぜ2を表示するのに気をつけますか? 3はほぼ即座に消去します。 – GameAlchemist

+0

@ Vincent Piel - 彼はバックグラウンドスレッドからUIを更新できるようにしたいと考えていると思うので、特定の順序で何かが起こることを望んでいます。 –

答えて

2

BackgroundWorkerクラスを確認してください。このクラスを使用すると、バックグラウンドで長時間実行されるタスクを実行し、イベントを介してステータスをUIに戻すことができます。クラスは "ProgressChanged"イベントと "RunWorkerCompleted"イベントをサポートします。私はあなたがしようとしていることを達成するためにそれを使用する方法を示すためにいくつかのサンプルコードを含んでいます。

Imports System.ComponentModel 
Imports System.Threading 

Public Class Form1 

    Private Sub TextBox1_LostFocus(ByVal sender As Object, 
     ByVal e As System.EventArgs) Handles TextBox1.LostFocus 

     TextBox1.Text = "1" 

     ' Execute the long-running task in the background 
     BackgroundWorker1.RunWorkerAsync() 

    End Sub 

    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, 
     ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork 

     ' Do your heavy lifting in this method; report progress as needed by 
     ' calling ReportProgress, which will in turn call Progress_Changed 
     ' safely on the UI thread. (DO NOT update the UI directly from here.) 

     Dim worker As BackgroundWorker = CType(sender, BackgroundWorker) 

     ' Simulate processing for a second 
     Thread.Sleep(1000) 

     ' Report progress to the UI (the first arg is the percentage complete; 
     ' the secong arg can be a string or any object) 
     worker.ReportProgress(50, "2") 

     ' Simulate processing for another second 
     Thread.Sleep(1000) 

    End Sub 

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, 
     ByVal e As ProgressChangedEventArgs) 
     Handles BackgroundWorker1.ProgressChanged 

     ' Update the UI with progress from the background task 
     TextBox1.Text = CType(e.UserState, String) 

    End Sub 

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, 
     ByVal e As RunWorkerCompletedEventArgs) 
     Handles BackgroundWorker1.RunWorkerCompleted 

     ' Update the UI when the background task is finished 
     TextBox1.Text = "3" 

    End Sub 
End Class 
+0

これはBeginInvokeとまったく同じではありませんか?テキストを "3"に変更する前にRunWorkerCompletedイベントが発生するという保証はありません。したがって、最終値は "2"でなく "3"になります –

+0

いいえ、何をする必要がありますか? TextBox_LostFocusイベントの "before"処理( "1")を実行し、(現在のように)バックグラウンドワーカーを起動します。 ProgressChangedイベントの "during"アップデート( "2")を実行し、最後にRunWorkerCompletedイベントの "after"処理( "3")を実行します。 –

+0

上記の解決策はうまくいくはずです...しかし、既存のプロジェクトは、この種の解決策が実現できないほど大きくなっています。ポストされた例は、問題を説明するために非常に単純化されたバージョンの問題です。理想的にはキュー/イベントベースのソリューションを検討していますか? –

0

私はあなたの最善の策は、メソッドが同期的に終了するのを待つことを試みないことです。非同期呼び出しの後に実装するLostFocusハンドラーメソッドの残りの部分をコールバックに変更し、BeginInvokeに渡します。これにより、操作の順序を維持しながらUIスレッドが応答可能なままになります。

関連する問題