none
Backgroudwork C# RRS feed

  • Pergunta

  • Ola estou com erro que não sei como fazer, tenho um LABEL q é atualizado por uma função porem sa erro : Operação entre threads invalida : controle 'lbMSG' acessado de um thread que nao é aquele no qual foi criado.

    Se alguém puder me ajudar.

     private void ConectaDBF(string TextoComando)
            {
                //lbMSG.Invoke(new MethodInvoker(() => { lbMSG.Text = "Comando iniciado...  " + DateTime.Now.ToString(); }));
                lbMSG.Text = "Comando iniciado...  " + DateTime.Now.ToString();
                lbMSG.ForeColor = Color.White;
                lbMSG.Refresh();
                //CaminhoArquivo = BuscaArquivo.FileName;
    
                try
                {
                    DateTime TempoInicio = DateTime.Now;
                    OleDbConnection oConn = new OleDbConnection();
                    oConn.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + CaminhoSource + ";Extended Properties=dBASE IV;";
                    oConn.Open();
                    OleDbCommand oCmd = oConn.CreateCommand();
                    oCmd.CommandText = TextoComando;
                    //DataTable dt = new DataTable();
                    dtComDBF = new DataTable();
                    dtComDBF.Load(oCmd.ExecuteReader());
                    oConn.Close();
    
                    /*------------------------------------------------------------*/
                    //if (dt.Columns.Count > 0)
                    //{
                    //    MessageBox.Show(dt.Columns.Count.ToString());
                    //    foreach (DataRow linha in dt.Rows)
                    //    {
                    //        GravaTxt("Datatable.txt", linha["B1_CODPRO"].ToString() + "    |    ");
                    //    }
                    //    MessageBox.Show("Concluido !");
                    //}
                    /*------------------------------------------------------------*/
    
                    double LinhasDt = Convert.ToDouble(dtComDBF.Rows.Count);
                    ProgressBar.Maximum = Convert.ToInt32(LinhasDt);
    
                    dgvDados.DataSource = dtComDBF;
    
                    for (int i = 0; i < dtComDBF.Rows.Count; i++)
                    {
                        lbPercent.Text = Math.Round(((i / LinhasDt) * 100)).ToString("F2") + "%";
                        ProgressBar.Value = i;
                        ProgressBar.Refresh();
                        lbPercent.Refresh();
                    }
    
                    lbPercent.Text = "100%";
                    ProgressBar.Value = ProgressBar.Maximum;
    
                    Tempo = Convert.ToString(DateTime.Now - TempoInicio);
                    lbMSG.ForeColor = Color.White;
                    registros = dgvDados.RowCount.ToString();
                    lbMSG.Text = "Tempo decorrido: " + Tempo + "\n" + registros + " : linhas afetadas";
                }
                catch (Exception Erro)
                {
                    lbMSG.ForeColor = Color.Orange;
                    lbMSG.Text = "Comando inválido !  " + DateTime.Now.ToString();
                    MessageBox.Show("Erro!  " + Erro.ToString());
                }
            }


    quinta-feira, 5 de dezembro de 2019 16:23

Todas as Respostas

  • Bom dia Cristiano!

    Este problema ocorre pois está tentando modificar um controle (label lbMSG) fora da Thread que criou este controle. Por questões de segurança, uma Thread não deveria modificar o estado de outra Thread.

    Para que você consiga atualizar este valor de fora da Thread "mãe" você terá que seguir algumas regrinhas para o compilador considerar que é uma operação "segura". Neste caso você poderá usar Delegates com Invokes ou BackgroundWorkers.

    Vou te passar um exemplo com Invokes/Delegates.

    Adicione este método à tua classe:

            // Delegate para acesso da Thread ao Control do Form
            // Exemplo de uso: SetControlPropertyValue(tela, "text", "conexão recebida!");
            delegate void SetControlValueCallback(Control oControl, string propName, object propValue);
            private void SetControlPropertyValue(Control oControl, string propName, object propValue)
            {
                if (oControl.InvokeRequired)
                {
                    SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
                    oControl.Invoke(d, new object[] { oControl, propName, propValue });
                }
                else
                {
                    Type t = oControl.GetType();
                    PropertyInfo[] props = t.GetProperties();
                    foreach (PropertyInfo p in props)
                    {
                        if (p.Name.ToUpper() == propName.ToUpper())
                        {
                            p.SetValue(oControl, propValue, null);
                        }
                    }
                }
            }

    E ao invés de chamar ibMSG.Text = "Tempo decorrido...etc";

    Você vai chamar o método: SetControlPropertyValue(lbMSG, "text", "Tempo decorrido...etc");

    Lembrando que o primeiro parâmetro é o Objeto que deseja atualizar, o segundo é o nome da propriedade que vai alterar e o terceiro é o novo valor (que pode ser string ou outro objeto). Tu podes adaptar para os diversos tipos de objetos e propriedades que precisar.

    Segue link com mais detalhes sobre estas operações, sugiro a leitura para maiores esclarecimentos pois este problema é bastante comum:

    https://docs.microsoft.com/pt-br/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls

    Espero ter ajudado, um abraço!

    • Sugerido como Resposta L 2014 quarta-feira, 22 de janeiro de 2020 13:58
    quarta-feira, 22 de janeiro de 2020 10:48