简体   繁体   English

C# 如何取消进度条?

[英]C# How to cancel a progress bar?

I'm trying to evolve the code from this Progress bar tutorial with a cancel button but so far without success:我正在尝试使用取消按钮改进此进度条教程中的代码,但到目前为止还没有成功:

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();
    }
}

After clicking run, the UI froze and I can't click on the Cancel button.单击运行后,用户界面冻结,我无法单击取消按钮。 I've read those blogs:我读过这些博客:

But couldn't find an answer, I have also tried this variation:但找不到答案,我也尝试过这种变体:

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

And it didn't work, can't click on the cancel button during the loading.而且它不起作用,加载过程中无法单击取消按钮。 Any idea?任何的想法? (I'm sure it's ridiculously simple). (我敢肯定这很简单)。

can't click on the cancel button during the loading.加载过程中无法点击取消按钮。 Any idea?任何的想法? (I'm sure it's ridiculously simple). (我敢肯定这很简单)。

As others have noted, your DoWork needs to do more work before interrupting the UI thread with another update.正如其他人所指出的,您的DoWork需要做更多的工作,然后才能用另一个更新中断 UI 线程。 So something like this should work:所以这样的事情应该有效:

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

Currently, the UI thread is swamped with progress updates, so it doesn't have time to respond to user input.目前,UI 线程被进度更新所淹没,因此它没有时间响应用户输入。

If your real-world code is not easily split into larger chunks, you can use a rate-throttling IProgress<T> implementation like one I wrote when updating my book :如果您的实际代码不容易分成更大的块,您可以使用速率限制IProgress<T>实现,就像在更新我的书写的那样:

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);
  }
}

Usage:用法:

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;
  }
}

The rate-throttling IProgress<T> will throw away extra progress updates, only sending one progress update every 100ms to the UI thread, which it should easily be able to handle and remain responsive to user interaction.速率限制IProgress<T>将丢弃额外的进度更新,每 100 毫秒仅向 UI 线程发送一个进度更新,它应该能够轻松处理并保持对用户交互的响应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM