web-dev-qa-db-ja.com

Dispatcher.Invokeを使用して非メインスレッドからWPFコントロールを変更する

私は最近WPFでプログラミングを開始し、次の問題にぶつかりました。 Dispatcher.Invoke()メソッドの使用方法がわかりません。私はスレッド化の経験があり、いくつかの単純なWindows Formsプログラムを作成しました。

Control.CheckForIllegalCrossThreadCalls = false;

はい、それはかなり不自由ですが、これらは単純な監視アプリケーションでした。

事実は、バックグラウンドでデータを取得するWPFアプリケーションを作成しています。新しいスレッドを開始して(Webサーバーから)データを取得するための呼び出しを行い、WPFフォームに表示するようになりました。問題は、このスレッドからコントロールを設定できないことです。ラベルでも何でもありません。これはどのように解決できますか?

回答コメント:
@ Jalfp:
つまり、データを取得するときに「新しいトレッド」でこのDispatcherメソッドを使用しますか?または、バックグラウンドワーカーにデータを取得させ、フィールドに入れて待機する新しいスレッドを開始する必要がありますこのフィールドがいっぱいになるまで、ディスパッチャを呼び出して、取得したデータをコントロールに表示しますか?

68
D. Veloper

最初のことは、Dispatcherが長いブロッキング操作(WebServerからのデータの取得など)を実行するように設計されていないことを理解することです。 UIスレッドで実行される操作(進行状況バーの値の更新など)を実行する場合は、Dispatcherを使用できます。

できることは、バックグラウンドワーカーでデータを取得し、ReportProgressメソッドを使用してUIスレッドの変更を伝達することです。

Dispatcherを直接使用する必要がある場合は、非常に簡単です。

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));
164
japf

japfが正しく答えています。複数行のアクションを見ている場合に備えて、次のように書くことができます。

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => { 
    this.progressBar.Value = 50;
  }));

パフォーマンスについて知りたい他のユーザー向けの情報:

高いパフォーマンスを得るためにコードを記述する必要がある場合は、CheckAccessフラグを使用して、最初に呼び出しが必要かどうかを確認できます。

if(Application.Current.Dispatcher.CheckAccess())
{
    this.progressBar.Value = 50;
}
else
{
    Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => { 
        this.progressBar.Value = 50;
      }));
}

メソッドCheckAccess()はVisual Studio 2015から隠されているため、インテリセンスが表示されることを期待せずに記述してください。 CheckAccessのパフォーマンスにはオーバーヘッドがあります(数ナノ秒のオーバーヘッド)。どんなコストでも「呼び出し」を実行するのに必要なマイクロ秒を節約したい場合にのみ優れています。また、メソッドの呼び出しがUIスレッド内にあるかどうかが確実な場合は、常に2つのメソッド(invokeを使用する場合と使用しない場合)を作成するオプションがあります。ディスパッチャのこの側面を確認する必要があるのは、ごくまれです。

25
Yogee

スレッドが実行されており、現在のスレッドによってブロックされているメインUIスレッドを実行する場合は、以下を使用します。

現在のスレッド:

Dispatcher.CurrentDispatcher.Invoke(MethodName,
    new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.

メインUIスレッド:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));
1
Gopi Rao