如何在wpf后台执行任务,同时能够提供报告并允许取消?

Sya*_*hya 11 c# wpf task-parallel-library

我想在点击一个wpf按钮后执行一个长时间运行的任务.这就是我做的.

private void Start(object sender, RoutedEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(2000); // simulate task
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,这会使wpf gui反应迟钝.我也想允许取消并每1秒报告一次进度.我扩展代码如下.

    DispatcherTimer dispatcherTimer = new DispatcherTimer(); // get progress every second
    private int progress = 0; // for progress reporting
    private bool isCancelled = false; // cancellation

    private void Start(object sender, RoutedEventArgs e)
    {
        InitializeTimer(); // initiallize interval timer 
        Start(10); // execute task
    }

    private void InitializeTimer()
    {
        dispatcherTimer.Tick += dispatcherTimer_Tick;
        dispatcherTimer.Interval = new TimeSpan(0,0,1);
        dispatcherTimer.Start();
    }

    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        Logger.Info("Current loop progress " + progress); // report progress
    }

    private void Cancel(object sender, RoutedEventArgs e) // cancel button
    {
        isCancelled = true;
    }

    private int Start(int limit)
    {
        isCancelled = true;
        progress = 0;

        for (int i = 0; i < limit; i++)
        {
            Thread.Sleep(2000); // simulate task
            progress = i; // for progress report
            if (isCancelled) // cancellation
            {
                break;
            }
        }
        return limit;
    }
Run Code Online (Sandbox Code Playgroud)

我的目标平台是.NET 4.5.建议的方法是什么?

谢谢.

nos*_*tio 20

我以为我在这里回答了你的问题.如果你需要更多关于如何使用任务并行库执行此操作的示例代码,使用CancellationTokenSourceIProgress<T>,这里是:

Action _cancelWork;

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    this.StartButton.IsEnabled = false;
    this.StopButton.IsEnabled = true;
    try
    {
        var cancellationTokenSource = new CancellationTokenSource();

        this._cancelWork = () => 
        {
            this.StopButton.IsEnabled = false;
            cancellationTokenSource.Cancel();
         };

        var limit = 10;

        var progressReport = new Progress<int>((i) => 
            this.TextBox.Text = (100 * i / (limit-1)).ToString() + "%");

        var token = cancellationTokenSource.Token;

        await Task.Run(() =>
            DoWork(limit, token, progressReport), 
            token);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    this.StartButton.IsEnabled = true;
    this.StopButton.IsEnabled = false;
    this._cancelWork = null;
}

private void StopButton_Click(object sender, RoutedEventArgs e)
{
    this._cancelWork?.Invoke();
}

private int DoWork(
    int limit, 
    CancellationToken token,
    IProgress<int> progressReport)
{
    var progress = 0;

    for (int i = 0; i < limit; i++)
    {
        progressReport.Report(progress++);
        Thread.Sleep(2000); // simulate a work item
        token.ThrowIfCancellationRequested();
    }
    return limit;
}
Run Code Online (Sandbox Code Playgroud)

  • @RohitVats,`IProgress <T>`适用于带有[Microsoft.Bcl.Async](http://www.nuget.org/packages/Microsoft.Bcl.Async)的.NET 4.0,这是一个生产质量库.它也可以很容易地用`Dispatcher.BeginInvoke`一行代替. (2认同)
  • @RohitVats,`IProgress <T> .Report`是**异步**.创建时,"Progress <T>"捕获UI线程的同步上下文.然后,当从工作线程调用`Report`时,它在内部使用`SynchronizationContext.Post`,而不是`SynchronizationContext.Send`.在使用`DispatcherSynchronizationContext`的情况下,`Post`是`Dispatcher.BeginInvoke`的包装器.如果你用Reflector或类似工具检查它,那么`Progress <T>`的实现是微不足道的. (2认同)