Excel VBA: Formulario de usuario funcionando mal después de 6 segundos

Resuelto
Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   -  
 EGM -
Hola,

Utilizo un formulario no modal (fStatus) con un título (lblStatus) para mostrar el progreso de la exploración por macros de un gran libro de Excel (170 hojas de aproximadamente 50 filas y 30 columnas).

Alrededor de 6 segundos después de iniciar la macro, el formulario deja de actualizarse (en pantalla) aunque la macro continúa normalmente. En ese momento, el cursor de reloj de arena es reemplazado por el cursor normal y, a veces, el formulario se borra y su ubicación queda en blanco.

He añadido un BlockInput para evitar cualquier interacción del usuario durante la ejecución de la macro (antes, un clic derecho provocaba el mismo fenómeno).

Aquí está el código (muy simplificado):

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

Diferentes valores de la bucle For
For ctr = 1 To 2000: Next

me han permitido verificar que el problema sigue ocurriendo tras aproximadamente 6 segundos.

¿Alguien tiene alguna idea de qué está pasando?

Configuración: XP / Mozilla / Office 2003

--
Nicolas dice siempre: «¡Es fácil cuando se conoce la respuesta!»

10 respuestas

  1. Mytå Mensajes publicados 4246 Fecha de registro   Estado Colaborador Última intervención   957
     
    Hola al foro

    Quizás haya una solución con DoEvents.

    DoEvents cede el control al sistema operativo hasta que este haya terminado de procesar los eventos de su cola


    Mytå
    --
    Versiones instaladas [MsProject 2003(FR), Excel 2003-2007(FR)]
    2
  2. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Gracias a todos,

    la solución de Mytå funciona bien, he colocado DoEvents en el bucle justo antes de modificar el control:

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

    La macro recupera su velocidad normal.

    Pongo el puesto como resuelto

    Patrice

    --
    Nicolas dice siempre: « ¡Es fácil cuando se conoce la respuesta! »
    2
    1. Mytå Mensajes publicados 4246 Fecha de registro   Estado Colaborador Última intervención   957
       
      Re el foro

      Con gusto ayudarte de nuevo en una próxima conversación.

      Mytå
      0
    2. EGM
       
      Muchas gracias, mi problema de visualización se resolvió gracias a DoEvents (tenía la espiral de espera, pero el userform no estaba actualizado).
      0
  3. gbinforme Mensajes publicados 14930 Fecha de registro   Estado Colaborador Última intervención   4 744
     
    hola

    Me parece un fenómeno bastante conocido para una visualización en un bucle largo.

    Cuando modifiques tu título (lblStatus), intenta añadir :
    lblStatus.Repaint

    Esto fuerza la visualización que debería funcionar mejor.
    --

    Siempre zen
    0
  4. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Hola gbinforme

    la actualización de la visualización del formulario ya está presente en el código: frmStatus.Repaint

    cuando intento aplicarlo al título (frmStatus.lblStatus.Repaint) obtengo un error de compilación « Miembro de método o datos no encontrado » ya que el método no se aplica a los UILabel (etiquetas).

    --
    Nicolas dice siempre: « ¡Es fácil cuando ya se sabe la respuesta! »
    0
  5. gbinforme Mensajes publicados 14930 Fecha de registro   Estado Colaborador Última intervención   4 744
     
    hola

    Utiliza mejor una textbox para mostrarlo porque efectivamente un título no está hecho para eso.

    Es efectivamente en el userform donde hay que aplicar la función y si no quieres que ralentice demasiado el proceso, no debes modificar el contenido hasta pasadas varias segundos (20-30) porque la visualización tarda mucho en comparación con los cálculos.
    --

    Siempre zen
    0
  6. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Hola gbinforme, gracias por tu respuesta.

    He intentado reemplazar el Label por un TextBox pero eso no cambia nada!

    Por otro lado he explorado la posibilidad de colocar varios .Repaint en el bucle aunque el contenido del UserForm no cambie.

    Colocando un .Repaint en el bucle Cell, funciona pero, por supuesto, ¡la duración de la macro se vuelve excesivamente larga!

    En compromiso, intenté usar la función Timer para hacer un refresco cada segundo pero eso no funciona, la frecuencia no es suficiente.

    He colocado un contador en el bucle Cell y he hecho varios ensayos con una frecuencia de refresco cada vez mayor: no es satisfactorio.

    Curiosamente, he encontrado un compromiso aceptable realizando un .Repaint al inicio de cada fila (mientras que con el método anterior hacía falta varios por fila) :

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

    Next

    Esto no resuelve el problema, sino que simplemente evita que ocurra.

    Ya había observado un fenómeno similar con Application.StatusBar
    al explorar archivos grandes: después de cierto tiempo, la visualización se congela, ya no se actualiza con cada cambio mientras que la macro continúa funcionando.

    Creo que un proceso ajeno a Excel toma el control y no lo restituye adecuadamente.

    Probablemente haya una solución usando la API de Windows, pero no he encontrado nada al respecto.

    Patrice
    0
    1. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
       
      Estoy devolviendo, mientras tanto intenté añadir:

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

      y en el bucle donde se encuentra el frmStatus.Repaint:

      UpdateWindow Application.hwnd

      Pero no cambia nada.
      0
  7. Le Pingou Mensajes publicados 12274 Fecha de registro   Estado Colaborador Última intervención   1 476
     
    Hola,
    Por cierto, he utilizado el siguiente código para mostrar un mensaje de paciencia (nombre del UserForm) en un procedimiento bastante largo para enviar correos y funciona bien.
    El código:

    Application.Cursor = xlWait 'muestra la hora de arena Patience.Show vbModeless 'muestra el UserForm pero continúa el procesamiento Patience.Repaint 'refresca el contenido mostrado; de lo contrario aparece una caja blanca vacía ` **** ` Aquí mi procedimiento de procesamiento [ClientAdresseCourriel clofpro, clsage, nbli, clbcmde, connecte] `**** Workbooks(clsage).Close ` cierra el libro de direcciones a procesar Unload Patience ' cerrar el UserForm Application.Cursor = xlDefault 'restablece el cursor por defecto


    --
    Saludos.
    Le Pingou
    0
    1. gbinforme Mensajes publicados 14930 Fecha de registro   Estado Colaborador Última intervención   4 744
       
      Bonjour Le Pingou,

      Je suppose que dans ta procédure tu modifies le contenu de "patience" et que tu vois le résultat ?

      J'ai créé à peu près le même procédé pour générer une application internet HTML qui comporte de nombreuses pages (que je liste au fur et à mesure) à partir d'une base de données et cela fonctionne comme prévu.

      Aussi je suis surpris que Patrice rencontre ce problème avec le repaint qui se met en grève.
      0
    2. Le Pingou Mensajes publicados 12274 Fecha de registro   Estado Colaborador Última intervención   1 476
       
      Buenas noches gbinforme,
      Sí, así es como debe ser.
      Mi intervención fue solo para mostrar mi solución, que al mirar detenidamente es la misma, excepto por el uso de la constante [vbModeless].
      Que tengas un buen domingo.
      0
  8. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Hola Le Pingou et gbinforme,

    Gracias por interesarse en mi problema.

    Declaré mi UserForm no modal en VBE poniendo directamente ShowModal en False, pero vi al usar el Explorador de objetos que ShowModal no aparece en las propiedades del UserForm (es la única que falta), por lo que se instauró la duda y seguí el consejo de Le Pingou:

    intenté frmStatus.Show vbModeless para estar más seguro, pero el problema persiste.

    Algunas precisiones:
    - en la bucle que crea el problema, la frecuencia de actualización es ligeramente superior a un segundo (paro a la 6ª o 7ª hoja).
    - uso este mismo UserForm en otros procedimientos del módulo y funciona muy bien, pero la frecuencia de actualización es más alta (0,5 a 0,7 segundos).
    - en la bucle, cuando añado un Repaint en cada línea explorada (sin cambio del userform) el userform funciona correctamente pero el tiempo de ejecución ...
    - estoy convencido de que el tamaño del archivo explorado influye en el comportamiento de la visualización de la aplicación, pero es solo una convicción.
    - el archivo explorado no es realmente enorme: 9 MB que contienen principalmente datos, fórmulas y algunos gráficos de Excel (menos de una quincena), sin objetos externos, sin imágenes, miles de hipervínculos pero todos destinados a celdas internas en el archivo.

    Cordialement
    Patrice

    -- Nicolas dice siempre: « ¡Es fácil cuando se sabe la respuesta! »
    0
    1. gbinforme Mensajes publicados 14930 Fecha de registro   Estado Colaborador Última intervención   4 744
       
      hola a todos,

      La duración entre dos repaint no tiene mucha importancia a mi parecer, pero me parece que solo es útil usarla cuando el valor a mostrar cambia.

      Me parece que la duración "útil" sería más bien superior a 10-15 segundos, porque por debajo de eso ralentiza la macro y la reacción del usuario no es del orden de un segundo: solo Usain Bolt recorre más de 10 m en ese tiempo.

      El tamaño del archivo no es el único factor, ya que importa sobre todo en función de la memoria disponible: si falla y hay que recurrir al swap, el tiempo se eleva.

      ¿Has inhibido el refresco de pantalla?
      Intenta hacer un repaint cada 10 líneas para ver.
      0
  9. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Hola gbinforme,

    he intentado refrescar cada 10, 5, 2 líneas pero no es suficiente, solo funciona con 1 línea (o menos), además los Repaint en cada línea se realizan aunque el valor no cambia (cambia solo en cada hoja).

    Al inicio del código tengo un

     Application.ScreenUpdating = False

    La cantidad de memoria es más que suficiente para evitar el swap.

    --
    Nicolas siempre dice: « ¡Es fácil cuando ya se la respuesta! »
    0
    1. gbinforme Mensajes publicados 14930 Fecha de registro   Estado Colaborador Última intervención   4 744
       
      hola de nuevo,

      No veo el interés del repaint si no cambias el valor, a menos que se ralentice la macro.

      Para que el usuario vea que funciona, hace falta que los valores mostrados cambien; si no, no pasa nada: ¿no podría ser ese tu problema?
      0
  10. Patrice33740 Mensajes publicados 8400 Fecha de registro   Estado Miembro Última intervención   1 783
     
    Buenas de nuevo

    Si no pongo este Repaint, sin interés teórico, el UserForm se queda sin ideas a los 6 segundos!

    Ahí es donde está mi problema.

    Atentamente
    Patrice

    --
    Nicolás siempre dice: «¡Es fácil cuando ya se conoce la respuesta!»
    0