非同期で実行するタスクを作成する .NET Windows フォームがあります。そのタスクは、進行状況の UI を更新するために呼び出す必要があります。動作しますが、進行状況バーは何らかの遅延でのみ更新されます。
public partial class WaitDialog : Form
{
private readonly TaskScheduler _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private void ReportViewerWaitForm_Load(object sender, EventArgs e)
{
_asyncTask = Task.Factory.StartNew(Calculate(), _cancellationTokenSource.Token);
_asyncTask.ContinueWith(task => Close(), CancellationToken.None, TaskContinuationOptions.None, _uiScheduler);
}
private void Calculate()
{
UpdateProgressCount(0, 1000);
for (int i = 0; i < 1000; i++)
{
// do some heavy work here
UpdateProgressCount(i);
}
}
private void UpdateUserInterface(Action action)
{
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _uiScheduler).Wait();
}
public void UpdateProgressCount(int count)
{
UpdateUserInterface(() => progressBar.Value = count);
}
public void UpdateProgressCount(int count, int total)
{
UpdateUserInterface(() =>
{
progressBar.Minimum = 0;
progressBar.Maximum = total;
});
UpdateProgressCount(count);
}
private void WaitForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (!_asyncTask.IsCompleted)
{
e.Cancel = true;
}
}
}
プログレス バーは正しく設定されており、フォームが閉じるまでに値が 1000 (または 100%) に設定されますが、UI ではこのように表示されず、約 50% の完了しか表示されません。
UI を更新する Task が開始され、その後 Wait() が呼び出されますが、UI が更新される前に非同期タスクが実行され続けているようです。これは、UI スレッド自体が何らかの BeginInvoke() を実行して UI を更新したためだと思います。
最終的に、非同期 (重い作業) タスクが終了するまでに、UI は完全には更新されず、フォームは閉じます。ただし、UI タスクの Wait()、Application.DoEvents()、または progressBar.Update() には、負荷の高いタスクに戻る前に UI を更新できるようにする効果があります。