C# Как отменить индикатор выполнения? - PullRequest
3 голосов
/ 14 января 2020

Я пытаюсь развить код из этого учебника по индикаторам выполнения с помощью кнопки отмены, но пока безуспешно:

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

После нажатия кнопки Run пользовательский интерфейс завис и я не могу нажать на кнопку Отмена. Я читал эти блоги:

Но не смог найти ответ, у меня также есть попробовал этот вариант:

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

И это не сработало, не могу нажать на кнопку отмены во время загрузки. Есть идеи? (Я уверен, что это до смешного просто).

1 Ответ

2 голосов
/ 15 января 2020

не может нажать на кнопку отмены во время загрузки. Есть идеи? (Я уверен, что это до смешного просто).

Как уже отмечали другие, вашему DoWork нужно проделать дополнительную работу, прежде чем прерывать поток пользовательского интерфейса другим обновлением. Итак, что-то вроде этого должно работать:

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

В настоящее время поток пользовательского интерфейса заполнен обновлениями прогресса, поэтому у него нет времени на ответ пользователя.

Если ваш реальный Мировой код нелегко разбить на более крупные куски, вы можете использовать реализацию 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);
  }
}

Использование:

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<T> отбрасывает дополнительные обновления прогресса, отправляя только одно обновление прогресса каждые 100 мс в поток пользовательского интерфейса, который он должен легко обрабатывать и реагировать на него. взаимодействие с пользователем.

...