c# - traves - Operación entre hilos no válida: Control ''textBox1'' al que se accede desde un hilo que no sea el hilo en el que se creó en
la aplicación llamó a una interfaz que se aplanó para un diferente subproceso (6)
Esta pregunta ya tiene una respuesta aquí:
Quiero enviar el valor de temperatura desde un microcontrolador usando UART a la interfaz C # y Mostrar temperatura en Label.Content
. Aquí está mi código de microcontrolador:
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);
}
}
y mi código C # es:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
textBox1.Text = txt.ToString();
}
pero la excepción surge allí " Operación entre hilos no válida: Control ''textBox1'' accedido desde un hilo que no sea el hilo en el que fue creado " Por favor dígame cómo obtener la cadena de temperatura de mi microcontrolador y eliminar este Error!
En la misma línea que las respuestas anteriores, pero una adición muy corta que permite usar todas las propiedades de control sin tener excepción de invocación de hilo cruzado.
Método auxiliar
/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c,Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
else return false;
return true;
}
Uso de la muestra
// usage on textbox
public void UpdateTextBox1(String text)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
textBox1.Text = ellapsed;
}
//Or any control
public void UpdateControl(Color c,String s)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(myControl, () => UpdateControl(c,s))) return;
myControl.Text = s;
myControl.BackColor = c;
}
Los datos recibidos en su método serialPort1_DataReceived
provienen de otro contexto de subproceso que el subproceso de interfaz de usuario, y esa es la razón por la que ve este error.
Para remediar esto, deberá usar un despachador como se describe en el artículo de MSDN:
Cómo hacer llamadas Thread-Safe a Windows Forms Controls
Entonces, en lugar de establecer la propiedad de texto directamente en el método serialport1_DataReceived
, use este patrón:
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;
}
}
Entonces en tu caso:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
SetText(txt.ToString());
}
No sé si esto es lo suficientemente bueno, pero hice una clase ThreadHelperClass estática y la implementé como sigue. Ahora puedo establecer fácilmente la propiedad de texto de varios controles sin mucha codificación.
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;
}
}
}
Usando el código:
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.");
}
Use las siguientes extensiones y simplemente pase la acción como:
_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
Clase de extensión:
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);
}
}
}
Use un contenedor compartido para transferir datos entre hilos.
simplemente puedes hacer esto.
TextBox.CheckForIllegalCrossThreadCalls = false;