【问题标题】:C# How to cancel a progress bar?C#如何取消进度条?
【发布时间】:2020-01-14 16:55:17
【问题描述】:

我正在尝试使用取消按钮从 Progress bar tutorial 演变代码,但到目前为止没有成功:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private CancellationTokenSource cts;

    private void Calculate(int i)
    {
        Math.Pow(i, i);
    }

    public void DoWork(IProgress<int> progress, CancellationToken cancelToken)
    {
        for (int j = 0; j < 100000; j++)
        {
            if (cancelToken.IsCancellationRequested)
                cancelToken.ThrowIfCancellationRequested();
            Calculate(j);
            progress?.Report((1 + j) * 100 / 100000);
        }
    }

    private async void run_Click(object sender, EventArgs e)
    {
        cts = new CancellationTokenSource();
        var cancelToken = cts.Token;
        progressBar.Maximum = 100;
        progressBar.Step = 1;
        var progress = new Progress<int>(v => progressBar.Value = v);

        try
        {
            await Task.Run(() => { DoWork(progress, cancelToken); }, cts.Token);
        }
        catch (OperationCanceledException ex)
        {
            Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {ex.Message}");
        }
        finally
        {
            cts = null;
        }
    }

    private void cancel_Click(object sender, EventArgs e)
    {
        Console.WriteLine("Cancel");
        cts?.Cancel();
    }
}

点击运行后,用户界面冻结,我无法点击取消按钮。我读过这些博客:

但找不到答案,我也尝试过这种变体:

    await Task.Factory.StartNew(() => { DoWork(progress, cancelToken); }, cts.Token);

而且它不起作用,在加载过程中无法点击取消按钮。任何想法? (我敢肯定这非常简单)。

【问题讨论】:

  • 看起来您的每次迭代都在报告进度,并且没有延迟。然后,您的循环将运行得太快,无法让 ui 更新和响应。将await Task.Delay(TimeSpan) 之类的内容添加到您的DoWork
  • 查看Progress<> 的文档:Progress 类在其创建时捕获同步上下文(此处为 UI 线程),然后切换到此同步。调用Report 时的上下文。因此,对于非常紧凑的循环——比如这里——你会不断地切换到 UI 线程,并考虑到更新进度比你的 Compute 函数更昂贵,你的 DoWork 方法主要在 UI 线程上运行并阻塞它。您应该每 1000 件左右报告一次。
  • 谢谢你们,我只是在progress?.Report((1 + j) * 100 / 100000000); 之前添加了if (j % 500 == 0) 并将循环增加到100000000,它就像一个魅力。正如你所说,代码实际上是正确的,DoWork 没有做足够的工作。

标签: c# task progress-bar


【解决方案1】:

在加载过程中无法点击取消按钮。任何想法? (我敢肯定这非常简单)。

正如其他人所指出的,您的DoWork 需要做更多的工作,然后才能通过另一个更新中断 UI 线程。所以这样的事情应该可以工作:

private void Calculate(int i)
{
  for (int j = 0; j != 10000; ++j)
    Math.Pow(i, i);
}

目前,UI 线程充斥着进度更新,因此没有时间响应用户输入。

如果您的实际代码不容易拆分成更大的块,您可以在更新my book 时使用IProgress&lt;T&gt; 之类的速率限制one I wrote 实现:

public static class ObservableProgress
{
  public static (IObservable<T> Observable, IProgress<T> Progress) CreateForUi<T>(TimeSpan? sampleInterval = null)
  {
    var (observable, progress) = Create<T>();
    observable = observable.Sample(sampleInterval ?? TimeSpan.FromMilliseconds(100))
        .ObserveOn(SynchronizationContext.Current);
    return (observable, progress);
  }

  public static (IObservable<T> Observable, IProgress<T> Progress) Create<T>()
  {
    var progress = new EventProgress<T>();
    var observable = Observable.FromEvent<T>(handler => progress.OnReport += handler, handler => progress.OnReport -= handler);
    return (observable, progress);
  }

  private sealed class EventProgress<T> : IProgress<T>
  {
    public event Action<T> OnReport;
    void IProgress<T>.Report(T value) => OnReport?.Invoke(value);
  }
}

用法:

private async void run_Click(object sender, EventArgs e)
{
  cts = new CancellationTokenSource();
  var cancelToken = cts.Token;
  progressBar.Maximum = 100;
  progressBar.Step = 1;
  var (observable, progress) = ObservableProgress.CreateForUi<int>();

  try
  {
    using (observable.Subscribe(v => progressBar.Value = v))
      await Task.Run(() => { DoWork(progress, cancelToken); }, cts.Token);
  }
  catch (OperationCanceledException ex)
  {
    Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {ex.Message}");
  }
  finally
  {
    cts = null;
  }
}

速率限制IProgress&lt;T&gt; 将丢弃额外的进度更新,每 100 毫秒仅向 UI 线程发送一个进度更新,它应该能够轻松处理并保持对用户交互的响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-02
    • 2011-06-21
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多