Cross-Thread Operation Not Valid: Control Accessed from a Thread Other Than the Thread It Was Created On

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on

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

So instead of setting the text property directly in the serialport1_DataReceived method, use this pattern:

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

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

As per Prerak K's update comment (since deleted):

I guess I have not presented the question properly.

Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

So only accessing the value so that corresponding data can be fetched from the database.

The solution you want then should look like:

UserContrl1_LOadDataMethod()
{
string name = "";
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
}
if(name == "MyName")
{
// do whatever
}
}

Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:

UserContrl1_LOadDataMethod()
{
if(textbox1.text=="MyName") //<<======Now it wont give exception**
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be
//bound to grid at some later stage
if(InvokeRequired)
{
// after we've done all the processing,
this.Invoke(new MethodInvoker(delegate {
// load the control with the appropriate data
}));
return;
}
}
}

Thread exception error in C# :Cross-thread operation not valid: Control 'lblp4' accessed from a Cross-thread operation not valid

As the exception suggests, you're accessing controls from a thread other than the one that that created the control (specifically, your ph1..5 threads all attempt to access the UI).

To correct this, you need to use the Invoke() method on the controls so that the accesses are performed on the main UI thread.

Perhaps add a function to Philosopher as follows:

private void UpdateText(Label label, string text)
{
// If the current thread is not the UI thread, InvokeRequired will be true
if (label.InvokeRequired)
{
// If so, call Invoke, passing it a lambda expression which calls
// UpdateText with the same label and text, but on the UI thread instead.
label.Invoke((Action)(() => UpdateText(label, text)));
return;
}
// If we're running on the UI thread, we'll get here, and can safely update
// the label's text.
label.Text = text;
}

Then, whenever you have something like:

AZsys.Program.frm.lblp1.Text = "Eating...";

Replace it with:

UpdateText(AZsys.Program.frm.lblp1, "Eating...");

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on

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

So instead of setting the text property directly in the serialport1_DataReceived method, use this pattern:

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

Cross-thread operation not valid: Control 'RichTextBox' accessed from a thread other than the thread it was created on

You can't access UI elements from just any thread - it must be on the UI thread. With a handler like that, you can't assume you are on the UI thread and actually you should assume you may not be. The way you ensure a UI control is accessed properly, always check if InvokeRequired before accessing it.

Dim text As String
If control.InvokeRequired Then
control.Invoke(Sub() text = control.Text)
Else
text = control.Text
End If

In your case, the issue is in HandleUpdateAsync. The line of code

Await botClient.SendTextMessageAsync("chatid", RichTextBox1.Text)

could be changed to

Dim text As String
If RichTextBox1.InvokeRequired Then
RichTextBox1.Invoke(Sub() text = RichTextBox1.Text)
Else
text = RichTextBox1.Text
End If
Await botClient.SendTextMessageAsync("chatid", text)

The accepted way to do this is not inline in the code like that. Your google search has found it. Generally, it's like this

Private Function GetRichTextBox1Text() As String
If RichTextBox1.InvokeRequired Then
Return RichTextBox1.Invoke(AddressOf GetRichTextBox1Text)
Else
Return RichTextBox1.Text
End If
End Function
Await botClient.SendTextMessageAsync("chatid", GetRichTextBox1Text())

Nobody wants to do this all the time. You can write extension methods (for action and simple function) in a Module like this, and of course you can extrapolate on these for any number of arguments

<Extension(), DebuggerHidden()>
Public Sub InvokeIfRequired(control As Control, action As MethodInvoker)
If control.InvokeRequired Then control.Invoke(action) Else action()
End Sub
' No parameters
<Extension(), DebuggerHidden()>
Public Function InvokeIfRequired(Of TR)(control As Control, func As Func(Of TR)) As TR
If control.InvokeRequired Then
Return control.Invoke(func)
Else
Return func.Invoke()
End If
End Function
' One parameter
<Extension(), DebuggerHidden()>
Public Function InvokeIfRequired(Of T1, TR)(control As Control, func As Func(Of T1, TR), arg1 As T1) As TR
If control.InvokeRequired Then
Return CType(control.Invoke(func, arg1), TR)
Else
Return func.Invoke(arg1)
End If
End Function
' n parameters can be extrapolated...

In your case, use the function

Await botClient.SendTextMessageAsync("chatid", RichTextBox1.InvokeIfRequired(Function() RichTextBox1.Text))


Related Topics



Leave a reply



Submit