I’m getting a ‘cross-thread operation not valid’ in one member but not in another and both belong to the same Form, the same UI thread I believe. Those members are uxServerGroup and uxServerListen. When I change the text on uxServerGroup.ValuesSecondary.Heading it works fine, I don’t get a cross-thread exception. But I do get one when I try to enable back the uxServerListen button. Why?
I should probably note that both of those components belong to Krypton Toolkit, they are not standard Windows Forms.
Here’s a sample of the code I have:
MainForm.cs:
public partial class MainForm : Form {
public MainForm() {
InitializeComponent();
SomeClass.ObjectStateChanged += new ObjectStateEventHandler(SomeClass_ObjectStateChanged);
}
private void uxServerListen_Click(object sender, EventArgs e) {
uxServerListen.Enabled = false;
ClassHandler ch = ClassHandler();
ch.Initialize();
}
private void SomeClass_ObjectStateChanged(Enum e1, Enum e2) {
switch(e1) {
case E1.TypeA:
HandleTypeAObjectChanges(e1);
break;
case E1.TypeB:
HandleTypeBObjectChanges(e2);
break;
}
}
private void HandleTypeAObjectChanges(Enum e2) {
switch(e2) {
case E2.ModeA:
uxServerGroup.ValuesSecondary.Image = Resources.StatusSuccess16;
break;
case E2.ModeB:
uxServerGroup.ValuesSecondary.Image = Resources.StatusFailure16;
uxServerListen.Enabled = true;
break;
}
}
}
SomeClass.cs:
public delegate void ObjectStateEventHandler(Enum e1, Enum e2);
public static class SomeClass {
public static event ObjectStateEventHandler ObjectStateChanged;
internal static E1 e1;
internal static void ObjectStateChanged(Enum e2) {
if(ConnectionStateChanged != null) {
ConnectionStateChanged(e1, e2);
}
}
}
ClassHandler.cs:
public class ClassHandler {
public ClassHandler() {
// (...)
}
public void Initialize() {
Thread thread = new Thread(new ThreadStart(SomeMethod));
thread.Start();
}
private void SomeMethod() {
SomeClass.ObjectStateChanged(E2.ModeB);
}
}
After a lot of searching, I ended up fixing it like this:
private void SomeClass_ObjectStateChanged(Enum e1, Enum e2) {
if(InvokeRequired) {
Invoke(new MethodInvoker(() => {
switch(e1) {
case E1.TypeA:
HandleTypeAObjectChanges(e2);
break;
case E1.TypeB:
HandleTypeBObjectChanges(e2);
break;
}
}));
}
}
As an additional question, can someone explain me this (the thing inside MethodInvoker):
new MethodInvoker(() => { /* (...) */ })
Invalid cross-thread operations need to be specifically detected to trigger a usable exception. If there is no detection, you won’t get that exception. But be careful: don’t take that as permission to access the property from another thread, it’s still not usually allowed, and the few exceptions will be documented as thread-safe.
This creates an anonymous inline method, and when called, the
/* (...) */statements will be executed. Adelegateis then created to make the method callable, and that’s what gets passed toInvoke. Usually, it can be easier to write it slightly more elaborately like so: