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

Jor*_*enH 1 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

我感觉该解决方案可能与 XAML 有关?

任何帮助将不胜感激!

Ste*_*ary 5

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

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

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

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

顺便说一句,您应该避免async void(正如我在 MSDN 文章中所描述的那样)。async void具有尴尬的错误处理语义并且不可进行单元测试。