Barra de progreso no termina

Iniciado por Lekim, 28 Marzo 2016, 22:24 PM

0 Miembros y 1 Visitante están viendo este tema.

Lekim

Hola

La duda que tengo es sobre el control ProgressBar y es que no termina la barra.

Este código, cuando el valor de la barra llega a 100 o al máximo de la misma debe cerrarse la aplicación. Sin embargo se cierra antes que la barra llegue a 100 o al máximo establecido. Usando exactamente un código equivalente en VB6, esto no pasa. ¿Por qué ocurre?

Código (vbnet) [Seleccionar]
Public Class Form1
   Private Time1 As New Timer()
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
       Call InitializeMyTimer()
   End Sub


   Private Sub InitializeMyTimer()
       Time1.Interval = 20
       AddHandler Time1.Tick, AddressOf IncreaseProgressBar
       Time1.Start()
   End Sub
   Private Sub IncreaseProgressBar(ByVal sender As Object, ByVal e As EventArgs)
       ProgressBar1.Increment(1)
       If ProgressBar1.Value = ProgressBar1.Maximum Then
           Time1.Stop()
           Close()
       End If
   End Sub

End Class


Gracias

MCKSys Argentina

No programo mucho en .NET, pero creo que no le estas dando tiempo de refrescar. Quizas, es por eso que parece que la barra no llega al final.

Prueba de mostrar un mensaje antes de cerrar el form (u otra cosa que permita refrescar la barra, antes de cerrar).

Saludos!
MCKSys Argentina

"Si piensas que algo está bien sólo porque todo el mundo lo cree, no estás pensando."


Lekim

Cita de: MCKSys Argentina en 28 Marzo 2016, 22:36 PM
No programo mucho en .NET, pero creo que no le estas dando tiempo de refrescar. Quizas, es por eso que parece que la barra no llega al final.

Prueba de mostrar un mensaje antes de cerrar el form (u otra cosa que permita refrescar la barra, antes de cerrar).

Saludos!

Lo de cerrar la aplicación lo he puesto como ejemplo, pero ocurre con cualquier evento. Si bajo la velocidad de progreso se nota más:

Código (vbnet) [Seleccionar]
Public Class Form1
    Private Time1 As New Timer()
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Call InitializeMyTimer()
    End Sub


    Private Sub InitializeMyTimer()
        Time1.Interval = 50
        AddHandler Time1.Tick, AddressOf IncreaseProgressBar
        Time1.Start()
    End Sub
    Private Sub IncreaseProgressBar(ByVal sender As Object, ByVal e As EventArgs)
        ProgressBar1.Increment(1)
        If ProgressBar1.Value = ProgressBar1.Maximum Then
            Time1.Stop()
            MessageBox.Show("hola")
        End If
    End Sub

End Class


MCKSys Argentina

Acabo de probar tu ejemplo y el mensaje se muestra cuando la barra llega al final...

La propiedad maximum es la por default (100).

Es raro...
MCKSys Argentina

"Si piensas que algo está bien sólo porque todo el mundo lo cree, no estás pensando."


Lekim

#4
Cita de: MCKSys Argentina en 28 Marzo 2016, 23:00 PM
Acabo de probar tu ejemplo y el mensaje se muestra cuando la barra llega al final...

La propiedad maximum es la por default (100).

Es raro...

Gracias por probarlo. Pues si que es raro. Ejecutaré la aplicación en otra compu, haber que pasa.




Ya lo he probado.

Lo he ejecutado en XP dentro de VirtualBox y funciona bien.

Luego he cambiado el tema de windows a Windows Clásico y entonces si que termina.

Sólo ocurre cuando se ve así:



Eleкtro

#5
Ten en cuenta, que una cosa son los campos (o lógica) del control, y luego, la UI. Ya te has dado cuenta por ti mismo de que la barra de progeso que llega a 100, puesto que el campo ProgressBar.Value así lo indica, pero si la UI se tuviera que actualizar/dibujar con la misma constancia, entonces la UI se volvería muy deficiente y propensa a constantes congelamientos, aparte, los efectos visuales de degradados y destellos y demás florituras, como el efecto de relleno 3D de una progressbar, requieren más trabajo/tiempo de ejecución.

Viendo el código fuente online, el método Increment o PerformStep lo único que hacen es llamar a la función Win32 SendMessage para enviar el mensaje que actualiza la posición, a la cola de mensajes del control, y este mensaje se procesará cuando se desocupe el resto de mensajes que están siendo procesados constantemente por el control, suele ser algo "instantaneo", pero no tiene por que serlo, igual que el redibujado del control para mostrar esos cambios, tampoco tiene que ser instantaneo, sino cuando sucede "algo" que llame al invocador de ese evento para dispararlo.

Código (csharp) [Seleccionar]
private void UpdatePos() {
   if (IsHandleCreated) SendMessage(NativeMethods.PBM_SETPOS, value, 0);
}



En resumen, que es lo más normal del mundo, aunque para entenderlo mejor habría que conocer el código fuente de la función SendMessage para ver como funciona, y sobre todo entender a fondo como funciona la cola de mensajes para ventanas Win32, cosa que yo no se a ciencia cierta, lo que si se es que si dejas el tiempo necesario para que se procese el mensaje y se pueda actualizar la UI (unos ms), entonces puedes comprobar como si que se completa la barra de progreso hasta el final:

Código (vbnet) [Seleccionar]
Public NotInheritable Class Form1 : Inherits Form

   Friend WithEvents Timer1 As New System.Windows.Forms.Timer With {.Interval = 20}

   Private Sub Form1_Shown(ByVal sender As Object, ByVal e As EventArgs) _
   Handles MyBase.Shown

       Me.ProgressBar1.Step = 1
       Me.Timer1.Start()

   End Sub

   Private Sub IncreaseProgressBar(ByVal sender As Object, ByVal e As EventArgs) _
   Handles Timer1.Tick

       Me.ProgressBar1.PerformStep()

       If (Me.ProgressBar1.Value = Me.ProgressBar1.Maximum) Then
           Me.Timer1.Stop()
           MsgBox("done") ' unos ms despues de saltar el msgbox verás como se completa la barra.
           MyBase.Close()
       End If

   End Sub

End Class


Saludos.








Lekim

Hola Elektro

Gracias por el respuesta. La cual al leerla, parce que Net lo has inventado tú ¿Como sabes esas cosas? XD

Sí comprobé que así és, por ejemplo usando sleep antes que se mostrara el mensaje, entoces la barra termina y se muestra el mensaje, pero hace un parón molesto y queda mal. También probé de usar Backgroundworker, pero ocurre exáctamente igual, que la barra termina después de mostrase el mensaje.

Necesita más tiempo para el llenado gráfico de la barra de progreso, que para los valores de la misma.

Ahora no puedo probar tu código porque estoy en XP, y aquí funcionaría bien incluso el que hice yo. Lo probaré mañana.

S2s

Lekim

Cita de: Eleкtro en 29 Marzo 2016, 19:52 PM
...
En resumen, que es lo más normal del mundo, aunque para entenderlo mejor habría que conocer el código fuente de la función SendMessage para ver como funciona, y sobre todo entender a fondo como funciona la cola de mensajes para ventanas Win32, cosa que yo no se a ciencia cierta, lo que si se es que si dejas el tiempo necesario para que se procese el mensaje y se pueda actualizar la UI (unos ms), entonces puedes comprobar como si que se completa la barra de progreso hasta el final:

Código (vbnet) [Seleccionar]
Public NotInheritable Class Form1 : Inherits Form

   Friend WithEvents Timer1 As New System.Windows.Forms.Timer With {.Interval = 20}

   Private Sub Form1_Shown(ByVal sender As Object, ByVal e As EventArgs) _
   Handles MyBase.Shown

       Me.ProgressBar1.Step = 1
       Me.Timer1.Start()

   End Sub

   Private Sub IncreaseProgressBar(ByVal sender As Object, ByVal e As EventArgs) _
   Handles Timer1.Tick

       Me.ProgressBar1.PerformStep()

       If (Me.ProgressBar1.Value = Me.ProgressBar1.Maximum) Then
           Me.Timer1.Stop()
           MsgBox("done") ' unos ms despues de saltar el msgbox verás como se completa la barra.
           MyBase.Close()
       End If

   End Sub

End Class


Saludos.

Ya he probado el código y ocurre igual, el mensaje se muestra antes que que termine la barra. Igual es cosa de mi compu o puede que del framework, porque no recuerdo que antes pasara.  Como solución me fabricaré mi propia barra (que ya tengo un snippet echo para eso) y listo, ya que he comprobado que funciona usando un estilo gráfico propio y el picturebox, en lugar del progressbar.

S2s


Eleкtro

#8
Cita de: Lekim en 30 Marzo 2016, 14:22 PMYa he probado el código y ocurre igual, el mensaje se muestra antes que que termine la barra. Igual es cosa de mi compu o puede que del framework, porque no recuerdo que antes pasara.  Como solución me fabricaré mi propia barra (que ya tengo un snippet echo para eso) y listo, ya que he comprobado que funciona usando un estilo gráfico propio y el picturebox, en lugar del progressbar.

Sí, con el código que te mostré ocurre lo mismo, lo que te dije es que despues de mostrar el msgbox (unos ms despues), la barra se completará, por que le estás dejando tiempo suficiente para que se redibuje el control.

Lo intentaré explicar de una forma más simple para que se entienda mejor:
Cuando le asignas un valor del 0 al 100 a la barra de progreso, de una forma rápida como en tu código, y ves que no se completa la barra al mismo tiempo, es sencillamente por que le estás enviando actualizaciones de valor más rápido del tiempo necesario que el control o ventana necesita para procesar ese mensaje y actualizar la animación entre cada valor.

Es muy posible que si administras por ti mismo el dibujado de la barra de progreso soluciones ese problema como ya has mencionado, puesto que al hacerlo (es decir, al activar el dibujado de usuario), el control se vuelve mucho más liviano, y por ende responsivo, al perder el estilo visual de Windows de la barra de progeso, es decir, la animación 3D en general, el efecto de relleno, el brillo, pseudo-translucidez parcial (no se describirlo bien), y el movimiento del efecto de "trayectoria" luminosa:


( simplemente una imagen para entender a lo que me refiero. )

Es un claro indicativo más para demostrar que el problema está en como Microsoft administra el dibujado de ese control y el tiempo de retraso que supone, puesto que es una animación, y necesita tiempo para completarse y actualizarse.




EDITO:
En esta respuesta comento algo relacionado sobre el tema, y dejo un snippet que tal vez te podría ser de utilidad:




TL/DR:
Cita de: Lekim en 29 Marzo 2016, 23:58 PMparce que Net lo has inventado tú ¿Como sabes esas cosas? XD

No lo se absolutamente todo sobre los internals de Windows en relación a la API Win32/Win64, o Windows Forms, ni mucho menos, pero lo que se, lo he aprendido de forma autodidacta, a base de lectura e investigación, tragándome documentación extensa en MSDN para descubrir, aprender, y comprender, leyendo artículos interesantes en otras fuentes sobre todo tipo de temáticas relacionadas en general, redactados por programadores veteranos con décadas de experiencia en .Net,
y por supuesto de la práctica, pasando por fases trial-and-error como todo el mundo, y cuando he tenido que recurrir a preguntarle a personas que saben muchísimo más que yo y me han ayudado en su momento, dignos de llamarse gurús o semi-dioses del conocimiento, como por ejemplo Hans Passant (de StackOverflow).

Saludos








Lekim

#9
Cita de: Eleкtro en 30 Marzo 2016, 19:35 PM
Sí, con el código que te mostré ocurre lo mismo, lo que te dije es que despues de mostrar el msgbox (unos ms despues), la barra se completará, por que le estás dejando tiempo suficiente para que se redibuje el control.

Lo intentaré explicar de una forma más simple para que se entienda mejor:
Cuando le asignas un valor del 0 al 100 a la barra de progreso, de una forma rápida como en tu código, y ves que no se completa la barra al mismo tiempo, es sencillamente por que le estás enviando actualizaciones de valor más rápido del tiempo necesario que el control o ventana necesita para procesar ese mensaje y actualizar la animación entre cada valor.


Con cualquier código el, mio o el tuyo, yo si veo que acaba la barra, pero el mensaje se muestra antes que acabe. Ya supongo que el proceso matemático por así decirlo va más rápido que el gráfico. El problema está cuando se trata de dar a la oportunidad al usuario de cancelar un cierre de sesión o de windows y gráficamente parece que aún da tiempo pero en realidad no es así, además que parece que se cierra antes de tiempo.

Y gracias por el snippet.

s2s




He probado el Snippet. Va bien, pero vamos que pasa lo mismo.

Este cierra la aplicación tras 5 segundos. Pero claro, visualmente parece que lo haga antes.

Código (vbnet) [Seleccionar]
Public Class Form1

    Private Sub UpdateProgress(ByVal value As Integer)
        Me.ProgressBar1.PerformStep()
        My.Application.DoEvents()
    End Sub

    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        Me.ProgressBar1.Minimum = 0
        Me.ProgressBar1.Maximum = 100
        Me.ProgressBar1.Step = 1
        Dim Segundos As Integer = 5

        For x As Integer = 0 To 100
            Threading.Thread.Sleep(Segundos * 10)
            UpdateProgress(x)
        Next
        If ProgressBar1.Value = ProgressBar1.Maximum Then
            Close()
        End If
    End Sub
End Class