2013-07-13 9 views
17

async/awaitパターンを使用している間にイベントからUIを更新する方法を理解しようとしています。 以下はWinFormアプリケーションで使用しているテストコードです。私はこれが正しいことだとは確信していません。 pwe_StatusUpdateメソッドがUIを更新するために必要なことは何ですか?そこにクロススレッド操作エラーがスローされます。asyc awaitを使用したイベントからUIを更新する

読んでいただきありがとうございます。

// calling code 
    ProcessWithEvents pwe = new ProcessWithEvents(); 
    pwe.StatusUpdate += pwe_StatusUpdate; 
    await pwe.Run(); 



void pwe_StatusUpdate(string updateMsg) 
    { 
     // Error Here: Cross-thread operation not valid: Control '_listBox_Output' accessed from a thread other than the thread it was created on. 
     _listBox_Output.Items.Add(updateMsg); 
    } 

-

// Class with long running process and event  
public delegate void StatusUpdateHandler(string updateMsg); 

public class ProcessWithEvents 
    { 
    public event StatusUpdateHandler StatusUpdate; 

    public async Task Run() 
    { 
     await Task.Run(() => 
     { 
      for (int i = 0; i < 10; i++) 
       { 

        RaiseUpdateEvent(String.Format("Update {0}", i)); 

        Thread.Sleep(500); 
       } 
      }); 

     } 

     private void RaiseUpdateEvent(string msg) 
     { 
     if (StatusUpdate != null) 
      StatusUpdate(msg); 
     } 
    } 

-

答えて

20

The async pattern has support for progress updates

つまり、asyncメソッドはで、呼び出しコードはそのインターフェイスの実装(通常Progress<T>)に入ります。

public class ProcessWithUpdates 
{ 
    public async Task Run(IProgress<string> progress) 
    { 
    await Task.Run(() => 
    { 
     for (int i = 0; i < 10; i++) 
     { 
     if (progress != null) 
      progress.Report(String.Format("Update {0}", i)); 
     Thread.Sleep(500); 
     } 
    }); 
    } 
} 

// calling code 
ProcessWithUpdates pwp = new ProcessWithUpdates(); 
await pwp.Run(new Progress<string>(pwp_StatusUpdate)); 
+0

代わりに、 'ProcessWithProject'が' ProcessWithUpdates'になっていますか? –

+0

@Brock:はい。編集されました。 –

2

あなたはControlInvoke方法を使用する必要があります。これは、コントロールのスレッドでいくつかのコードを実行します。 Invokeメソッドを呼び出す必要があるかどうかを確認するには、InvokeRequiredプロパティをチェックします(呼び出し元がコントロールが作成されたスレッドとは別のスレッドにあるかどうかを確認します)。

簡単な例:

void SomeAsyncMethod() 
{ 
    // Do some work    

    if (this.InvokeRequired) 
    { 
     this.Invoke((MethodInvoker)(() => 
      { 
       DoUpdateUI(); 

      } 
     )); 
    } 
    else 
    { 
     DoUpdateUI(); 
    } 
} 

void DoUpdateUI() 
{ 
    // Your UI update code here 
} 

は、いくつかのケースでは、Invokeメソッドを呼び出す前にControlIsHandleCreatedプロパティをチェックする必要があります。

var add = new Add((msg) => { 
    _listBox_Output.Items.Add(msg); 
}); 

:偽IsHandleCreatedリターンは、あなたがコントロールのハンドルが

+0

だけでなく、進捗suggessionです。私は非常に感謝の両方のアプローチを使用しています。 – ChiliYago

+0

非同期パターンを使用しているときは、これを実装しないでください。 'Progress 'は既に作成されたSynchronizationContextで実行されます。 – cremor

+0

シンプルなソリューション。よく働く。 –

2

//ただ、そう

delegate void Add(string msg); 

//そして、そのようにデリゲートメソッドを宣言するようにデリゲートを宣言し作成されている間待つ必要がある場合//デリゲートを呼び出します。

void pwe_StatusUpdate(string updateMsg) 
    { 

     _listBox_Output.Invoke(add,updateMsg); 
    } 
0

は、ここでこれは素晴らしい作品別の例

async void DoExport() 
{ 
    var rMsg = ""; 
    var t = await Task<bool>.Factory.StartNew(() => ExportAsMonthReport(LastMonth.Name, LastYear.Name, out rMsg)); 

    if (t) 
    { 
      BeginInvoke((Action)(() => 
      { 
       spinnerMain.Visible = false; 
       menuItemMonth.Enabled = true; 

       MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Information, 200); 
      })); 
    } 
    else 
    { 
      BeginInvoke((Action)(() => 
      { 
       spinnerMain.Visible = false; 
       menuItemMonth.Enabled = true; 

       MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Error, 200); 
      })); 
    } 
} 
関連する問題