3

非同期で実行するタスクを作成する .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 を更新できるようにする効果があります。

4

2 に答える 2

1

UpdateProgressCount では、代わりに進行状況でフォームを呼び出したい場合があります。これは、別のタスクを作成するのではなく、物事を更新する標準的な方法です。

さらに、UI スレッドで実行されているタスクを待機することで、バックグラウンド スレッドが引き続き実行されると思います。しかし、私はその部分について間違っているかもしれません。とにかく、進行状況でフォームを呼び出すと、問題が解決するはずです。

于 2012-09-12T14:03:42.077 に答える
0

WinForm の状態を更新するために invoke メソッドを呼び出して、UI 更新用の別のタスクを作成しないでくださいprogressBar

を交換してください

UpdateUserInterface(() => progressBar.Value = count)

if (progressBar.InvokeRequired) {
    progressBar.Invoke(() => progressBar.Value = count);
} else {
    progressBar.Value = count;
}
于 2012-09-12T14:32:26.453 に答える