我想构建一个文件夹清理程序。预计会将已删除的文件实时报告给TextBox
控件。所以我在我的按钮点击事件中使用了await Task.Run(() => CleanFolder(folderPath, progress))
功能。但是UI在运行时被阻止了。在CheanFolder()
方法运行完成一段时间后,所有已删除的文件都会一次显示。
namespace FolderCleaner
{
public partial class MainWindow : Window
{
string folderPath;
string matchPattern;
private void ButtonOpen_Click(object sender, RoutedEventArgs e)
{
FolderBrowserDialog fbd = new FolderBrowserDialog() { Description = "Select a folder" };
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
folderPath = fbd.SelectedPath;
textBoxPath.Text = folderPath;
buttonClean.IsEnabled = true;
textBoxList.Text = "Folder path: " + folderPath + "\n";
}
}
private async void ButtonClean_Click(object sender, RoutedEventArgs e)
{
matchPattern = textBoxPattern.Text;
buttonOpen.IsEnabled = false;
buttonClean.IsEnabled = false;
Progress<string> progress = new Progress<string>(msg =>
{
textBoxList.AppendText("File deleted: " + msg + "\n");
textBoxList.CaretIndex = textBoxList.Text.Length;
textBoxList.ScrollToEnd();
});
try
{
await Task.Run(() => CleanFolder(folderPath, progress));
textBoxList.AppendText("Mission complete!");
textBoxList.CaretIndex = textBoxList.Text.Length;
textBoxList.ScrollToEnd();
}
catch
{
System.Windows.MessageBox.Show("Error!");
}
finally
{
buttonOpen.IsEnabled = true;
}
}
private void CleanFolder(string path, IProgress<string> progress)
{
var filePaths = Directory.EnumerateFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
foreach (var filePath in filePaths)
{
var matchResult = Regex.Match(filePath, matchPattern);
if (matchResult.Success)
{
File.Delete(filePath);
progress.Report(filePath);
}
}
}
}
}
答案 0 :(得分:2)
GUI不能从另一个线程控制。
但我认为,真正的问题是将字符串和输出连接到TextBox是一种非常低效的操作。
在您的情况下,最好在一行中或使用进度条显示删除进度。
以下是我的问题解决方案(我改变了2种方法):
private async void ButtonClean_Click(object sender, RoutedEventArgs e)
{
matchPattern = textBoxPattern.Text;
buttonOpen.IsEnabled = false;
buttonClean.IsEnabled = false;
await Task.Run(() => CleanFolder(folderPath));
textBoxList.Text += "Mission complete!";
buttonOpen.IsEnabled = true;
}
private void CleanFolder(string path)
{
var filePaths = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories);
foreach (var filePath in filePaths)
{
var matchResult = Regex.Match(filePath, matchPattern);
if (matchResult.Success)
{
File.Delete(filePath);
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
// this working fast
textBoxList.Text = "File deleted: " + filePath + "\n";
// this working slow and slower over time
//textBoxList.Text += "File deleted: " + filePath + "\n";
textBoxList.ScrollToEnd();
});
}
}
}
我希望这会有所帮助。
答案 1 :(得分:0)
谢谢大家。多亏了这本书 C#6.0,简而言之
我已经找到了解决方案并且对async / await有了更好的理解。
首先,自.Net Framework 4.5以来,不建议使用Dispatcher.Invoke
,基于任务的异步已成为主导模式(使用async / awit)。
其次,使用async / await有一些原则:
await
后的表达必须是Task
或Task<TResult>
对象
如果您对方法使用async
修饰符,则方法为t
need to return a
任务method manually. The compile will wrap the
method as a
任务对象。
如果您使用async Task Foo()
之类的方法,则必须在其中使用await
关键字。
如果没有任何等待,请删除async
修饰符,使用Task
返回return Task.Run(() => { Do Something });
个对象。现在,您可以在调用await Foo()
。
Foo()
Task Foo()
无法操作用户界面,但async Task Foo()
可以。