none
WPF VB6 Background Worker Consulta ProgressBar RRS feed

  • Pregunta

  • Hola Muy buenas noches Disculpen pero desafortunadamente no puede hacer que se actualice mi progress bar. 
    Quiero que una vez que comience el proceso se llene la barrita de acuerdo al tiempo que le lleve realizar la accion.
    Mi codigo es el siguiente

    Backgroud Worker

    Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
            For i As Integer = 0 To 100 Step +1
                worker.ReportProgress(i)
            Next
        End Sub
    Private Sub worker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
            ProgressBar1.Value = e.ProgressPercentage
        End Sub
    
        Private Sub worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
            Me.Cursor = System.Windows.Input.Cursors.Arrow
            If e.[Error] IsNot Nothing Then
                MessageBox.Show(e.[Error].Message)
            End If
            'button.IsEnabled = True
        End Sub
     Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
            emp.llenarCmbEmpresas(cmbEmpresa)
            cmbTipo.Items.Add("Recibidos")
            cmbTipo.Items.Add("Emitidos")
    
            worker.WorkerReportsProgress = True
            'AddHandler worker.DoWork, New DoWorkEventHandler(AddressOf worker_DoWork)
            AddHandler worker.RunWorkerCompleted, New RunWorkerCompletedEventHandler(AddressOf worker_RunWorkerCompleted)
            AddHandler worker.ProgressChanged, AddressOf worker_ProgressChanged
    
        End Sub
     

    Aqui doy clic al boton y quiero que comience a trabajar y actualizar mi progress bar

    Private Sub btnValidar_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnValidar.Click If Internet.IsConnectedToInternet() Then Me.Cursor = System.Windows.Input.Cursors.Wait worker.RunWorkerAsync() Valida() End If End Sub

    Método Valida

     Public Sub Valida()
            dt = CType(dgValidacion.ItemsSource, DataView).ToTable
            FE.ValidacionCFDI(dt)
            Dim P As String = cmbPeriodo.SelectedItem.ToString.Replace("/", "")
            Dim TipoInicial As String = cmbTipo.SelectedItem.ToString.ToCharArray
            Dim Tabla As DataTable
            If txtBuscar.Text.Length > 0 Then
                Tabla = periodo.dataTableValidacionLike(P.Replace("  ", ""), TipoInicial(0), txtBuscar.Text)
                dgValidacion.ItemsSource = Tabla.DefaultView
                dt = CType(dgValidacion.ItemsSource, DataView).ToTable
                GenerarXLS(dt)
            Else
                Tabla = periodo.dataTableValidacion(P.Replace("  ", ""), TipoInicial(0))
                dgValidacion.ItemsSource = Tabla.DefaultView
                dt = CType(dgValidacion.ItemsSource, DataView).ToTable
                GenerarXLS(dt)
            End If
        End Sub

    Una vez terminado ese metodo, quiero que mi progress bar se llene. pero aun no lo logro resolver.

    Ayuda!!

    lunes, 30 de mayo de 2016 23:16

Respuestas

  • Bueno, lo que has escrito es un backgroundworker que tiene un apretadísimo bucle For que a toda velocidad realiza 100 iteraciones y marca en un progressbar el avance de dichas operaciones. Con lo que el ProgressBar avanza a toda pastilla, y en una fracción de segundo que apenas será perceptible alcanza el 100% (y solo se hará visible en pantalla cuando el hilo principal no esté ocupado haciendo otra cosa).

    Y después lanzas la operación "Validar()", que tardará lo que tarde pero no está conectada de ninguna manera con el backgroundworker que pinta el ProgressBar.

    Si quieres que funcione, el método Validar es el que tendría que estar metido dentro del DoWork (en lugar del bucle For), y las llamadas a ReportProgress tendrían que estar dentro de Validar, que es el único que sabe el progreso que lleva. Por ejemplo, si el método GenerarXLS usa un bucle que va recorriendo los registros de dt, dentro de ese bucle habría que ir llamando a ReportProgress cada N iteraciones, donde N lo calcularías adecuadamente en función del número total de registros de dt para al final el número de llamadas resulte en 100 pasos, y si además de esa operación también es lenta alguna otra de las que tienes dentro de Validar, pues igualmente tendrías que adjudicarle algunos de los ReportProgress.

    martes, 31 de mayo de 2016 6:22
  • Puedes pasar el Backgroundworker como argumento a la función:

    Public Sub ValidacionCFDI(ByVal TablaValidar As DataTable, ByVal bgw as BackgroundWorker)
        '...
        bgw.ReportProgress(...)
        '....
    End Sub

    Evidentemente, al llamar a la función le pasarías el worker desde el formulario.

    O, si te atreves con ella, hay una solución todavía más elegante: En la clase que contiene la subrutina, declara un evento de progreso, y dentro de la rutina dispara ese evento según vaya iterando. Después, en la clase que contiene el BackgroudWorker, suscríbete a ese evento y usa el manejador para que a su vez llame al ReportProgress del BackgroundWorker.


    martes, 31 de mayo de 2016 15:21
  • Ese mensaje de error ocurre cuando desde el hilo de background se intenta manipular algún objeto que está en la pantalla. Estos objetos solo se pueden manipular desde el hilo principal.

    Para eso está el evento RunWorkerCompleted del BackgroundWorker. La idea es que el DoWork hace todos los cálculos y los deja en variables en memoria. Y cuando termina, se dispara el RunWorkerCompleted, y este evento sí que rueda en el hilo principal en lugar del hilo de background. De esa manera, en el RunWorkerCompleted se toman los datos que antes quedaron guardados en variables, y se pintan en la pantalla.

    No sé en qué línea exactamente te sale el error, pero te lo dirá el Debugger de Visual Studio. Esa es la línea que tienes que trasladar al RunWorkerCompleted (obviamente después de revisarla y ver qué variables tienes que hacerle accesibles para que esa línea pueda funcionar).

    martes, 31 de mayo de 2016 17:50

Todas las respuestas

  • Bueno, lo que has escrito es un backgroundworker que tiene un apretadísimo bucle For que a toda velocidad realiza 100 iteraciones y marca en un progressbar el avance de dichas operaciones. Con lo que el ProgressBar avanza a toda pastilla, y en una fracción de segundo que apenas será perceptible alcanza el 100% (y solo se hará visible en pantalla cuando el hilo principal no esté ocupado haciendo otra cosa).

    Y después lanzas la operación "Validar()", que tardará lo que tarde pero no está conectada de ninguna manera con el backgroundworker que pinta el ProgressBar.

    Si quieres que funcione, el método Validar es el que tendría que estar metido dentro del DoWork (en lugar del bucle For), y las llamadas a ReportProgress tendrían que estar dentro de Validar, que es el único que sabe el progreso que lleva. Por ejemplo, si el método GenerarXLS usa un bucle que va recorriendo los registros de dt, dentro de ese bucle habría que ir llamando a ReportProgress cada N iteraciones, donde N lo calcularías adecuadamente en función del número total de registros de dt para al final el número de llamadas resulte en 100 pasos, y si además de esa operación también es lenta alguna otra de las que tienes dentro de Validar, pues igualmente tendrías que adjudicarle algunos de los ReportProgress.

    martes, 31 de mayo de 2016 6:22
  • Me suena muy bien tu explicación, voy comprendiendo mas el background worker sin embargo  tengo cuando entro al metodo FE.ValidacionCFDI se va hacia otra Clase 

    Public Sub ValidacionCFDI(ByVal TablaValidar As DataTable) Dim ConsultaCFDI As New ConsultaCFDIServiceClient Dim Acuse As New Acuse ConsultaCFDI.Open() For Each Row As DataRow In TablaValidar.Rows Acuse = ConsultaCFDI.Consulta("?re=" & Row("rfcEmisor") & "&rr=" + Row("rfcReceptor") + "&tt=" + Row("total") + "&id=" + Row("UUID") + "") If Acuse.Estado <> Row("Estatus") Then bd.Query = "update sat set estatus='" + Acuse.Estado + "' where UUID='" + Row("UUID") + "'" bd.ConsultaDataReader() End If Next

    End Sub

    Y aqui no se como invocar el report progress porque ya no esta en mi formulario donde esta declarado.

    Tengo que hacerlo funcion y que me regrese backgroundworker.ReportProgress ???



    martes, 31 de mayo de 2016 14:37
  • Puedes pasar el Backgroundworker como argumento a la función:

    Public Sub ValidacionCFDI(ByVal TablaValidar As DataTable, ByVal bgw as BackgroundWorker)
        '...
        bgw.ReportProgress(...)
        '....
    End Sub

    Evidentemente, al llamar a la función le pasarías el worker desde el formulario.

    O, si te atreves con ella, hay una solución todavía más elegante: En la clase que contiene la subrutina, declara un evento de progreso, y dentro de la rutina dispara ese evento según vaya iterando. Después, en la clase que contiene el BackgroudWorker, suscríbete a ese evento y usa el manejador para que a su vez llame al ReportProgress del BackgroundWorker.


    martes, 31 de mayo de 2016 15:21
  • Ya quedo! Muchisimas Gracias, ya puedo ir viendo el progreso de mi barra, se va actualizando poco a poco, agrege una linea mas, que era para reportar el progreso y quedo asi: 

    Public Sub ValidacionCFDI(ByVal TablaValidar As DataTable, ByVal bw As BackgroundWorker)
    
            bw.WorkerReportsProgress = True
            Dim ConsultaCFDI As New ConsultaCFDIServiceClient
            Dim Acuse As New Acuse
            ConsultaCFDI.Open()
            Dim totRows = TablaValidar.Rows.Count
            Dim iteracion As Integer = 0
            For Each Row As DataRow In TablaValidar.Rows
                Acuse = ConsultaCFDI.Consulta("?re=" & Row("rfcEmisor") & "&rr=" + Row("rfcReceptor") + "&tt=" + Row("total") + "&id=" + Row("UUID") + "")
    
                If Acuse.Estado <> Row("Estatus") Then
                    bd.Query = "update sat set estatus='" + Acuse.Estado + "' where UUID='" + Row("UUID") + "'"
                    bd.ConsultaDataReader()
                End If
                iteracion = iteracion + 1
                bw.ReportProgress((100 * iteracion) / totRows)
            Next
    
        End Sub

    Sin Embargo al finalizar este, me tira el siguiente error

    El código de usuario no controló System.InvalidOperationException
      HResult=-2146233079
      Message=El subproceso que realiza la llamada no puede obtener acceso a este objeto porque el propietario es otro subproceso.
      Source=WindowsBase
      StackTrace:
           en System.Windows.Threading.Dispatcher.VerifyAccess()
           en System.Windows.DependencyObject.GetValue(DependencyProperty dp)
           en System.Windows.Controls.Primitives.Selector.get_SelectedItem()
           en sisTools.SatValidacion.Valida() en C:\Users\Cystac\Documents\Visual Studio 2010\Projects\sisToolsV2\sisToolsV2\Formularios\SAT\SatValidacion.xaml.vb:línea 167
           en sisTools.SatValidacion.worker_DoWork(Object sender, DoWorkEventArgs e) en C:\Users\Cystac\Documents\Visual Studio 2010\Projects\sisToolsV2\sisToolsV2\Formularios\SAT\SatValidacion.xaml.vb:línea 49
           en System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
           en System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
      InnerException: 

    Public Sub Valida()
            dt = CType(dgValidacion.ItemsSource, DataView).ToTable
            FE.ValidacionCFDI(dt, worker)
            'Despues de que realiza la validacion me tira el error al hacer el replace'
            Dim P As String = cmbPeriodo.SelectedItem.ToString.Replace("/", "")
            Dim TipoInicial As String = cmbTipo.SelectedItem.ToString.ToCharArray
            Dim Tabla As DataTable
            If txtBuscar.Text.Length > 0 Then
                Tabla = periodo.dataTableValidacionLike(P.Replace("  ", ""), TipoInicial(0), txtBuscar.Text)
                dgValidacion.ItemsSource = Tabla.DefaultView
                dt = CType(dgValidacion.ItemsSource, DataView).ToTable
                GenerarXLS(dt)
            Else
                Tabla = periodo.dataTableValidacion(P.Replace("  ", ""), TipoInicial(0))
                dgValidacion.ItemsSource = Tabla.DefaultView
                dt = CType(dgValidacion.ItemsSource, DataView).ToTable
                GenerarXLS(dt)
            End If
        End Sub


    martes, 31 de mayo de 2016 17:05
  • Ese mensaje de error ocurre cuando desde el hilo de background se intenta manipular algún objeto que está en la pantalla. Estos objetos solo se pueden manipular desde el hilo principal.

    Para eso está el evento RunWorkerCompleted del BackgroundWorker. La idea es que el DoWork hace todos los cálculos y los deja en variables en memoria. Y cuando termina, se dispara el RunWorkerCompleted, y este evento sí que rueda en el hilo principal en lugar del hilo de background. De esa manera, en el RunWorkerCompleted se toman los datos que antes quedaron guardados en variables, y se pintan en la pantalla.

    No sé en qué línea exactamente te sale el error, pero te lo dirá el Debugger de Visual Studio. Esa es la línea que tienes que trasladar al RunWorkerCompleted (obviamente después de revisarla y ver qué variables tienes que hacerle accesibles para que esa línea pueda funcionar).

    martes, 31 de mayo de 2016 17:50
  • Excelente Muchisimas Gracias, Ya esta funcionando sin errores
    martes, 31 de mayo de 2016 17:53