Reputation: 1
I want to build a folder cleaner program. It is expected to report deleted files to a TextBox
control at real-time. So I use await Task.Run(() => CleanFolder(folderPath, progress))
function in my button click event. But the UI blocked when running. After a while when the CheanFolder()
method run complete, all the deleted files are showed at one time.
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);
}
}
}
}
}
Upvotes: 0
Views: 138
Reputation: 1
Thanks for everyone. Thanks to the book C# 6.0 in a nutshell
I have figured out the solution and have a better understanding of async/await.
First of all, Dispatcher.Invoke
is not recommended to use since .Net Framework 4.5, task-based asynchrony has become the dominant pattern (using async/awit).
Second, there are a few principles of using async/await:
The expression after await
must be a Task
or Task<TResult>
object
If you use async
modifier to a method, then the method dont
need to return a
Taskmethod manually. The compile will wrap the
method as a
Task` object.
If you use a method like async Task Foo()
, you must use an await
keyword in it.
If there is nothing to await, then remove the async
modifier, return a Task
object by using return Task.Run(() => { Do Something });
. Now you can use await Foo()
in the method that calling Foo()
.
Task Foo()
can not operate UI, but async Task Foo()
can.
Upvotes: 0
Reputation: 429
GUI can`t be controlled from another thread.
But i think, that real problem is that concatenating of string and output to a TextBox is a very inefficient operation.
In your case it is better to show progress of removal in a single line or by using the progress bar.
Here is my solution of your problem (i`ve changed 2 methods):
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();
});
}
}
}
I hope this will help.
Upvotes: 2