VBA Excel: UserForm mal fonctionnel après 6 secondes.

Solved
Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   -  
 EGM -
Hello,

I am using a non-modal form (fStatus) with a label (lblStatus) to display the progress of the macro exploration of a large Excel workbook (170 sheets of about 50 rows and 30 columns).

About 6 seconds after launching the macro, the form stops refreshing (on the screen) while the macro continues normally. At that point, the hourglass cursor is replaced by the normal cursor, and sometimes the form disappears and its location turns completely white.

I added a BlockInput to prevent any user interaction during the execution of the macro (before, right-clicking caused the same phenomenon).

Here is the code (very simplified):

Option Explicit Option Private Module Declare Function BlockInput Lib "user32" (ByVal fBlockIt As Long) As Long Public Sub Test() Dim frmStatus As fStatus Dim wsh As Excel.Worksheet Dim wbkCible As Excel.Workbook Dim cell As Excel.Range Dim ctr As Long Application.Calculation = xlCalculationManual Application.Cursor = xlWait Application.ScreenUpdating = False Set wbkCible = Application.ActiveWorkbook Set frmStatus = New fStatus frmStatus.Show BlockInput 1 For Each wsh In wbkCible.Worksheets wsh.Select frmStatus.lblStatus.Caption = wsh.Name frmStatus.Repaint For Each cell In wsh.UsedRange cell.Select ` For ctr = 1 To 2000: Next Next cell Next wsh Application.ScreenUpdating = True Application.Cursor = xlDefault Application.Calculation = xlCalculationAutomatic frmStatus.Hide Unload frmStatus Set frmStatus = Nothing BlockInput 0 End Sub


Different values for the loop
` For ctr = 1 To 2000: Next

allowed me to verify that the problem occurs after a delay of about 6 seconds.

Does anyone have an idea of what is happening?

Configuration: XP / Mozilla / Office 2003

--
Nicolas always says: "It's easy when you know the answer!"

10 answers

  1. Mytå Posted messages 4246 Registration date   Status Contributor Last intervention   957
     
    Hello forum

    There might be a solution with DoEvents.

    DoEvents hands over control to the operating system until it has finished processing the events in its queue


    Mytå
    --
    Installed versions [MsProject 2003(FR), Excel 2003-2007(FR)]
    2
  2. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Thank you all,

    Mytå's solution works well, I placed the DoEvents in the loop just before modifying the control:

    For Each wsh In wbkCible.Worksheets DoEvents frmStatus.lblStatus.Caption = wsh.Name frmStatus.Repaint .... 

    The macro returns to its normal speed.

    I mark the post as resolved

    Patrice

    --
    Nicolas always says: “It’s easy when you know the answer!”
    2
    1. Mytå Posted messages 4246 Registration date   Status Contributor Last intervention   957
       
      Hello the forum

      Looking forward to helping you again in a future discussion.

      Mytå
      0
    2. EGM
       
      Thank you very much, my display issue has been resolved thanks to DoEvents (I had the hourglass, but the userform was not updated).
      0
  3. gbinforme Posted messages 14930 Registration date   Status Contributor Last intervention   4 744
     
    Hello

    This seems to be a well-known phenomenon for display in a long loop.

    When you modify your title (lblStatus), try adding:
    lblStatus.Repaint

    This forces the display, which should work better.
    --

    Always Zen
    0
  4. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Hello gbinforme

    The refresh of the form display is already present in the code: frmStatus.Repaint

    When I try to apply it to the label (frmStatus.lblStatus.Repaint), I get a compilation error "Method or data member not found" since the method does not apply to labels.

    --
    Nicolas always says: "It's easy when you know the answer!"
    0
  5. gbinforme Posted messages 14930 Registration date   Status Contributor Last intervention   4 744
     
    Hello

    Use a textbox to display instead, as a title is not meant for that.

    It is indeed on the userform that the function should be applied, and if you don't want it to slow down the process too much, you shouldn't modify the content until after several seconds (20-30) because displaying takes much longer compared to the calculations.
    --

    Stay calm
    0
  6. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Hello gbinforme, thank you for your response.

    I tried replacing the Label with a TextBox but it doesn't change anything!

    However, I explored the option of adding multiple .Repaint in the loop even though the UserForm's content doesn't change.

    By placing a .Repaint in the Cell loop, it works but, of course, the macro's duration becomes excessively long!

    As a compromise, I tried using the Timer function to refresh every second, but it doesn’t work, the frequency is not sufficient.

    I placed a counter in the Cell loop and conducted several tests with an increasing refresh frequency: it's not satisfactory.

    Curiously, I found an acceptable compromise by performing a .Repaint at the beginning of each line (while with the previous method I needed several per line!):

    For Each cell In wsh.UsedRange
    If cell.Column = 1 Then frmStatus.Repaint
    ...

    Next

    This doesn’t solve the problem but simply prevents it from occurring.

    I had already observed a similar phenomenon with Application.StatusBar
    when exploring large files: after a while, the display freezes, it is no longer updated with each change while the macro continues to run.

    I think an external process outside of Excel takes control and doesn’t return it correctly.

    There is probably a solution using the Windows API, but I haven’t found anything on this topic.

    Patrice
    0
    1. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
       
      I'm going back, meanwhile I tried to add:

      Declare Function UpdateWindow Lib "user32" (ByVal hwnd As Long) As Long

      and in the loop where frmStatus.Repaint is located:

      UpdateWindow Application.hwnd

      But it doesn't change anything.
      0
  7. Le Pingou Posted messages 12274 Registration date   Status Contributor Last intervention   1 476
     
    Hello,
    Just passing by, I used the following code to display a patience message (UserForm name) in a lengthy procedure for sending emails and it works well.
    The code:

    Application.Cursor = xlWait ' displays the wait cursor Patience.Show vbModeless ' displays the UserForm but continues processing Patience.Repaint ' refreshes the displayed content otherwise we get a blank box ` **** ` Here is my processing procedure [ClientAdresseCourriel clofpro, clsage, nbli, clbcmde, connecte] `**** Workbooks(clsage).Close ` closes the address workbook to be processed Unload Patience ' close the UserForm Application.Cursor = xlDefault ' reset the cursor to default


    --
    Regards.
    The Penguin
    0
    1. gbinforme Posted messages 14930 Registration date   Status Contributor Last intervention   4 744
       
      Hello Le Pingou,

      I suppose that in your procedure you modify the content of "patience" and that you see the result?

      I have created roughly the same process to generate an HTML web application that has many pages (which I list as I go) from a database and it works as expected.

      Also, I am surprised that Patrice is encountering this issue with the repaint that is on strike.
      0
    2. Le Pingou Posted messages 12274 Registration date   Status Contributor Last intervention   1 476
       
      Good evening gbinforme,
      Yes, that’s right.
      My intervention was just to showcase my solution, which, upon closer inspection, is the same, except for the use of the constant [vbModeless].
      Have a nice Sunday.
      0
  8. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Hello Le Pingou and gbinforme,

    Thank you for taking an interest in my problem.

    I declared my UserForm as non-modal using the VBE by setting ShowModal directly to False, but I noticed that ShowModal does not appear in the properties of the UserForm (that’s the only one missing), so doubt crept in, and I followed Le Pingou's advice:

    I tried frmStatus.Show vbModeless to be more sure, but the problem persists.

    A few clarifications:
    - In the loop causing the issue, the refresh rate is slightly over one second (it hangs on the 6th or 7th sheet).
    - I use this same UserForm in other procedures in the module and it works very well, but the refresh rate is higher (0.5 to 0.7 seconds).
    - In the loop, when I add a Repaint for each explored line (without changing the userform), the userform works correctly, but the execution time...
    - I am convinced that the size of the file being explored affects the application's display behavior, but that’s just a belief.
    - The explored file is not really huge: 9 MB containing mainly data, formulas, and a few Excel charts (less than fifteen), no external objects, no images, thousands of hyperlinks but all targeted at internal cells in the file.

    Best regards
    Patrice

    --
    Nicolas always says: "It's easy when you know the answer!"
    0
    1. gbinforme Posted messages 14930 Registration date   Status Contributor Last intervention   4 744
       
      Hello everyone,

      In my opinion, the duration between two repaints doesn't matter much, but it seems to me that it is only useful to use it when the value displayed changes.

      It seems to me that the "useful" duration should be rather greater than 10-15 seconds because below that, it slows down the macro and the user's reaction is not on the order of a second: only Usain Bolt can cover more than 10 m in that time!

      The file size is not the only cause because it is especially important depending on the available memory: if there is a shortage and swapping is required, the time increases.

      Have you disabled screen refresh?
      Try to set a repaint every 10 lines to see.
      0
  9. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Hello gbinforme,

    I tried to refresh every 10, 5, 2 lines but it's not enough, it only works with 1 line (or less), plus the Repaints happen with each line even though the value doesn't change (it only changes for each sheet).

    At the beginning of the code I have a

     Application.ScreenUpdating = False

    The amount of memory is more than sufficient to avoid swapping.

    --
    Nicolas always says: "It's easy when you know the answer!"
    0
    1. gbinforme Posted messages 14930 Registration date   Status Contributor Last intervention   4 744
       
      Hello again,

      I don’t see the point of repainting if you don’t change the value, unless it slows down the macro.

      For the user to see that it’s working, the displayed values need to change; otherwise, nothing happens: could your problem stem from there?
      0
  10. Patrice33740 Posted messages 8400 Registration date   Status Member Last intervention   1 783
     
    Hello again

    If I don't put this Repaint, without any theoretical interest, the Userform goes on strike after 6 seconds!

    That's where my problem lies.

    Best regards
    Patrice

    --
    Nicolas always says: "It's easy when you know the answer!"
    0