There is some horrible mixing of UI and business logic going on here. It's a good thing you tripped over this cross threading issue and are discovering that you have all those places where you'd need to Invoke, because it shows up the real problem – you're trying to run the tests and do UI work all in the same place, and that leads to messy code.
"So now I created a seperate thread to run this process in but I still need to update and get info from controls on the main Form."
No you don't. You still need to get information from the data model which is also being displayed on the main form, and you need to be able to signal progress and completion. The thread running the tests shouldn't know anything about the UI.
Broadly speaking what you want is something like
class TestRunner {
public event EventHandler<IntEventArgs> Completed;
public event EventHandler<IntEventArgs> Progress;
public event EventHandler<TestCaseEventArgs> CaseComplete;
public List<decimal> TestCases { get; set; }
public void RunTests(){
int fails = 0;
for(int i = 0; i < TestCases.Count; i++){
decimal d = TestCases[i];
input = ExecCommand("TESTSEQ" + d.ToString().PadLeft(2, '0') + "\r", 3000, "No responese from OVPT");
if(!TestPassed) fails++;
EventHandler<TestCaseEventArgs> completeHandler = CaseComplete;
if(null != completeHandler) completeHandler(this, new TestCaseEventArgs(i, d, TestPassed);
EventHandler<IntEventArgs> progressHandler = Progress;
if(null != progressHandler) progressHandler(this, new IntCaseEventArgs(i);
}
EventHandler<IntEventArgs> completedHandler = Completed;
if(null != completedHandler) completedHandler(this, new IntCaseEventArgs(fails);
}
public class TestCaseEventArgs: EventArgs {
public int Index { get; private set; }
public decimal Value { get; private set; }
public bool Passed { get; private set; }
public TestCompleteEventArgs(int index, decimal value, bool passed) { Index = index; Value = value; Passed = passed; }
}
}
public class IntEventArgs: EventArgs {
public int Value { get; private set; }
public TestCompleteEventArgs(int value) { Value = value; }
}
Then your main form should construct the input data for the test run and hook the events. The event handlers will need to use Invoke or BeginInvoke, but that's only 3 methods.
class MainForm {
TestRunner testRunner = new TestRunner();
private void StartTests(){
Thread t = new Thread(testRunner.RunTests);
SetControlState(false);
List<decimal> cases = new List<decimal>();
for(int i = 0; i < checkedListBox1.Items.Count; i++){
if(checkedListBox1.Items[i].Selected) cases.Add(i);
}
progressBar.MaxValue = cases.Count;
testRunner.TestCases = cases;
t.Start();
}
public MainForm(){
testRunner.Completed += TestComplete;
testRunner.Progress += TestProgress;
testRunner.CaseComplete += CaseComplete;
}
private void TestProgress(object sender, IntEventArgs e){
Invoke( () => progressBar.Value = e.Value );
}
private void CaseComplete(object sender, TestRunner.TestCaseEventArgs e){
Invoke ( () => {
});
}
private void TestComplete(object sender, IntEventArgs e){
Invoke( ()=> {
if (e.Value > 0)
{
label_Result.Text = "FAILED!!";
label_Result.BackColor = Color.Red;
label_Result.ImageIndex = 0;
}
else
{
label_Result.Text = "PASS!!";
label_Result.BackColor = Color.Lime;
label_Result.ImageIndex = 1;
}
SetControlState(true);
} );
void SetControlState(bool state){
}
}
(If you're not using .Net 4 you'll have to Invoke on a proper method or anonymous delegate, instead of this rather neat lambda trick I was shown a while back.)
It's probably possible to have a BackgroundWorkerThread for the test runner, which reports progress and completion, but I'm not sure it gains a lot (the thread class is pretty simple here).