This question already has an answer here:
这个问题已经有了答案:
- Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on 20 answers
- 跨线程操作无效:从线程以外的线程访问控制,该线程在20个答案上创建。
I want to send temperature value from a microcontroller using UART to C# interface and Display temperature on Label.Content
. Here is my microcontroller code:
我想用UART从微控制器发送温度值到c#接口,并在lab . content上显示温度。这是我的微控制器代码:
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);
}
}
and my C# code is:
我的c#代码是:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
textBox1.Text = txt.ToString();
}
but exception arises there "Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on" Please tell me how to get temperature string from my microcontroller and remove this Error!
但是出现了一个异常,“跨线程操作无效:控制从线程以外的线程访问的‘textBox1’”请告诉我如何从我的微控制器获取温度字符串并删除这个错误!
6 个解决方案
#1
232
The data received in your serialPort1_DataReceived
method is coming from another thread context than the UI thread, and that's the reason you see this error.
To remedy this, you will have to use a dispatcher as descibed in the MSDN article:
How to: Make Thread-Safe Calls to Windows Forms Controls
您的serialPort1_DataReceived方法中接收到的数据来自另一个线程上下文,而不是UI线程,这就是您看到这个错误的原因。要解决这个问题,您必须使用dispatcher作为MSDN文章“如何:如何:对Windows窗体控件进行线程安全调用”的描述
So instead of setting the text property directly in the serialport1_DataReceived
method, use this pattern:
因此,与其直接在serialport1_DataReceived方法中设置文本属性,不如使用以下模式:
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;
}
}
So in your case:
所以在你的情况中:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
SetText(txt.ToString());
}
#2
39
I don't know if this is good enough but I did made a static ThreadHelperClass class and implemented it as following .Now I can easily set text property of various controls without much coding .
我不知道这是否足够好,但是我做了一个静态的ThreadHelperClass类,并实现了它。现在我可以轻松地设置各种控件的文本属性,而无需编写太多代码。
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;
}
}
}
Using the 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.");
}
#3
19
Use the following extensions and just pass the action like:
使用以下扩展,并通过以下操作:
_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
Extension class:
扩展类:
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);
}
}
}
#4
18
you can simply do this.
你可以这么做。
TextBox.CheckForIllegalCrossThreadCalls = false;
#5
8
Along the same lines as previous answers, but a very short addition that Allows to use all Control properties without having cross thread invokation exception.
与前面的答案一样,只是添加了一个非常短的内容,允许使用所有控件属性,而不存在交叉线程调用异常。
Helper Method
辅助方法
/// <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;
}
Sample Usage
示例使用
// 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;
}
#6
6
Use a shared container to transfer data between threads.
使用共享容器在线程之间传输数据。
#1
232
The data received in your serialPort1_DataReceived
method is coming from another thread context than the UI thread, and that's the reason you see this error.
To remedy this, you will have to use a dispatcher as descibed in the MSDN article:
How to: Make Thread-Safe Calls to Windows Forms Controls
您的serialPort1_DataReceived方法中接收到的数据来自另一个线程上下文,而不是UI线程,这就是您看到这个错误的原因。要解决这个问题,您必须使用dispatcher作为MSDN文章“如何:如何:对Windows窗体控件进行线程安全调用”的描述
So instead of setting the text property directly in the serialport1_DataReceived
method, use this pattern:
因此,与其直接在serialport1_DataReceived方法中设置文本属性,不如使用以下模式:
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;
}
}
So in your case:
所以在你的情况中:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
SetText(txt.ToString());
}
#2
39
I don't know if this is good enough but I did made a static ThreadHelperClass class and implemented it as following .Now I can easily set text property of various controls without much coding .
我不知道这是否足够好,但是我做了一个静态的ThreadHelperClass类,并实现了它。现在我可以轻松地设置各种控件的文本属性,而无需编写太多代码。
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;
}
}
}
Using the 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.");
}
#3
19
Use the following extensions and just pass the action like:
使用以下扩展,并通过以下操作:
_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
Extension class:
扩展类:
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);
}
}
}
#4
18
you can simply do this.
你可以这么做。
TextBox.CheckForIllegalCrossThreadCalls = false;
#5
8
Along the same lines as previous answers, but a very short addition that Allows to use all Control properties without having cross thread invokation exception.
与前面的答案一样,只是添加了一个非常短的内容,允许使用所有控件属性,而不存在交叉线程调用异常。
Helper Method
辅助方法
/// <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;
}
Sample Usage
示例使用
// 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;
}
#6
6
Use a shared container to transfer data between threads.
使用共享容器在线程之间传输数据。