none
Thread erro: System.InvalidOperationException: 'Operação entre threads inválida: controle 'progressBar1' acessado de um thread que não é aquele no qual foi criado.' RRS feed

  • Pergunta

  • Boa Tarde Gente, 

    Desculpem a minha ignorância mas estou começando a estudar um pouco de c# e estou com esta dificuldade na parte de threads, ocorre este erro até procurei alguns tutorias mas a internet não tem me ajudado muito, ou não estou sabendo procurar, mas enfim precisa de uma ajuda como corrigir o erro citado acima, algum favor poderia me ajudar, existe algum erro no meu codigo.

    att, 

    Fabio

    using System.Threading;

    namespace Threads
    {
        public partial class frmLista01 : Form
        {
            Thread tarefa;
            public frmLista01()
            {
                InitializeComponent();
            }

            private void cmdListar_Click(object sender, EventArgs e)
            {
                //Listar();
                tarefa.IsBackground = true;
                tarefa.Priority = ThreadPriority.Lowest;
                tarefa.Start();
            }

            public void Listar()
            {
                for (int i = 0; i <= 100; i++)
                {
                    progressBar1.Value = i;
                    Thread.Sleep(100);
                }
            }

            private void timer1_Tick(object sender, EventArgs e)
            {
                lblHorario.Text = "Horário: " + DateTime.Now.ToString("hh:mm:ss");
            }

            private void frmLista01_Load(object sender, EventArgs e)
            {
                tarefa = new Thread (new ThreadStart(Listar));
                timer1.Start();
            }
        }
    }

    terça-feira, 9 de maio de 2017 16:15

Respostas

  • Olá,

    Isso acontece porque o componente de progressbar foi criado na Thread principal, a do formulário, e você tenta obter o valor de uma logica dentro de outra, elas não se conhecem. Já passei por isso, uma das alternativas que aprendi e adotei usar, é por delegação, veja:

    /// <summary>
    /// Evento botão coletar informação
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnBuscarInfo_Click(object sender, EventArgs e)
            {
                btnBuscarInfo.Enabled = false;
    
                marcaColetaThread = ColetaAsync();
    
                marcaColetaThread.Priority = ThreadPriority.Highest;
                marcaColetaThread.Start();
    
                txtResult.Clear();
                txtResult.Text = "Coletando informação ..........";
            }

    Aqui eu nomeio uma variave "marcaColetaThread ", do tipo "Thread", que recebe uma "Thead" criada dentro do método "ColetaAsync":

     /// <summary>
            /// Thread chama a execução da rotina de coleta e transfere o retorno para fora
            /// </summary>
            /// <returns></returns>
            public Thread ColetaAsync()
            {
                AsyncOperation operation = AsyncOperationManager.CreateOperation(null);
                return new Thread(new ThreadStart(delegate () {
                    try
                    {
                        string result = Coletar();
                        operation.PostOperationCompleted(delegate (object source) {
                            // Retorno pra a Thread Principal
                            RetornoColetaCompleted(result);
                        }, null);
                    }
                    catch (Exception ex)
                    {
                        operation.PostOperationCompleted(delegate (object source) {
                            ErrorRetornoColeta((Exception)source);
                        }, ex);
                    }
                }));
            }

    Nesta função, crio uma operação assíncrona que vai chamar o método que realmente vai fazer o trabalho que quero, e o retorno dela irei passar para outro método "RetornoColetaCompleted" se ocorrer tudo certo, ou "ErrorRetornoColeta" se ocorrer um erro, e dentro destes métodos eu coloco nos objetos do formulário:

    /// <summary>
            /// Expoe o retorno da coleta
            /// </summary>
            /// <param name="result"></param>
            private void RetornoColetaCompleted(string result)
            {
                txtResult.Text =  result;
                txtResult.Select(0, 0);
                btnBuscarInfo.Enabled = true;
            }
    
            /// <summary>
            /// Expoe e trata o retorno de erro da rotina de coleta
            /// </summary>
            /// <param name="error"></param>
            private void ErrorRetornoColeta(Exception error)
            {
                btnBuscarInfo.Enabled = true;
                txtResult.Text = error.Message;
            }
            
            /// <summary>
            /// Método coletar informações
            /// </summary>
            private string Coletar()
            {
                StringBuilder strbl = new StringBuilder();              
    
                Identify identificar = new Identify();          
    
                strbl.Append(identificar.GetSystemInfo());
                strbl.Append(identificar.Framework());
    
                return strbl.ToString();
            }


    Atenciosamente, Ezequiel S. Daniel

    terça-feira, 9 de maio de 2017 18:19

Todas as Respostas

  • Olá,

    Isso acontece porque o componente de progressbar foi criado na Thread principal, a do formulário, e você tenta obter o valor de uma logica dentro de outra, elas não se conhecem. Já passei por isso, uma das alternativas que aprendi e adotei usar, é por delegação, veja:

    /// <summary>
    /// Evento botão coletar informação
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnBuscarInfo_Click(object sender, EventArgs e)
            {
                btnBuscarInfo.Enabled = false;
    
                marcaColetaThread = ColetaAsync();
    
                marcaColetaThread.Priority = ThreadPriority.Highest;
                marcaColetaThread.Start();
    
                txtResult.Clear();
                txtResult.Text = "Coletando informação ..........";
            }

    Aqui eu nomeio uma variave "marcaColetaThread ", do tipo "Thread", que recebe uma "Thead" criada dentro do método "ColetaAsync":

     /// <summary>
            /// Thread chama a execução da rotina de coleta e transfere o retorno para fora
            /// </summary>
            /// <returns></returns>
            public Thread ColetaAsync()
            {
                AsyncOperation operation = AsyncOperationManager.CreateOperation(null);
                return new Thread(new ThreadStart(delegate () {
                    try
                    {
                        string result = Coletar();
                        operation.PostOperationCompleted(delegate (object source) {
                            // Retorno pra a Thread Principal
                            RetornoColetaCompleted(result);
                        }, null);
                    }
                    catch (Exception ex)
                    {
                        operation.PostOperationCompleted(delegate (object source) {
                            ErrorRetornoColeta((Exception)source);
                        }, ex);
                    }
                }));
            }

    Nesta função, crio uma operação assíncrona que vai chamar o método que realmente vai fazer o trabalho que quero, e o retorno dela irei passar para outro método "RetornoColetaCompleted" se ocorrer tudo certo, ou "ErrorRetornoColeta" se ocorrer um erro, e dentro destes métodos eu coloco nos objetos do formulário:

    /// <summary>
            /// Expoe o retorno da coleta
            /// </summary>
            /// <param name="result"></param>
            private void RetornoColetaCompleted(string result)
            {
                txtResult.Text =  result;
                txtResult.Select(0, 0);
                btnBuscarInfo.Enabled = true;
            }
    
            /// <summary>
            /// Expoe e trata o retorno de erro da rotina de coleta
            /// </summary>
            /// <param name="error"></param>
            private void ErrorRetornoColeta(Exception error)
            {
                btnBuscarInfo.Enabled = true;
                txtResult.Text = error.Message;
            }
            
            /// <summary>
            /// Método coletar informações
            /// </summary>
            private string Coletar()
            {
                StringBuilder strbl = new StringBuilder();              
    
                Identify identificar = new Identify();          
    
                strbl.Append(identificar.GetSystemInfo());
                strbl.Append(identificar.Framework());
    
                return strbl.ToString();
            }


    Atenciosamente, Ezequiel S. Daniel

    terça-feira, 9 de maio de 2017 18:19
  • Amigo Ezequiel,

    Em primeiro lugar gostaria de agradece-lo pela reposta rapida e objetiva eu testei e funcionou perfeitamente, porém pesquisei e verificquei que também tem como fazer por delegate, funcionou também, e o código ficou desta forma.

    using System.Threading;
    using System.Reflection;

    namespace Threads
    {
        public partial class frmLista01 : Form
        {
            Thread tarefa;
            public frmLista01()
            {
                InitializeComponent();
            }

            private void cmdListar_Click(object sender, EventArgs e)
            {
                //Listar();
                tarefa.IsBackground = true;
                tarefa.Priority = ThreadPriority.Lowest;
                tarefa.Start();
            }

            public void Listar()
            {
                for (int i = 0; i <= 100; i++)
                {
                    //progressBar1.Value = i;
                    SetControlPropertyValue(progressBar1, "value", i);
                    Thread.Sleep(100);
                }
            }

            private void timer1_Tick(object sender, EventArgs e)
            {
                lblHorario.Text = "Horário: " + DateTime.Now.ToString("hh:mm:ss");
            }

            private void frmLista01_Load(object sender, EventArgs e)
            {
                tarefa = new Thread (new ThreadStart(Listar));
                timer1.Start();
            }

            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);
                        }
                    }
                }
            }
        }

    att,

    Fabio 

    terça-feira, 9 de maio de 2017 19:57
  • Isso,

    Existe varias formas de fazer, eu adotei a primeiro por ter conseguido entender mais rápido, mas você pode utilizar outras maneiras para chegar no mesmo resultado, utilize o que você sinta mais confiança..

    Obrigado pelo retorno!!


    Atenciosamente, Ezequiel S. Daniel

    sexta-feira, 12 de maio de 2017 12:54