This type of behavior is by design. It is a protection mechanism put in place for Form threading to protect the contents of controls by ensuring that mutual exclusion takes place. Imagine if any thread could read/write the contents of any control at any time. Without the use of synchronization constructs such as Sempahores and Monitors you would be left with a very unstable application.
You have the right idea. It is a good idea to put long running GUI code in its own thread. The main reason for this is that you do not want to tie up what is known as the "message pump". By default, Form controls are bound to the thread that run their message pump. So, if some long running code is on that thread, the other of the controls on the form are not going to get serviced since they are being blocked.
So, back to the original problem. You've spawned off a new thread in the background that is completely separate from the thread where the control was created. Now, from this background thread, you want to update the control that was created on the main UI thread (as mentioned above). The main two ways to do this are using Invoke, which is synchronous, and BeginInvoke, which is asynchronous. Invoke will cause the calling thread to block while BeginInvoke will allow you to get called back (much like how BeginInvoke works for delegates. However BeginInvoke works in a slightly different manner and you are not required to have a corresponding EndInvoke call).
There are plenty of examples around for both of these, so I won't go into details here. In .NET 2.0 there is a spiffy new type of delegate called MethodInvoker. I use it a lot. This is what it looks like:
C#:
private void MyProcess()
{
for (int i = 0; i < 10; i++)
{
BeginInvoke((MethodInvoker)delegate
{
textBox1.Text = Convert.ToString(i);
});
Thread.Sleep(500);
}
}
You could then blast this off in its own thread with:
C#:
Thread thread = new Thread(new ThreadStart(MyProcess));
thread.IsBackground = true;
thread.Start();
Another useful property is the "InvokeRequired" property -- which exists for all form controls. It basically returns true if the current process is running on a thread other than the main UI thread and false if it is not. This is very helpful at times. You can use it like the following:
C#:
private void MyProcess()
{
for (int i = 0; i < 10; i++)
{
if (InvokeRequired)
{
BeginInvoke((MethodInvoker)delegate
{
// we must marshal our GUI update to the main UI thread
textBox1.Text = Convert.ToString(i);
});
}
else
{
// hey, we're running on the main UI thread -- we can just
// perform an update as normal
textBox1.Text = Convert.ToString(i);
}
Thread.Sleep(500);
}
}
(This is a simple example for the sake of demonstration)