あなたは少なくとも1つの誤った仮定に基づいて行動しているように私には思えます。
1.レスポンシブUIを使用するために、ProgressChangedイベントを発生させる必要はありません。
あなたの質問であなたはこれを言います:
進行状況の通知が届かない可能性があるため、BackgroundWorkerは答えではありません。つまり、DoWorkは外部関数への単一の呼び出しであるため、ProgressChangedへの呼び出しはありません。。。
実際には、イベントを呼び出すかどうかは関係ありませProgressChanged
ん。このイベントの全体的な目的は、一時的に制御をGUIスレッドに戻し、によって行われている作業の進行状況を何らかの形で反映する更新を行うことBackgroundWorker
です。マーキープログレスバーを表示しているだけの場合、イベントを発生させることは実際には無意味ProgressChanged
です。プログレスバーは、GUIとは別のスレッドで作業を行っているため、BackgroundWorker
表示されている限り回転し続けます。
(ちなみにDoWork
、これはイベントです。これは、単なる「外部関数への単一の呼び出し」ではないことを意味します。必要な数のハンドラーを追加できます。これらの各ハンドラーには、必要な数の関数呼び出しを含めることができます。好きです。)
2.レスポンシブUIを使用するためにApplication.DoEventsを呼び出す必要はありません
私には、GUIを更新する唯一のApplication.DoEvents
方法は、次のコマンドを呼び出すことであるとあなたが信じているように思えます。
Application.DoEvents();を呼び出し続ける必要があります。プログレスバーが回転し続けるようにします。
これは、マルチスレッドシナリオでは当てはまりません。を使用すると、GUIは、そのイベントに関連付けられているすべての処理を実行しているBackgroundWorker
間、(独自のスレッドで)応答し続けます。以下は、これがどのように機能するかを示す簡単な例です。BackgroundWorker
DoWork
private void ShowProgressFormWhileBackgroundWorkerRuns() {
// this is your presumably long-running method
Action<string, string> exec = DoSomethingLongAndNotReturnAnyNotification;
ProgressForm p = new ProgressForm(this);
BackgroundWorker b = new BackgroundWorker();
// set the worker to call your long-running method
b.DoWork += (object sender, DoWorkEventArgs e) => {
exec.Invoke(path, parameters);
};
// set the worker to close your progress form when it's completed
b.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => {
if (p != null && p.Visible) p.Close();
};
// now actually show the form
p.Show();
// this only tells your BackgroundWorker to START working;
// the current (i.e., GUI) thread will immediately continue,
// which means your progress bar will update, the window
// will continue firing button click events and all that
// good stuff
b.RunWorkerAsync();
}
3.同じスレッドで2つのメソッドを同時に実行することはできません
あなたはこれを言います:
ワーカー関数がメインスレッドで機能している間にMarqueプログレスバーが機能するように、Application.DoEvents()を呼び出す必要があります。。。
あなたが求めているのは、単に本物ではありません。Windowsフォームアプリケーションの「メイン」スレッドはGUIスレッドであり、長時間実行するメソッドでビジー状態の場合、視覚的な更新を提供しません。そうでなければ、私はあなたが何をしているのか誤解しているのではないかと思います:それは別のスレッドBeginInvoke
でデリゲートを起動します。実際、質問に含めたサンプルコードは、との間で呼び出すために冗長です。とにかく更新されるGUIスレッドから実際に繰り返し呼び出しています。(別の方法を見つけた場合は、すぐに呼び出したため、メソッドが終了するまで現在のスレッドがブロックされた可能性があります。)Application.DoEvents
exec.BeginInvoke
exec.EndInvoke
Application.DoEvents
exec.EndInvoke
そうです、あなたが探している答えは、を使用することBackgroundWorker
です。
を使用することもできますが、GUIスレッドからBeginInvoke
呼び出す代わりに(メソッドが終了していない場合はブロックします)、呼び出しにパラメーターを渡し(単に渡すのではなく)、コールバックの進行状況フォームを閉じます。ただし、これを行う場合は、GUIスレッドから進行状況フォームを閉じるメソッドを呼び出す必要があることに注意してください。そうしないと、GUI関数であるフォームをから閉じようとするためです。非GUIスレッド。しかし、実際には、 「。NETマジックコード」だと思っていても、/を使用する際のすべての落とし穴は、クラスですでに対処されています(私にとっては、これは直感的で便利なツールです)。EndInvoke
AsyncCallback
BeginInvoke
null
BeginInvoke
EndInvoke
BackgroundWorker