WPF MVVM等待:取消任务不会停止我的进度条

时间:2015-02-10 14:32:23

标签: c# wpf mvvm async-await

我正在研究.NET 4.5小型WPF / MVVM,我正在努力让一个进度条与async / await一起工作正确。

我已经把事情搞定了,但是我无法正确取消任务。或者,我认为任务可能会被取消,但无论如何,进度条都会全长运行。

我在“开始”和“停止”时调用“LongRun”方法,但在我想要停止时使用可选的取消令牌参数。这可能是一种不寻常的做事方式,但我认为它看起来没问题 - 至少在理论上: - )

以下是一些代码:

视图模型

public MainViewModel()
    {
        _progress = new Progress<int>(ReportProgress);
        CountFilesCreatedCommand = new RelayCommand<IProgress<int>>(progress => LongRun(Progress));
        _tokenSource = new CancellationTokenSource();
        _tokenSource.Cancel();
        StopCountFilesCommand = new RelayCommand<CancellationToken>(token => LongRun(Progress, _tokenSource.Token));
        ProgressText = "...";
    }

    private void ReportProgress(int result)
    {
        PercentProgress = result;
    }

    private async void LongRun(IProgress<int> progress, CancellationToken token = default(CancellationToken))
    {
        try
        {
            // Start long running task here
            int i = 0;
            for (; i != 101; ++i)
            {
                if (token.IsCancellationRequested)
                {
                    // This part of the code is executed when I
                    // try to stop the Task, I can see the "Cancelled!"
                    // text flicker for a second, and then the code
                    // resume to run
                    _tokenSource.Cancel();
                    progress?.Report(PercentProgress);
                    ProgressText = "Cancelled!";
                 }
                await Task.Delay(TimeSpan.FromSeconds(0.1), token).ConfigureAwait(false);
                                    progress?.Report(i);
                ProgressText = i + "%";
            }
        }
        catch (OperationCanceledException)
        {

            //throw;
        }
    }

XAML

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <StackPanel>
        <!--<Button Margin="10,18,10,0" Content="Start Long Run" Height="30" Click="Button_Click_Async"/>-->
        <ProgressBar x:Name="pbStatus" Margin="10,10,10,0" Height="30" IsIndeterminate="False" Visibility="Visible" Minimum="0" Maximum="100" Value ="{Binding PercentProgress, Mode=TwoWay}"/>
        <TextBlock x:Name="tbResultat" Margin="0,10,0,10" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding ProgressText}" />
        <DockPanel>
            <Button Margin="10,0" Content="Start Long Run" Height="30" Width="200" Command="{Binding CountFilesCreatedCommand}"/>
            <Button Margin="10,0" Content="Stop Long Run" Height="30" Width="200" HorizontalAlignment="Right" Command="{Binding StopCountFilesCommand}"/>
        </DockPanel>
    </StackPanel>
</Grid>

我觉得解决方案可能与XAML有关吗?

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:4)

  

我在“开始”和“停止”时调用“LongRun”方法,但在我想要停止时使用可选的取消令牌参数。

这就是你的问题所在。当您停止时,您希望现有LongRun的调用存在,而不是创建对LongRun调用。

这样的事情会更合适(假设此操作只能执行一次,并且您根据需要启用/禁用按钮):

_progress = new Progress<int>(ReportProgress);
_tokenSource = new CancellationTokenSource();
CountFilesCreatedCommand = new RelayCommand(() => LongRun(_progress, _tokenSource.Token));
StopCountFilesCommand = new RelayCommand(() => _tokenSource.Cancel());
ProgressText = "...";

在旁注中,您应该avoid async void(正如我在MSDN文章中所述)。 async void具有笨拙的错误处理语义,不能单元测试。