joe_pool_is
Contributor
I have a test application that sends data from a new thread back to the main thread. I would prefer to use the BackgroundWorker class (which is perfect for doing this), but I can not, as explained in my earlier thread No BackgroundWorker Allowed.
I designed the custom class ThreadParameter (below) with a delegate and a corresponding an event. The event, in turn, is intended to Invoke the method in the main thread; however, whenever the method in the main thread is called and I attempt to access one of the controls on my form, I get an InvaidOperationException telling me that I can not access the control from a thread other than where it is owned. I thought I was!
So, I wrote an even more basic application that was Console based because I wanted to cut out all of the fat before posting it here. Ugh! The Console Application runs without a hitch, and it is mostly from cut and paste from my Windows Application!
Below are the "meat and potatoes" of my two basic applications. Could someone with vast knowledge (i.e. Marble Eater or Plausibly) kindly help me see the error to my ways?
Both versions (Windows and Console) take an instance of this custom class that I created as the thread's Start parameter:
Here is the Windows version, which throws the InvalidOperationException:
Getting this to work would be a considerable milestone for me.
If (after getting the code itself to work) someone sees bad logic or bad techniques, I would very much like to hear your input. Years ago, my major was physics, so I don't always take the best approach to software development.
Regards,
Joe
I designed the custom class ThreadParameter (below) with a delegate and a corresponding an event. The event, in turn, is intended to Invoke the method in the main thread; however, whenever the method in the main thread is called and I attempt to access one of the controls on my form, I get an InvaidOperationException telling me that I can not access the control from a thread other than where it is owned. I thought I was!
So, I wrote an even more basic application that was Console based because I wanted to cut out all of the fat before posting it here. Ugh! The Console Application runs without a hitch, and it is mostly from cut and paste from my Windows Application!
Below are the "meat and potatoes" of my two basic applications. Could someone with vast knowledge (i.e. Marble Eater or Plausibly) kindly help me see the error to my ways?
Both versions (Windows and Console) take an instance of this custom class that I created as the thread's Start parameter:
C#:
public class ThreadParameter {
string _serialNumber;
public delegate void ReportProgressDelegate(int step, DataTable data);
public event ReportProgressDelegate ProgressChanged;
public ThreadParameter(string serialNumber) {
_serialNumber = serialNumber;
ProgressChanged = null;
}
public void ReportProgress(int step, DataTable data) {
if (ProgressChanged != null) {
ProgressChanged.Invoke(step, data);
}
}
public string SerialNumber { get { return _serialNumber; } }
}
Here is the Windows version, which throws the InvalidOperationException:
C#:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
void button1_Click(object sender, EventArgs e) {
Go();
}
void Go() {
listView1.Clear();
listView1.Columns.Add("TestName", 140);
listView1.Columns.Add("Result", (listView1.Width - listView1.Columns[0].Width - 2));
string serialNumber = "123456789A";
ThreadParameter tp = new ThreadParameter(serialNumber);
tp.ProgressChanged += new ThreadParameter.ReportProgressDelegate(Thread_Report);
Thread th = new Thread(new ParameterizedThreadStart(Thread_Routine));
th.IsBackground = true;
th.Start(tp);
while (!th.IsAlive) Thread.Sleep(0);
button1.Enabled = false;
th.Join();
button1.Enabled = true;
}
void Thread_Report(int step, object data) {
if ((data != null) && (data is DataTable)) {
DataTable table = (DataTable)data;
const string TEST_RESULT = "Test_Result";
switch (step) {
case 0:
listView1.Items.Add(new ListViewItem(new string[] { "Row 1", string.Empty }));
listView1.Items.Add(new ListViewItem(new string[] { "Row 2", string.Empty }));
listView1.Items.Add(new ListViewItem(new string[] { "Row 3", string.Empty }));
listView1.Items.Add(new ListViewItem(new string[] { "Row 4", string.Empty }));
listView1.Items.Add(new ListViewItem(new string[] { "Row 5", string.Empty }));
listView1.Items.Add(new ListViewItem(new string[] { "Row 6", string.Empty }));
break;
case 1:
if ((2 < table.Rows.Count) && (table.Columns.Contains(TEST_RESULT))) {
listView1.Items[0].SubItems[1].Text = table.Rows[1][TEST_RESULT].ToString();
listView1.Items[1].SubItems[1].Text = table.Rows[2][TEST_RESULT].ToString();
}
break;
case 2:
if ((4 < table.Rows.Count) && (table.Columns.Contains(TEST_RESULT))) {
listView1.Items[2].SubItems[1].Text = table.Rows[3][TEST_RESULT].ToString();
listView1.Items[3].SubItems[1].Text = table.Rows[4][TEST_RESULT].ToString();
}
break;
case 3:
if ((6 < table.Rows.Count) && (table.Columns.Contains(TEST_RESULT))) {
listView1.Items[4].SubItems[1].Text = table.Rows[5][TEST_RESULT].ToString();
listView1.Items[5].SubItems[1].Text = table.Rows[6][TEST_RESULT].ToString();
}
break;
default:
break;
}
}
}
void Thread_Routine(object threadParam) {
ThreadParameter tp = (ThreadParameter)threadParam;
const string sqlConn = "SUPPLY_YOUR_CONNECTION_STRING";
using (SqlConnection conn = new SqlConnection(sqlConn)) {
int step = 0;
string sqlText = "sp_GetPartRecord";
DataSet ds = new DataSet();
using (DataTable table = new DataTable()) {
tp.ReportProgress(step++, table);
}
using (SqlDataAdapter da = new SqlDataAdapter(sqlText, sqlConn)) {
DataTable table = new DataTable();
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.AddWithValue("@SN", tp.SerialNumber);
da.Fill(table);
ds.Tables.Add(table);
tp.ReportProgress(step++, ds.Tables[ds.Tables.Count - 1]);
}
using (SqlDataAdapter da = new SqlDataAdapter(sqlText, sqlConn)) {
DataTable table = new DataTable();
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.AddWithValue("@SN", tp.SerialNumber);
da.Fill(table);
ds.Tables.Add(table);
tp.ReportProgress(step++, ds.Tables[ds.Tables.Count - 1]);
}
using (SqlDataAdapter da = new SqlDataAdapter(sqlText, sqlConn)) {
DataTable table = new DataTable();
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.AddWithValue("@SN", tp.SerialNumber);
da.Fill(table);
ds.Tables.Add(table);
tp.ReportProgress(step++, ds.Tables[ds.Tables.Count - 1]);
}
}
}
}
Getting this to work would be a considerable milestone for me.
If (after getting the code itself to work) someone sees bad logic or bad techniques, I would very much like to hear your input. Years ago, my major was physics, so I don't always take the best approach to software development.
Regards,
Joe