I noticed a difference this weekend with how multi-threading works with the Thread class. Prior to CLR 2.0 you could perform cross-thread operations. Now, doing so generates an InvalidOperationException with the message “Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on.”
Here is the code that will compile on both 1.1 and 2.0, but fails on 2.0 with the aforementioned exception:
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button startButton;
private System.Windows.Forms.Button cancelButton;
private System.ComponentModel.Container components = null;
private bool m_cancelled = false;
// designer code removed for brevity
private bool Cancelled
{
get { return m_cancelled; }
set { m_cancelled = value; }
}
private void startButton_Click(object sender, System.EventArgs e)
{
Cancelled = false;
startButton.Enabled = false;
cancelButton.Enabled = true;
Thread processThread = new Thread(new ThreadStart(ProcessSomething));
processThread.Start();
}
private void cancelButton_Click(object sender, System.EventArgs e)
{
Cancelled = true;
}
private void ProcessSomething()
{
DateTime startTime = DateTime.Now;
do
{
if (Cancelled) break;
TimeSpan duration = DateTime.Now.Subtract(startTime);
double totalSeconds = duration.TotalSeconds;
double percent = totalSeconds / 10 * 100.0;
int progressPercent = (int)percent;
progressPercent = (progressPercent > 100 ? 100 : progressPercent);
progressBar1.Value = progressPercent;
}
while (DateTime.Now < startTime.AddSeconds(10));
startButton.Enabled = true;
cancelButton.Enabled = false;
}
}
Now, for the purposes of the progress bar you're okay, because you can use the new BackgroundWorker class. This class exposes a ProgressChanged and RunWorkerCompleted that should allow you to perform what you need. The ReportProgress method raises the ProgressChanged event and it has two overloads. The first simply provides an integer progress value. But, the second overload adds a state argument that can be any object.
I'm going to look at this a little more. I have a good idea why Microsoft decided to change the way this works. But, I think I like being able to shoot myself in the foot if I so choose. I have mixed feelings on this.