我是高级编程语言的初学者。我正在尝试使用VS 2010 C#
为串口创建一个WForms应用程序我收到以下错误:
Cross-thread operation not valid: Control 'rtxtDataArea' accessed from a thread other than the thread it was created on.
这发生在这里:
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
我在收到一些数据时尝试更改文本框的颜色。 它会触发交叉线程错误。
问题是当我尝试更改标签的颜色时,为什么它不会在此处触发相同的错误?
private void btnConnect_Click(object sender, EventArgs e)
{
if (ComPort.IsOpen)
{
disconnect();
}
else
{
connect();
rdText.ForeColor = Color.Blue;//ok, it works
}
}
这工作;第一个没有。
为什么呢? ComPort_DataReceived_1与btnConnect_Click的性质不同吗? 或者是什么原因?
我已经阅读了很多关于线程的内容,但我理解我无法使用,有人可以给出一个直观的解释吗?
答案 0 :(得分:2)
在winforms中,只有一个线程可以更改UI上的任何内容,例如启用按钮,更改文本框等。通常这是UI线程。这通常是你唯一的主题。
但是,如果您启动一个新线程,该线程可能想要更改UI。如果此新线程触发表单收到的事件,则会发生这种情况。
每当您看到从上创建的线程以外的线程访问的消息时,您几乎可以肯定是这种情况。
解决此问题的最简单方法是使用函数 Control.IsInvokeRequired 和 Control.Invoke 。这样做的模式如下。以下函数更新myForm上的myButton
private void UpdateMyButton (MyType myParameter)
{
if (myButton.InvokeRequired)
{ // this is not the thread that created the button to update
this.Invoke(new MethodInvoker( () => this.UpdateMyButton(myParameter)));
// this will let the UI thread call this function with the same parameter.
}
else
{ // Now called by the UI thread: this thread may change myButton
myButton.Enabled = myParameter.ShouldButtonEnable;
myButton.Text = myParameter.ButtonText;
}
}
顺便说一句,如果你必须更新表单上的几个控件,你应该为每个控件检查InvokeRequired。但是,由于它们通常由相同的UI线程创建,因此检查this.InvokeRequired就足够了。
Control.Invoke在调用完成后返回,因此在更新所有项目之后。返回Invoke后,您可以使用UpdateMyButton的结果。
如果您不希望非ui线程等待UpdateMyButton的完成,请考虑使用Control.BeginInvoke:"嘿UI线程,只要您有时间,是否可以我的UpdateMyButton。当然,在这种情况下,您无法使用UpdateMyButton的结果
答案 1 :(得分:1)
因为“DataReceived”在另一个线程而不是UI线程上运行。您必须使用Invoke:
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
if (InvokeRequired)
{
// If not on UI thread, then invoke
Invoke(new MethodInvoker(() =>
{
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}));
}
else
{
// On UI thread, invoke not needed
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
}