Does a progress indicator appear only after completion of work?

I am trying to create an indicator of progress on a form, but for some reason the form is not actually visible until the process is completed and it is closed when the process is completed (or, in other words, the form is open only for a moment). How can I make the form appear at the beginning of the process?

Note: my code may not be 100% correct, I'm just trying to make it different than mine, for privacy reasons.

public void SpawnPizzaProgressBarForm(object sender, EventArgs e) { FormPizzaProgressBar Form = new FormPizzaProgressBar(); Form.ShowDialog(); } ... public void ProgressBarForm_Load(object sender, EventArgs e) { Pizza = new Pizza(); Pizza.Eat(PizzaEatingProgressBar); this.Close(); } ... public void Eat(ProgressBar PizzaEatingProgressBar) { foreach(var Slice in Pizza) { Slice.Clear(); // PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100 } } 
0
source share
5 answers

This is because you are doing all the processing in the Load event for the form . This is called before the form is shown for the first time.

In the event handler, you actually forbid the display of the form, since the event handler must complete before anything else is processed.

What you would like to do is use BackgroundWorker to do your work. To do this, you need to follow these steps:

You have some problems with having your Pizza class closely related to the progress bar. This is not a good idea. Rather, you should have an event that will fire to indicate that the progress has been changed, and then raise the ProgressChanged event from the event handler for your Pizza instance.

I moved the code for your Eat method and encapsulated it in a form to show you an example of using the BackgroundWorker class, but the ideal solution would be to expose the event to indicate when the number of pizza consumed changes.

Also note that you must override the protected Dispose method in the form class in order to properly get rid of the BackgroundWorker instance when the form is deleted.

Here is an example:

 public void SpawnPizzaProgressBarForm(object sender, EventArgs e) { FormPizzaProgressBar Form = new FormPizzaProgressBar(); Form.ShowDialog(); } ... BackgroundWorker worker = new BackgroundWorker(); public void ProgressBarForm_Load(object sender, EventArgs e) { // Initialize the background worker. worker = new BackgroundWorker(); // Indicate that the worker supports progress. worker.WorkerSupportsProgress = true; // Subscribe to the DoWork event. worker.DoWork += (s, e) => { // Create the pizza instance. Pizza = new Pizza(); // Process the slices. foreach (var Slice in Pizza) { // Clear the slice. Slice.Clear(); // Report the progress. worker.ReportProgress(Slice.Index / Pizza.Count() * 100); } }; // Subscribe to the ProgressChanged event. worker.ProgressChanged = (s, e) => { // Update the progress bar. PizzaEatingProgressBar.Value = e.ProgressPercentage; }; // Subscribe to the RunWorkerCompleted event. worker.RunWorkerCompleted = (s, e) => { // Close the dislog. this.Close(); }; } // Must override to properly dispose of the background worker. protected override void Dispose(bool disposing) { // Call the base. base.Disposing(disposing); // Dispose of the background worker if disposing is true. if (disposing) worker.Dispose(); } 
+6
source

Winforms is based on the Windows API, which does not update the GUI elements if the thread on which the GUI elements were created is "pumped". WinForms called your ProgressForm_Load method on a thread on which it should "pump" messages to update GUI elements. You do not pump the message queue while you work in Eat . So, if the GUI elements are not updated, you will not see the change in the progress bar.

the easiest solution is to call Application.DoEvents periodically in your Eat method. The best answer is a lengthy operation in another thread, displaying an hourglass during this operation, and periodically informing the progress bar to update using the Invoke GUI refresh update style (you cannot call most methods of GUI elements directly from a thread other than the thread on which they were created).

+3
source
  PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100 

This does not work if Slice.Index as a whole. To get floating point separation, you need to make it double. For instance:

  PizzaEatingProgressBar.Value = (int)((double)Slice.Index / Pizza.Count) * 100); 

or follow these steps:

  PizzaEatingProgressBar.Value = (Slice.Index * 100) / Pizza.Count; 

This ensures that the division cannot be truncated to 0, as your original expression did. Contrary to all the advice, ProgressBar draws itself when you change the Value property. Although your window remains catatonic.

+2
source

It looks like a tight loop - you need to either put the work into the background thread using BackgroundWorker , or cheat and give the UI time to update by calling Application.DoEvents() (it processes the message queue, which includes the paint of the commands).

If the loop is too dense or too heavy, the user interface will block until it finishes, even if the user interface changes are mixed.

+1
source

You need to use threads to update the user interface while BackgroundWorker processes your actual process. Read this article (from the revered John Skeet) before you begin.

0
source

All Articles