我正在开发一个有时会删除文件夹的应用程序。为了向用户显示进度,我在视图中使用了ProgressBar
。因此,我在ViewModel double SetupProgress
和double SetupProgressMax
上有两个属性。我会编译要删除的文件夹中包含的所有文件的列表,并且每次成功删除文件后,都会更新属性SetupProgress
。我不知道要事先删除的文件数量。
我的代码(浓缩到重要部分):
public class ViewModel : INotifyPropertyChanged
{
public double SetupProgress
{
get; set; // Notifies about changes
}
public double SetupProgressMax
{
get; set; // Notifies about changes
}
public async Task<bool> DeleteFiles(IList<string> filesToBeDeleted)
{
bool success = true;
SetupProgressMax = filesToBeDeleted.Count;
foreach (string filePath in filesToBeDeleted)
{
success = success && await IOHelper.TryDeleteFile(filePath);
if (success)
{
// Report that one item has been processed.
_OnProgressChanged();
}
else
{
break;
}
}
return success;
}
public void _OnProgressChanged()
{
// SetupProgress is the VM property bound by the ProgressBar
SetupProgress++;
}
}
public static class IOHelper
{
public static async Task<bool> TryDeleteFile(string filePath, int tries = 3)
{
while (tries > 0)
{
try
{
FileInfo fi = new FileInfo(filePath);
if (fi.IsReadOnly)
{
fi.IsReadOnly = false;
}
fi.Delete();
return true;
}
catch (FileNotFoundException)
{
return true;
}
catch (Exception ex)
{
tries--;
if (tries == 0)
{
// Log error
}
else
{
// Log warning
}
await Task.Delay(50);
}
}
return false;
}
}
我的问题是,在删除文件时,UI线程会完全阻塞,并且仅在操作完成后(所有文件都已删除)才会更新。
更新:删除了我在发布问题之前测试过的解决方案,因为它们不起作用,甚至无法解决问题的根源。
答案 0 :(得分:3)
似乎TryDeleteFile
正在UI线程中执行。考虑到当前的实现,它不是异步方法,不应返回Task
,而应返回bool
:
public static bool TryDeleteFile(string filePath)
{
try
{
FileInfo fi = new FileInfo(filePath);
if (fi.IsReadOnly)
{
fi.IsReadOnly = false;
}
fi.Delete();
return true;
}
catch (FileNotFoundException)
{
return true;
}
catch (Exception ex)
{
// Log Exception
return false;
}
}
await
关键字是完全不需要的,因为该方法缺少任何await
操作。
您可以使用Task.Run
在视图模型的后台线程上调用同步方法:
public async Task<bool> DeleteFiles(IList<string> filesToBeDeleted)
{
...
foreach (string filePath in filesToBeDeleted)
{
success = success && await Task.Run(() => IOHelper.TryDeleteFile(filePath));
...
}
return success;
}
请注意,使用异步API公开真正同步的方法是一种不好的做法。有关更多信息,请参阅Stephen Toub's blog post。
答案 1 :(得分:0)
您的TryDeleteFile(string filePath)
方法缺少 await 运算符,它将同步运行。
您可以将同步代码包装到异步调用中,更简单的方法是使用 Task.Run
编辑
没有本机功能可以异步执行文件删除。但是,通过使用FileStream
,仍然可以执行异步文件删除。
public static bool TryDeleteFile(string filePath)
{
try
{
var fi = new FileInfo(filePath);
if (fi.IsReadOnly) fi.IsReadOnly = false;
using (new FileStream(filePath, FileMode.Truncate, FileAccess.ReadWrite, FileShare.Delete, 1,
FileOptions.DeleteOnClose | FileOptions.Asynchronous))
{
}
return true;
}
catch (FileNotFoundException)
{
return true;
}
catch (Exception ex)
{
// Log Exception
return false;
}
}
用法
await Task.Run(() => TryDeleteFile(filePath));