OK, so you have a windows form and want to spawn a new thread, and from the new thread, you need to be able to report back on progress. But your getting an error that says: "Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on." So, how do you fix this? What is the actual issue? A Windows form has one, and only one GUI thread. Any manipulation of the GUI MUST be done from that thread, and that thread only. The is a runtime error, not a design time error. So you can write code that updates a label on the form as to status, but the end result is the above error message. Note that this error is usually thrown, but in some instances, your code can seem to work in the development evironment, but then in the release version, you sometimes will get a hang or a crash. Not what you were hoping. The answer is simple, NEVER manipulate the GUI in anyway in other than on the main thread. So how do you do accomplish that? The answer, again is simple, check for control.InvokeRequired in your GUI update method. If it returns true, you use control.Invoke to invoke the method on the correct thread. Here is a code example:
private void UpdateStatusLabel(string message)
{
if(this.InvokeRequired)
{
this.message = message;
this.Invoke( new MethodInvoker(UpdateStatusLabel_Invoked));
}
else
{
this.lblStatus = message;
}
}
private void UpdateStatusLabel_Invoked()
{
//This will be run on the correct thread.
this.UpdateStatusLabel(this.message);
this.message = string.Empty;
}
private string message = string.Empty;
What this code is doing is simply checking to see if the thread from which it is being called is the GUI thread. If so, it returns true and invokes the method on the correct thread and then returns. Note the use of the MethodInvoker delegate. This is a delegate that return void and takes no parameters. It was used in this example for simplicity. In a production environment, one would create a delegate to match the method. Then one would not have to store the parameter. The extended example looks like this:
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent();
}
private void btnDoWork_Click(object sender, EventArgs e)
{ CreateNewThread();
}
private void CreateNewThread()
{ UpdateStatus("Creating New Thread"); Thread thread = new Thread(new ParameterizedThreadStart(StartThread));
thread.Start();
}
private void StartThread(object data)
{ this.UpdateStatus("Running StartThread() on a new thread."); Thread.Sleep(2000);
this.UpdateStatus("Done!"); }
private void UpdateStatus_Invoked(string msg)
{ UpdateStatus(msg);
}
private void UpdateStatus(string msg)
{ if (this.InvokeRequired)
{ this.Invoke(new MethodInvoker_TakesString(UpdateStatus_Invoked), new object[] { msg }); }
else
{
this.lblStatus.Text = msg;
}
}
public delegate void MethodInvoker_TakesString(string message);
}