Reputation: 1
so I am relatively new to the C#/WPF techstack. I created a WPF program but sometimes the main window takes a bit to load and I am trying to add a loading window with progress bar but the UI is not updating as progress goes its just doing it all at once at the end of the process.
I've tried using a background worker adding some of the tasks that need to be run to it and running it ASYNC but to report the progress I need to add the worker to the class doing the work and the window reporting it and its causing a circular issue where you need the worker to get the datahandler but you need the login window to get the worker and you need the datahandler to get the login window.
So I tried creating and showing the loading window in mainWindow's constructor, and updating the progressbar element from there using await Task.Run(()=> this.Dispatcher.Invoke((Action)(() => { LDNG.LoadingBar.Value = percent; }))); but it doesnt update it as program runs and just does everything at the end when the main window is basically completed
heres a snippet to give an idea of the current state of my code
DataHandler dataCtrl;
public MainWindow(string version, string username, string password)
{
displayProgress();
dataCtrl = new DataHandler(username, password);
updateProgress(10);
InitializeComponent();
updateProgress(30);
signatures.Add(dataCtrl.user);
updateProgress(40);
MainWindowContainer.Title = "SDS2 " + DesignData.SDS2.Database.Version.ProgramVersion.ToString() + " Transmittal Report Tool";
updateProgress(50);
initialBinding();
updateProgress(100);
}
async void displayProgress() {
this.LDNG = new ProgressBarWindow(ref worker);
await Task.Run(() => this.Dispatcher.Invoke((Action)(() => { LDNG.Show(); })));
}
async void updateProgress(int percent) {
//LDNG.LoadingBar.Value = percent
await Task.Run(()=> this.Dispatcher.Invoke((Action)(() => { LDNG.LoadingBar.Value = percent; })));
//MessageBox.Show("");
//if (percent == 100) {
// LDNG.Close();
//}
}
void initialBinding() {
RepoBtn.Content = dataCtrl.getDefaultRepo("initialbinding mainwindow");
JobBtn.Content = dataCtrl.getDefaultJob();
InputByCmbx.ItemsSource = signatures;
//updateProgress((int)(50*(1/21)+50));
InputCheckedByCmbx.ItemsSource = signatures;
//updateProgress((int)(50*(2 /21)+50));
... some more code here
}
if you've got a suggestion for a better way to go about adding this loading screen I'll take that too I just need to get ere done
Upvotes: 0
Views: 92
Reputation: 457227
For progress updates, use the Progress<T>
pattern, which removes all the Dispatcher.Invoke
mess.
More generally, you'll need to think about how your application is executing. The MainWindow
constructor shouldn't take a long time to create the window. UI elements need to be created immediately. They can start a long-running operation and update that with results, but they must be created immediately.
Also, you can/should use Task.Run
to do the background work. The way the code is currently using it doesn't make any sense: any time it does a progress update, it's jumping to a background thread and then jumping back to the UI thread.
This shows how the code can be structured; the details will vary based on what your methods do:
public MainWindow(string version, string username, string password)
{
InitializeComponent();
var progress = new Progress<int>(value => LDNG.LoadingBar.Value = value);
Initialize(progress);
}
private async void Initialize(IProgress<int> progress)
{
dataCtrl = new DataHandler(username, password);
progress?.Report(30);
await Task.Run(() => signatures.Add(dataCtrl.user));
progress?.Report(40);
...
}
Note that code going inside a Task.Run
cannot update the UI directly. You may need to modify your code so that it returns results instead of being sprinkled with UI access.
Upvotes: 1