Short Version :
I need to pass complex data back and forth between threads, 1 thread being a WinForm, the other thread calling an online translator service and changing all the data the Winform uses.
Long Version :
Passing large amounts of data to an online translator service was freezing up my front end for minutes at a time, so I'm trying to move that logic into a thread. The Winform makes extensive use of the data that the online service needs to process and return with new info attached.
I was using this code to kick off the thread :
threadWork tw = new threadWork(); //obj to hold data for thread
tw.settings = frmMain.projectSettings;
tw.CompletedEvent += tw_CompletedEvent; // callback event, returns data
ThreadPool.QueueUserWorkItem(doWork,tw); // kick off thread
receiving callback code :
void tw_CompletedEvent(projectFormat settings)
{
frmMain.projectSettings = settings;
NotifyLoadTransationKeys(frmMain.projectSettings.translationKeys,frmMain.projectSettings.translatedLanguages);
}
which basically created this error :
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on. [on frmMain]
so I found this suggesting an alternative use of [STAThread] [sta = single thread apartment] inside Program.cs (default c# winform entry point)
using System.Threading;
Thread t = new Thread(new ThreadStart(StartNewStaThread));
// Make sure to set the apartment state BEFORE starting the thread.
t.ApartmentState = ApartmentState.STA;
t.Start();
private void StartNewStaThread() {
Application.Run(new Form1());
}
Everything in my C# app starts in 'Program.cs' so I tried the above suggestion like so :
static class Program
{
public static translationUtil TranslationUtil; //location of time intensive webservice
public static projectFormat projectSettings; //data that needs sharing
static void Main() // prog entry point
{
ProgramCode pc = new ProgramCode();
pc.Main();
}
}
class ProgramCode
{
public void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread t = new Thread(new ThreadStart(StartNewStaThread));
t.ApartmentState = ApartmentState.STA;
t.Start();
}
private void StartNewStaThread()
{
Application.Run(new Main());
}
}
in the code above I've tried to move the shared resources to 'Program.cs' and then created a new class and thread to hold my winform,but this still gives me cross threading issues (same error as before)! Has anybody got an suggestions as to how to use threads and share data successfully in my situation?
Update : @HenkHolterman answered my question best thus far, but I've come across this code many times after researching the answer he has given me (using "invoke")
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
Follow on question... the 'if' statement above appears to just set the text without invoke (on one of the branches of the 'if', should I follow this pattern as well, what is it for?