J
jp2msft
I have a test application that I am working on to create a way for my
threads to send information back to the main thread. I would prefer to use
the BackgroundWorker class (which is perfect for doing this), but I can not
because I am working on a reduced Windows feature set (Mobile).
I designed my custom threading class with a delegate that is used to create
an event. The event, in turn, should 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 is 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 kindly help me see the error in my design?
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:
Here is the Console version, which does not throw an exception (though I
can't actually see the output)
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
threads to send information back to the main thread. I would prefer to use
the BackgroundWorker class (which is perfect for doing this), but I can not
because I am working on a reduced Windows feature set (Mobile).
I designed my custom threading class with a delegate that is used to create
an event. The event, in turn, should 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 is 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 kindly help me see the error in my design?
Both versions (Windows and Console) take an instance of this custom class
that I created as the thread's Start parameter:
Code:
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:
Code:
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]);
}
}
}
}
Here is the Console version, which does not throw an exception (though I
can't actually see the output)
Code:
class Program {
Button button1;
ListView listView1;
static void Main(string[] args) {
Program pgm = new Program();
pgm.Go();
}
public void Go() {
button1 = new Button();
button1.Enabled = true;
listView1 = new ListView();
listView1.Width = 500;
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);
Console.WriteLine("Step {0}", step);
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);
Console.WriteLine("Step {0}", step);
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);
Console.WriteLine("Step {0}", step);
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