等待任务完成而不阻塞 UI,奇怪的异常

Wait for task to finish without blocking the UI, strange exception

我创建了一个小应用程序来使用基本的 mvvm 模式重命名一些文件。 文件被复制到具有新名称的新文件夹,并且有一个进度条显示进度(成功)。由于有很多文件,我通过 Task.Factory.StartNew(...) 进行处理。进度条颜色为橙色;当所有文件都重命名后,颜色应变为绿色。

这是 xaml 的一部分:

<ProgressBar x:Name="pBar" HorizontalAlignment="Left" Height="20" Margin="10,5" Foreground="{Binding ProgBarBrush}" VerticalAlignment="Top" Width="100" Value="{Binding ProgBarValue}" Maximum="{Binding MaxIndex}"/>

视图模型:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

    private int _maxIndex;
    public int MaxIndex
    {
        get { return _maxIndex; }
        set
        {
            if (value == _maxIndex)
                return;
            _maxIndex = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaxIndex)));
        }
    }

    private int _pBarValue;
    public int ProgBarValue
    {
        get { return _pBarValue; }
        set
        {
            if (value == _pBarValue)
                return;
            _pBarValue = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(ProgBarValue)));
        }
    }

    private SolidColorBrush _brush;
    public SolidColorBrush ProgBarBrush
    {
        get { return _brush; }
        set
        {
            if (value == _brush)
                return;
            _brush = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(ProgBarBrush)));
        }
    }
}

任务:

private void StartRename_Click(object sender, RoutedEventArgs e)
{
    var newDir = dir + @"\Renamed";
    Directory.CreateDirectory(newDir);

    vm.ProgBarValue = 0;
    vm.ProgBarBrush = new SolidColorBrush(Colors.Orange);

    Task t1 = Task.Factory.StartNew(() =>
    {
        foreach (var pic in pics)
        {
            if (File.Exists(newDir + @"\" + pic.newName))
            {
                MessageBox.Show("File " + newDir + @"\" + pic.newName + " already exists. Skipping remaining files...", "File already exists");
                return;
            }

            File.Copy(pic.fileInfo.FullName, newDir + @"\" + pic.newName);
            vm.ProgBarValue++;
        }
        vm.ProgBarBrush = new SolidColorBrush(Colors.Green);
    });

}

绑定有效 - 进度条的值发生变化,UI 线程中的第一个颜色也变为橙色。这些文件也以我想要的方式被复制和重命名。 但是当我像这样改变颜色时,我得到一个奇怪的 "kind of exception"(Visual Studio 2017 只是停止):

Ihre App wurde angehalten, aber es gibt keinen anzuzeigenden Code, da alle Threads externen Code ausgeführt haben (normalerweise System- oder Frameworkcode).

Your app was halted, but there is no code to be shown because all threads ran external code (usually System or Framworkcode) => this is my own translation

当我使用 ContinueWith 时,我得到相同的 "exception"。使用 Task.WaitAll() 然后在 UI 线程中进行颜色设置会冻结 UI。如何在不冻结 UI 的情况下重命名所有文件后更改颜色?

感谢您的帮助。

您需要从 UI 线程更改进度条颜色和值。您可以使用 async/await 轻松做到这一点,如本示例所示。另外,我使用 IProgress.

更新了进度
private async void StartRename_Click(object sender, RoutedEventArgs e)
{
    var newDir = dir + @"\Renamed";
    Directory.CreateDirectory(newDir);

    vm.ProgBarValue = 0;
    vm.ProgBarBrush = new SolidColorBrush(Colors.Orange);
    var progress = new Progress<int>(_ => vm.ProgBarValue++)
    await Task.Run(() =>
    {
        foreach (var pic in pics)
        {
            if (File.Exists(newDir + @"\" + pic.newName))
            {
                MessageBox.Show("File " + newDir + @"\" + pic.newName + " already exists. Skipping remaining files...", "File already exists");
                return;
            }

            File.Copy(pic.fileInfo.FullName, newDir + @"\" + pic.newName);
            progress.Report(1);
        }                
    });
    vm.ProgBarBrush = new SolidColorBrush(Colors.Green);
}

正如@ckuri 在评论中提到的,IO 绑定操作可以通过 async api 正确完成。 See this reference 关于如何进一步改进代码。给出的第一个示例是异步复制文件。