Showing Progress while Embedding Containers

I have a button in a window that opens a new window and embeds a number of containers in a For loop (the progress of which is shown using a separate dialog window containing a progress bar).

Each container is actually an entire invoice. The purpose of this window is to show a number of invoices and to be able to scroll through them to review a selection of invoices (and to make corrections, if necessary).

This actually works quite well.

Code looks something like this:

//Count the number of invoices checked in the listbox
  For i = 0 to lb.ListCount-1
    If lb.CellTag(i,0) = 1 then
      NumOfInvoices = NumOfInvoices + 1
    End If
  Next
  
  // Init progressDialog
  progressDialog.init("Preparing INVOICE Forms", True, True)
  progressDialog.startProgress("Counting...", NumOfInvoices)
  
  For i = 0 to lb.ListCount-1
    progressDialog.updateProgress("", 1)
    
    If UserCancelled Then
      progressDialog.Close
      INVOICE_window(w).Close
      Return
      Exit
    End If
    
    If lb.CellTag(i,0) = 1 then    //the invoice is selected to be displayed
      
      Dim c as New INVOICE_Container
      
      INVOICE_Container(c).EmbedWithin( INVOICE_window(w), 0, vHeight)

      //Place the invoice number in the field in the INVOICE_Container
      INVOICE_Container(c).vInvNumber.Text = lb.Cell(i,4) 
      vHeight = vHeight + INVOICE_Container(c).Height - 800

      INVOICE_window(w).ccList.Append c   //keep track of the containers in the window in an array

     //Adjust the scroll bar settings for the window
      INVOICE_window(w).ScrollBar1.Maximum = INVOICE_window(w).ScrollBar1.Maximum + INVOICE_Container(c).Height 
    End if
    
  Next
  
  progressDialog.Close

When the invoice number is loaded in the invoice, it looks up the data and loads all the fields of the invoice.

This process does take some time to load (about 3 seconds per invoice), and does lock up the user interface while the process is running.

Now, I am finding that in El Capitan the progress bar is invisible (all other version of Mac OS X work fine).

I realize that the proper way to correct this is to move the work into a Thread and then use a Timer to update the progress while the window is being prepared. From all I have read, I don’t believe it is possible to load containers into a window from within a thread.

What would be the preferred way to address this issue?

You are correct. You can use the new http://developer.xojo.com/xojo-core-timer$CallLater to move the loading of containers to the main thread. Use 0 ms to add the container to the next event loop.

Not sure how this would help.

Also, what would be the work around using earlier versions of Xojo?

Is there a guide or manual that explains how to update a progress bar when you are trying to build a window with embedded containers? I am using Xojo 2013 r3.3 to stay compatible with Mac 10.6.8.

How many are you embedding that a progress bar is even necessary ?

Just for reference the entire inspector in the IDE is all embedded container controls and its built all at once (all tabs) and there’s no perceptible lag
Now this is only 50 or 60 at most but still …

[quote=273535:@Norman Palardy]How many are you embedding that a progress bar is even necessary ?

[/quote]
At most maybe 50, but the data in the invoice container is being pulled from a remote database and each invoice takes 2 or 3 seconds to build.

Also, there is a background graphic of the form, too.

This is only under El Cap?
Maybe its just the built in progress bar that is the trouble

It wont take a moment to test…Add a label showing the percentage… does that display properly?
If so, substitute an ever-widening canvas with a color background for the progress bar control and you are set

[quote=273536:@David Schlam]At most maybe 50, but the data in the invoice container is being pulled from a remote database and each invoice takes 2 or 3 seconds to build.

Also, there is a background graphic of the form, too.[/quote]
You should change the data pull to be async then, as this sounds like synchronous data is what would lock up the interface.

[quote=273543:@Jeff Tullin]This is only under El Cap?
Maybe its just the built in progress bar that is the trouble

It wont take a moment to test…Add a label showing the percentage… does that display properly?
If so, substitute an ever-widening canvas with a color background for the progress bar control and you are set[/quote]
On El Cap, the label does refresh and can count the progress percentage.
The progress bar does not refresh.

[quote=273543:@Jeff Tullin]This is only under El Cap?
Maybe its just the built in progress bar that is the trouble[/quote]

[quote=274209:@David Schlam]On El Cap, the label does refresh and can count the progress percentage.
The progress bar does not refresh.[/quote]

This is not a bug in ProgressBar. As long as an event is not over, the UI is not refreshed. So the progressBar does not update until the event is over, when it is at its maximum.

The VERY bad solution was in the past to use App.DoEvents. But that can lead to unpredictable bugs.

A much better solution would be to place the loop in the Action event of a timer.

Beginning of the code does not change

[code]//Count the number of invoices checked in the listbox
For i = 0 to lb.ListCount-1
If lb.CellTag(i,0) = 1 then
NumOfInvoices = NumOfInvoices + 1
End If
Next

// Init progressDialog
progressDialog.init(“Preparing INVOICE Forms”, True, True)
progressDialog.startProgress(“Counting…”, NumOfInvoices)

LoopTimer.Mode = LoopTimer.ModeMultiple[/code]

The rest of the code with minor modifications goes in the Action event of the timer dragged onto the window, mode Off, period 1 :

[code]
Static i as integer
// For i = 0 to lb.ListCount-1 // Previous code

If i < lb.listCount-1 then
progressDialog.updateProgress("", 1)

If UserCancelled Then
  progressDialog.Close
  INVOICE_window(w).Close
  Return
  Exit
End If

If lb.CellTag(i,0) = 1 then    //the invoice is selected to be displayed
  
  Dim c as New INVOICE_Container
  
  INVOICE_Container(c).EmbedWithin( INVOICE_window(w), 0, vHeight)

  //Place the invoice number in the field in the INVOICE_Container
  INVOICE_Container(c).vInvNumber.Text = lb.Cell(i,4) 
  vHeight = vHeight + INVOICE_Container(c).Height - 800

  INVOICE_window(w).ccList.Append c   //keep track of the containers in the window in an array

 //Adjust the scroll bar settings for the window
  INVOICE_window(w).ScrollBar1.Maximum = INVOICE_window(w).ScrollBar1.Maximum + INVOICE_Container(c).Height 

End if

// Next // previous code
i = i + 1
else

progressDialog.Close
me.Mode = Timer.ModeOff

end if[/code]

This way the code in the loop executes every time the Action event of the timer fires, but since it takes place in different events, the ProgressBar updates and moves.

I do not know why labels update, but I suspect it should not be taken for granted. A next version of the OS may just break that.

I have changed things up a bit.

The window that displays all the invoices starts hidden. The method similar to above loads the containers into the window.

I placed a thread in the container (which runs in the textchange event of the invoicenumber field) which loads all the data into multiple RecordSet properties of the container. When the thread is finished it sets a property “threadfinished” as True. (If I try to show progress as I place the containers in the window, the progress dialog finishes almost instantly, since I have moved all the processing to threads within each container).

I then placed a timer in the container with a period of 250 that checks the threadfinished property and, if true, loads all the data from the recordsets into the window.

I have found at least a 50-70% increase in speed moving the loading of the data from each container to a thread.

I then created a progress dialog window with both a label and a progress bar showing progress. The progress dialog has two properties, myProgress and myMaximum. The progress dialog also has a timer with a period of 300 with the following code:

  pbar1.Value = myProgress
  pbar1.Refresh
  
  mainLabel.Text = "Counting ..... " + Str(myProgress) +" out of " + Str(myMaximum)
  mainLabel.Refresh
  
  If myProgress >= pbar1.Maximum then
    self.Close
  End If

Just before threadfinished is set to true, myProgress is incremented in the progress dialog window.

The window that displays the containers also has a timer (period 500) that checks to see if myProgress >= myMaximum and, if so, shows the invoice window.

I have found that the progress label (mainLabel) and progress bar (pbar1) update only once (at the beginning) and then stops updating no matter how many containers are loading.

I have tried setting different periods for the timers with little change.

One other interesting thing happens also. I have a scroll bar on the window to allow to scroll through the containers. This scroll bar works great until I have about 30 or more containers loaded in the window. Then the whole window breaks and no scrolling is possible.