【问题标题】:WPF MVVM await: Cancel task does not stop my progressbarWPF MVVM 等待:取消任务不会停止我的进度条
【发布时间】:2023-03-09 07:28:01
【问题描述】:

我正在开发一个 .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 有关?

任何帮助将不胜感激!

【问题讨论】:

    标签: c# wpf mvvm async-await


    【解决方案1】:

    我在“开始”和“停止”上都调用了“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 具有笨拙的错误处理语义并且不可单元测试。

    【讨论】:

    • 感谢斯蒂芬为我指明了正确的方向。我创建了一种新的取消方法;基本上是在做 _tokenSource.Cancel()。 MSDN 文章非常清楚,我正在重新设计一些东西以使用 async Task 而不是 async void 现在。继续努力!
    猜你喜欢
    • 2017-01-22
    • 1970-01-01
    • 2017-10-25
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    相关资源
    最近更新 更多