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

时间:2018-01-03 22:04:50

标签: c# wpf user-interface task-parallel-library

我创建了一个小应用程序,使用基本的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线程中的第一个颜色更改为橙​​色。这些文件也会以我想要的方式复制和重命名。 但是,当我正在进行这样的颜色更改时,我会得到一个奇怪的“异常”(Visual Studio 2017简直停止):

  

Ihre App wurde angehalten,aber es gibt keinen anzuzeigenden Code,   da alle Threads externenCodeausgeführthaben(normalerweise System-   oder Frameworkcode)。

     

您的应用已停止,但没有代码可以显示,因为全部   线程运行外部代码(通常是System或Framworkcode)=&gt;这是我自己的翻译

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

感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

您需要从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在评论中提到的,使用async api正确完成了IO绑定操作。 See this reference关于如何进一步改进代码。给出的第一个例子是异步复制文件。