The problem is that all of your code runs in the UI thread. The UI has no chance to update itself before the entire process finishes.
You can use Task.Run()
to run a heavy job in the background without blocking the UI. You can use the await
keyword to await that task to complete without blocking the UI. Once the task completes, you are back in the UI thread where you can modify the UI again.
A quick and dirty fix for this event handler would be :
private async void Start_Click(object sender, RoutedEventArgs e)
{
ProcessProgress.IsIndeterminate = true;
ProcessProgress.Visibility = Visibility.Visible;
...
await Task.Run(()=>
{
//process starts here
var fP = new FileProcessor();
...
});
//We are back in the UI thread, we can modify the UI
ProcessProgress.IsIndeterminate = false;
ProcessProgress.Value = ProcessProgress.Maximum;
}
No need to use Invoke
to get back to the UI thread, that's the job of await
itself.
A note about async void
. It's ONLY meant for event handlers or similar methods. You can't await an async void
method which means you can't even get any exceptions if something goes wrong. Asynchronous methods that return nothing should have the async Task
signature.
If you want to report progress you can use the IProgress interface and the Progress class as explained here. The Progress class will call a callback or raise an event on the thread it was created. The payload can be any class, not just a percentage
It's best to move the reporting code to separate methods, to keep the button handler clean. The code can look like this :
//The progress class
class FileProgress
{
public string FileName{get;set;}
public int Progress{get;set;}
public string Message{get;set;}
public FileProgress(string fileName,int progress,string message)
{
FileName=filename;
Progress=progress;
Message=message;
}
}
//In the form itself
private void ReportStart()
{
ProcessProgress.IsIndeterminate = true;
ProcessProgress.Visibility = Visibility.Visible;
}
private void ReportEnd()
{
ProcessProgress.IsIndeterminate = false;
ProcessProgress.Value = ProcessProgress.Maximum;
}
private void ReportProgress(FileProgress progress)
{
ProcessProgress.Value =progress.Progress;
PanelStatus.Text = $"Working on {progress.FileName} : {progress.Message}";
}
The event handler can now look like this :
private async void Start_Click(object sender, RoutedEventArgs e)
{
ReportStart();
IProgress<FileProgress> progress=new Progress<FileProgress>(ReportProgress);
...
await Task.Run(()=>
{
//process starts here
var fP = new FileProcessor();
foreach(var file in someFiles)
{
progress.Report(new FileProgress(file,0,"Starting");
//Do some processing
progress.Report(new FileProgress(file,100,"Finished");
}
...
});
//We are back in the UI thread, we can modify the UI
ReportEnd();
}