222 votes

Opération cross-thread non valide: Contrôle 'textBox1' accédé à partir d'un thread autre que celui sur lequel il a été créé

Je veux envoyer à la valeur de la température à partir d'un microcontrôleur à l'aide de l'UART pour C# interface et l'Affichage de la température sur Label.Content. Voici mon microcontrôleur code:

   while(1){
   key_scan();// get value of temp
if (Usart_Data_Ready())
                {
                   while(temperature[i]!=0)
                    {
                    if(temperature[i]!=' ')
                    {
                      Usart_Write(temperature[i]);
                      Delay_ms(1000);
                    }
                    i = i + 1;
                    }
                  i =0;
                  Delay_ms(2000);
                }
     }

et mon code C#:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        txt += serialPort1.ReadExisting().ToString();
        textBox1.Text = txt.ToString();
    }

mais exception se pose là "inter-threads non valide: le Contrôle "textBox1' accessible à partir d'un thread autre que le thread qu'il a été créé" Merci de me dire comment obtenir la température de la chaîne de mon microcontrôleur et supprimer cette Erreur!

367voto

Magnus Points 15064

Les données reçues dans votre serialPort1_DataReceived méthode est à venir à partir d'un autre thread contexte que le thread d'INTERFACE utilisateur, et c'est la raison pour laquelle vous voyez ce message d'erreur.
Pour remédier à cela, vous devrez utiliser un répartiteur comme décrit dans l'article MSDN:
Comment Faire: créer de Thread-Safe Appels à des Contrôles Windows Forms

Ainsi, au lieu de définir la propriété text directement dans l' serialport1_DataReceived méthode, l'utilisation de ce modèle:

delegate void SetTextCallback(string text);

private void SetText(string text)
{
  // InvokeRequired required compares the thread ID of the
  // calling thread to the thread ID of the creating thread.
  // If these threads are different, it returns true.
  if (this.textBox1.InvokeRequired)
  { 
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
  }
  else
  {
    this.textBox1.Text = text;
  }
}

Donc dans votre cas:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  txt += serialPort1.ReadExisting().ToString();
  SetText(txt.ToString());
}

63voto

Thunder Points 1747

Je ne sais pas si cela suffit mais j'ai créé une classe statique ThreadHelperClass et je l'ai implémenté comme suit. Maintenant, je peux facilement définir la propriété text de divers contrôles sans trop coder.

 public static class ThreadHelperClass
    {
        delegate void SetTextCallback(Form f, Control ctrl, string text);
        /// <summary>
        /// Set text property of various controls
        /// </summary>
        /// <param name="form">The calling form</param>
        /// <param name="ctrl"></param>
        /// <param name="text"></param>
        public static void SetText(Form form, Control ctrl, string text)
        {
            // InvokeRequired required compares the thread ID of the 
            // calling thread to the thread ID of the creating thread. 
            // If these threads are different, it returns true. 
            if (ctrl.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                form.Invoke(d, new object[] { form, ctrl, text });
            }
            else
            {
                ctrl.Text = text;
            }
        }
    }
 

En utilisant le code:

  private void btnTestThread_Click(object sender, EventArgs e)
        {
            Thread demoThread =
               new Thread(new ThreadStart(this.ThreadProcSafe));
            demoThread.Start();
        }

        // This method is executed on the worker thread and makes 
        // a thread-safe call on the TextBox control. 
        private void ThreadProcSafe()
        {
            ThreadHelperClass.SetText(this, textBox1, "This text was set safely.");
            ThreadHelperClass.SetText(this, textBox2, "another text was set safely.");
        }
 

46voto

HforHisham Points 684

vous pouvez simplement faire ceci.

 TextBox.CheckForIllegalCrossThreadCalls = false;
 

29voto

Utilisez les extensions suivantes et passez l'action comme ceci:

 _frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
 

Classe d'extension:

 public static class CrossThreadExtensions
{
    public static void PerformSafely(this Control target, Action action)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action);
        }
        else
        {
            action();
        }
    }

    public static void PerformSafely<T1>(this Control target, Action<T1> action,T1 parameter)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, parameter);
        }
        else
        {
            action(parameter);
        }
    }

    public static void PerformSafely<T1,T2>(this Control target, Action<T1,T2> action, T1 p1,T2 p2)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, p1,p2);
        }
        else
        {
            action(p1,p2);
        }
    }
}
 

7voto

rotator Points 166

Utilisez un conteneur partagé pour transférer des données entre des threads.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X