【问题标题】:Using a TPL Task to update the UI does not immediately update the UI使用 TPL 任务更新 UI 不会立即更新 UI
【发布时间】:2012-09-12 22:32:26
【问题描述】:

我有一个 .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 的任务已启动,然后调用 Wait(),但异步任务似乎在更新 UI 之前继续运行。我认为这是因为 UI 线程本身执行了某种 BeginInvoke() 来更新 UI。

最终,当异步(繁重的工作)任务完成时,UI 并未完全更新并且表单关闭。但是 UI 任务 Wait()、Application.DoEvents() 或 progressBar.Update() 有任何作用,可以让 UI 在返回繁重的工作任务之前进行更新。

【问题讨论】:

    标签: c# winforms task-parallel-library


    【解决方案1】:

    在 UpdateProgressCount 中,您可能希望使用进度调用表单。这是更新事物的标准方式,而不是通过创建另一个任务。

    此外,我相信通过等待 UI 线程上运行的任务,您的后台线程将继续运行。但我可能对那部分不正确。无论如何,如果您使用应该解决问题的进度调用表单。

    【讨论】:

    • 所以.. 用简单的 Invoke(action) 替换 UpdateUserInterface() 中的任务 -> 不起作用。
    • 您确定您的进度条设置为最大值 1000?
    • 是的,当 FormClosing 事件触发时,它肯定是 1000。UI 任务完成后,UI 不会立即更新。如果我在 UI 任务 Wait() 之后添加 Thread.Sleep(50),进度条会达到 75%。如果我将其更改为 Thread.Sleep(100),进度条将达到 95%。此外,如果我从异步任务延续中删除 Close() 调用,UI 自然会完成更新并且进度达到 100%。
    • 任何地方都不应该有Wait()。如果您将 updateuserinterface 替换为调用,它应该可以工作。
    • @user981225 Invoke 应该可以工作,但 OPs 解决方案也应该如此。我很想知道这个错误是什么。
    【解决方案2】:

    不要为UI更新创建另一个任务,调用invoke方法更新WinFormprogressBar中的状态。

    替换

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

    到

    if (progressBar.InvokeRequired) {
        progressBar.Invoke(() => progressBar.Value = count);
    } else {
        progressBar.Value = count;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-12-07
      • 1970-01-01
      • 2020-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多