none
Background worker al abrir un formulario desde otro RRS feed

  • Pregunta

  • Buenas tardes, estoy teniendo la siguiente complicación, tengo un formulario menú principal y otro formulario vehículo, estuve probando usar backgroundworker para las consultas que hago en cada form y me va bien, pero tengo problema al querer usarlo para abrir un formulario.

    El formulario vehículo carga 4 combobox en el load cuando lo abro, pero quiero usar el backgroundworker para que no me tranque todo el programa mientras consulta.

    En el formulario principal tengo una barra de tareas con un menu, y cuando pincho el menu de vehiculo me abre el form de vehiculo, pero al empezar a hacer las consultas mediante el backgroundworker me da el siguiente mensaje de error: Operación no valida atraves de subprocesos: se tuvo acceso al control "cbomarca" desde un subproceso distinto a aquel en el que se lo creó. Este error me lo muestra 4 veces, correspondiente a cada una de las query que hago a los 4 combobox.

    Dejo el código que tengo:

    NOTA:  cbo son los combobox y CargaInicio es el backgroundworker

    FORMULARIO VEHICULO:

     

    Sub cargar()

            Dim sql As String

            Dim da As New Odbc.OdbcDataAdapter

            Dim ds As New DataSet

            Dim dt As New DataTable

            Try

                sql = "SELECT * FROM vehiculo_marca"

                ConexionBD.conecta()

                With comando

                    .Connection = Conectar

                    .CommandText = sql

                    .ExecuteNonQuery()

                End With

                da.SelectCommand = comando

                dt = New DataTable

                da.Fill(dt)

                With cbomarca

                    .DataSource = dt

                    .DisplayMember = "marca"

                    .ValueMember = "id_vehimarc"

                End With

            Catch ex As Exception

                MsgBox(ex.Message, MsgBoxStyle.Critical)

            Finally

                Conectar.Close()

            End Try

            Try

                sql = "SELECT * FROM CATEGORIA"

                ConexionBD.conecta()

                With comando

                    .Connection = Conectar

                    .CommandText = sql

                    .ExecuteNonQuery()

                End With

                da.SelectCommand = comando

                dt = New DataTable

                da.Fill(dt)

                With cboidcategoria

                    .DataSource = dt

                    .DisplayMember = "cat_nombre"

                    .ValueMember = "id_categoria"

                End With

            Catch ex As Exception

                MsgBox(ex.Message, MsgBoxStyle.Critical)

            Finally

                Conectar.Close()

            End Try

            Try

                sql = "SELECT * FROM SUCURSAL"

                ConexionBD.conecta()

                With comando

                    .Connection = Conectar

                    .CommandText = sql

                    .ExecuteNonQuery()

                End With

                da.SelectCommand = comando

                dt = New DataTable

                da.Fill(dt)

                With cbosucursal

                    .DataSource = dt

                    .DisplayMember = "suc_nombre"

                    .ValueMember = "id_sucursal"

                End With

            Catch ex As Exception

                MsgBox(ex.Message, MsgBoxStyle.Critical)

            Finally

                Conectar.Close()

            End Try

            Try

                sql = "SELECT * FROM vehiculo_modelo"

                ConexionBD.conecta()

                With comando

                    .Connection = Conectar

                    .CommandText = sql

                    .ExecuteNonQuery()

                End With

                da.SelectCommand = comando

                dt = New DataTable

                da.Fill(dt)

                With cbomodelo

                    .DataSource = dt

                    .DisplayMember = "modelo"

                    .ValueMember = "id_vehimod"

                End With

            Catch ex As Exception

                MsgBox(ex.Message, MsgBoxStyle.Critical)

            Finally

                Conectar.Close()

            End Try

        End Sub

    Private Sub CargaInicio_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles CargaInicio.DoWork

            Call cargar()

            piccargando.Visible = True

        End Sub

        Private Sub CargaInicio_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles CargaInicio.RunWorkerCompleted

            piccargando.Visible = False

        End Sub

    FORMULARIO MENU PRINCIPAL:

        Private Sub nuevovehiculo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tlsinuevovehiculo.Click

            frmnuevovehiculo.MdiParent = Me

            frmnuevovehiculo.Show()

            frmnuevovehiculo.CargaInicio.RunWorkerAsync()

            frmnuevovehiculo.BringToFront()

        End Sub

    • Cambiado Enrique M. Montejo miércoles, 27 de septiembre de 2017 8:52 Pregunta relacionada con controles de Windows Forms.
    sábado, 23 de septiembre de 2017 21:19

Respuestas

  • El problema es que el DoWork del backgroundworker se ejecuta en un hilo distinto, pero los formularios Windows son mono-hilo; su contenido solo se puede modificar desde el hilo que los creó. Esto implica que desde el DoWork de un BackGround worker no se puede pintar nada en pantalla, por ejemplo, no puedes meter items en los combos ni hacer visible un picturebox. Para resolver esto, el backgroundworker tiene el evento RunWorkerCompleted, que se dispara en el hilo principal y sí que puede modificar la pantalla. Entonces lo que hay que hacer es que en el DoWork se ejecutan las Select y se dejan los resultados en variables (tales como por ejemplo datasets), y esos datasets NO se conectan a los combos. Y luego, en el RinWorkerCompleted, se toman los datasets y se conectan a los combos (y se hacen cualesquiera otras operaciones como cambiar la visibilidad de un picturebox).
    domingo, 24 de septiembre de 2017 15:39

Todas las respuestas

  • El problema es que el DoWork del backgroundworker se ejecuta en un hilo distinto, pero los formularios Windows son mono-hilo; su contenido solo se puede modificar desde el hilo que los creó. Esto implica que desde el DoWork de un BackGround worker no se puede pintar nada en pantalla, por ejemplo, no puedes meter items en los combos ni hacer visible un picturebox. Para resolver esto, el backgroundworker tiene el evento RunWorkerCompleted, que se dispara en el hilo principal y sí que puede modificar la pantalla. Entonces lo que hay que hacer es que en el DoWork se ejecutan las Select y se dejan los resultados en variables (tales como por ejemplo datasets), y esos datasets NO se conectan a los combos. Y luego, en el RinWorkerCompleted, se toman los datasets y se conectan a los combos (y se hacen cualesquiera otras operaciones como cambiar la visibilidad de un picturebox).
    domingo, 24 de septiembre de 2017 15:39
  • me podrias mostrar un ejemplo en el codigo? no me doy cuenta que es lo que deberia poner.

    Muchas gracias!

    domingo, 24 de septiembre de 2017 15:39
  • El problema es que el DoWork del backgroundworker se ejecuta en un hilo distinto, pero los formularios Windows son mono-hilo; su contenido solo se puede modificar desde el hilo que los creó. Esto implica que desde el DoWork de un BackGround worker no se puede pintar nada en pantalla, por ejemplo, no puedes meter items en los combos ni hacer visible un picturebox. Para resolver esto, el backgroundworker tiene el evento RunWorkerCompleted, que se dispara en el hilo principal y sí que puede modificar la pantalla. Entonces lo que hay que hacer es que en el DoWork se ejecutan las Select y se dejan los resultados en variables (tales como por ejemplo datasets), y esos datasets NO se conectan a los combos. Y luego, en el RinWorkerCompleted, se toman los datasets y se conectan a los combos (y se hacen cualesquiera otras operaciones como cambiar la visibilidad de un picturebox).

    Probé tu solución, pero no me estaría funcionando del todo bien me parece. 

    Logre hacerlo tal cual lo dijiste, pero en el backgroundcomplete cuando relleno los combos o lo que sea, se cuelga el hilo principal del programa, la consulta si me la hace en background, pero al llenar los combos u otro contenedor se cuelga unos segundos hasta rellenarlos

    domingo, 24 de septiembre de 2017 16:37
  • La idea es que normalmente lo que es lento es la ejecución de la consulta contra base de datos, mientras que la carga de esos datos en los combos solo debería durar unas milésimas de segundo. No tiene remedio el que el formulario se congele durante esas milésimas de segundo; esta carga necesariamente tiene que hacerse desde el hilo principal. Si en lugar de unas milésimas de segundo está tardando varios segundos, algo falla. O bien se está haciendo alguna operación superflua que no debería hacerse en ese momento, o bien se están cargando muchos miles de registros, en cuyo caso es un error utilizar un combo, que no está pensado para manejar grandes cantidades de datos.
    • Propuesto como respuesta Juan Mondragón martes, 26 de septiembre de 2017 21:31
    domingo, 24 de septiembre de 2017 17:25