Simple Threading Problem with WinForm

E

Elliot Rodriguez

Hi:

I am writing a WinForm app that contains a DataGrid control and a StatusBar
control. My goal is to update the status bar using events from a separate
class, as well as some other simple things.

The method I am writing queries a large dataset. As part of my feedback to
the user, I am updating the status bar when the connection is made and the
dataset is actually retrieved. The dataset retrieval method I have placed on
a separate thread so that the status bar updates can be viewed by the user.

Everything works except for the actual binding of my dataset to the grid
control. when I attempt to bind my dataset, i get an error along the lines
of "cannot make control on parent thread of another control" or something to
that effect (I'm sorry, I dont have the actual text handy, and the query
takes a while so forgive the paraphrasing.). I am new to writing.NET
threading code, and would prefer to learn in this instance how it works and
how I broke it rather than copy and paste code, so can anyone please take a
sec and view below and tell me what I am missing? Some of the code I took
from the MSDN's Threading tutorial.

private void mnuGetDocuments_Click(object sender, System.EventArgs e)

{

if (MessageBox.Show(this, "Retrieve list of documents waiting for
printing?", "Confirm Queue Retrieval",
MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

// my events, QueueDocs is object outside of function scope.

QueueDocs.ConnectionEstablished += new
ConnectionMadeHandler(UpdateStatusOnConnection);

QueueDocs.DatasetReceived += new
DatasetReceivedHandler(UpdateStatusOnDataset);

this.sbOperationStatus.Text = "Retrieving queues... 0% complete";

// instance our new thread

Thread objThread = new Thread(new ThreadStart(this.GetDocuments));


objThread.Start();

while (!objThread.IsAlive)

{

Thread.Sleep(1);

objThread.Abort();

objThread.Join();

}

}

}



private void GetDocuments()

{

DataSet dsLocal = QueueDocs.RetrievePrintQueues();

// breaks here, on binding.

this.dgQueueDocuments.DataSource = dsLocal.Tables[0].DefaultView;

}



Thank you,
Elliot Rodriguez
 
N

Nicholas Paldino [.NET/C# MVP]

Elliot,

The reason it does not work is that you are trying to update the UI on a
thread that doesn't have the message loop for the UI. If you want to set
the data source on the datagrid (or do any other sort of updates), then you
have to create a delegate and pass it to the Invoke method on the datagrid,
and it will invoke the method, like so:

// The delegate to call the method to set the data source.
private delegate void SetDataSourceCallback(DataView view);

// The method that sets the data source.
private void SetDataSource(DataView view)
{
// Set the data source.
dgQueueDocuments.DataSource = view;
}

// This code is called in the thread.
// Your comment:
// breaks here, on binding.
dgQueueDocuments.Invoke(new SetDataSourceCallback(SetDataSource,
new object[]{dsLocal.Tables[0].DefaultView});


You will have to do this for any UI operation you want to perform from
another thread, including your update of the status bar.

A few other things. I don't understand the purpose of this code:

while (!objThread.IsAlive)
{
Thread.Sleep(1);
objThread.Abort();
objThread.Join();
}

Your thread should always be alive, and if you were waiting for the
process to kick off, you would end up sleeping for a second then aborting
it. It doesn't make sense. If you want to wait for the thread to kick off,
then use a ManualResetEvent and wait on it, and set it in the first line of
your method that is called on the new thread.

Also, if you bind to dsLocal.Tables[0], the data grid will automatically
bind to the DefaultView property on that table, it never binds to the table
directly.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Elliot Rodriguez said:
Hi:

I am writing a WinForm app that contains a DataGrid control and a
StatusBar control. My goal is to update the status bar using events from a
separate class, as well as some other simple things.

The method I am writing queries a large dataset. As part of my feedback to
the user, I am updating the status bar when the connection is made and the
dataset is actually retrieved. The dataset retrieval method I have placed
on a separate thread so that the status bar updates can be viewed by the
user.

Everything works except for the actual binding of my dataset to the grid
control. when I attempt to bind my dataset, i get an error along the lines
of "cannot make control on parent thread of another control" or something
to that effect (I'm sorry, I dont have the actual text handy, and the
query takes a while so forgive the paraphrasing.). I am new to writing.NET
threading code, and would prefer to learn in this instance how it works
and how I broke it rather than copy and paste code, so can anyone please
take a sec and view below and tell me what I am missing? Some of the code
I took from the MSDN's Threading tutorial.

private void mnuGetDocuments_Click(object sender, System.EventArgs e)

{

if (MessageBox.Show(this, "Retrieve list of documents waiting for
printing?", "Confirm Queue Retrieval",
MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

// my events, QueueDocs is object outside of function scope.

QueueDocs.ConnectionEstablished += new
ConnectionMadeHandler(UpdateStatusOnConnection);

QueueDocs.DatasetReceived += new
DatasetReceivedHandler(UpdateStatusOnDataset);

this.sbOperationStatus.Text = "Retrieving queues... 0% complete";

// instance our new thread

Thread objThread = new Thread(new ThreadStart(this.GetDocuments));


objThread.Start();

while (!objThread.IsAlive)

{

Thread.Sleep(1);

objThread.Abort();

objThread.Join();

}

}

}



private void GetDocuments()

{

DataSet dsLocal = QueueDocs.RetrievePrintQueues();

// breaks here, on binding.

this.dgQueueDocuments.DataSource = dsLocal.Tables[0].DefaultView;

}



Thank you,
Elliot Rodriguez
 
E

Elliot Rodriguez

Nicholas:

Thanks for your reply.

In response to:
while (!objThread.IsAlive)
{
Thread.Sleep(1);
objThread.Abort();
objThread.Join();
}

Well, this was part of the threading tutorial I found in the MSDN. To me it
seemed it was a requirement for implementation. If youre suggesting its not
needed, then I should certainly remove it.

However, I understood this snippet to be the code that kills the thread. I
could be wrong of course.

Again, many thanks.
Elliot Rodriguez

Nicholas Paldino said:
Elliot,

The reason it does not work is that you are trying to update the UI on
a thread that doesn't have the message loop for the UI. If you want to
set the data source on the datagrid (or do any other sort of updates),
then you have to create a delegate and pass it to the Invoke method on the
datagrid, and it will invoke the method, like so:

// The delegate to call the method to set the data source.
private delegate void SetDataSourceCallback(DataView view);

// The method that sets the data source.
private void SetDataSource(DataView view)
{
// Set the data source.
dgQueueDocuments.DataSource = view;
}

// This code is called in the thread.
// Your comment:
// breaks here, on binding.
dgQueueDocuments.Invoke(new SetDataSourceCallback(SetDataSource,
new object[]{dsLocal.Tables[0].DefaultView});


You will have to do this for any UI operation you want to perform from
another thread, including your update of the status bar.

A few other things. I don't understand the purpose of this code:

while (!objThread.IsAlive)
{
Thread.Sleep(1);
objThread.Abort();
objThread.Join();
}

Your thread should always be alive, and if you were waiting for the
process to kick off, you would end up sleeping for a second then aborting
it. It doesn't make sense. If you want to wait for the thread to kick
off, then use a ManualResetEvent and wait on it, and set it in the first
line of your method that is called on the new thread.

Also, if you bind to dsLocal.Tables[0], the data grid will
automatically bind to the DefaultView property on that table, it never
binds to the table directly.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Elliot Rodriguez said:
Hi:

I am writing a WinForm app that contains a DataGrid control and a
StatusBar control. My goal is to update the status bar using events from
a separate class, as well as some other simple things.

The method I am writing queries a large dataset. As part of my feedback
to the user, I am updating the status bar when the connection is made and
the dataset is actually retrieved. The dataset retrieval method I have
placed on a separate thread so that the status bar updates can be viewed
by the user.

Everything works except for the actual binding of my dataset to the grid
control. when I attempt to bind my dataset, i get an error along the
lines of "cannot make control on parent thread of another control" or
something to that effect (I'm sorry, I dont have the actual text handy,
and the query takes a while so forgive the paraphrasing.). I am new to
writing.NET threading code, and would prefer to learn in this instance
how it works and how I broke it rather than copy and paste code, so can
anyone please take a sec and view below and tell me what I am missing?
Some of the code I took from the MSDN's Threading tutorial.

private void mnuGetDocuments_Click(object sender, System.EventArgs e)

{

if (MessageBox.Show(this, "Retrieve list of documents waiting for
printing?", "Confirm Queue Retrieval",
MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

// my events, QueueDocs is object outside of function scope.

QueueDocs.ConnectionEstablished += new
ConnectionMadeHandler(UpdateStatusOnConnection);

QueueDocs.DatasetReceived += new
DatasetReceivedHandler(UpdateStatusOnDataset);

this.sbOperationStatus.Text = "Retrieving queues... 0% complete";

// instance our new thread

Thread objThread = new Thread(new ThreadStart(this.GetDocuments));


objThread.Start();

while (!objThread.IsAlive)

{

Thread.Sleep(1);

objThread.Abort();

objThread.Join();

}

}

}



private void GetDocuments()

{

DataSet dsLocal = QueueDocs.RetrievePrintQueues();

// breaks here, on binding.

this.dgQueueDocuments.DataSource = dsLocal.Tables[0].DefaultView;

}



Thank you,
Elliot Rodriguez
 
E

Elliot Rodriguez

Nicholas:

I am having trouble implementing the code you posted.

I've been checking out Google and it appears that it should work, but I am
not sure why it isnt.

If I use your code as is, I get the error "Method(method name) referenced
without parentheses".

Thinking this was just an oversight, I added the parens, at which point I
thought I should pass an argument (since the method expects one), and that
breaks as well "Method name expected".

Frankly I am still very fuzzy on how threading works in .NET, so I am not
sure what to do here.

Thanks again,
Elliot
Nicholas Paldino said:
Elliot,

The reason it does not work is that you are trying to update the UI on
a thread that doesn't have the message loop for the UI. If you want to
set the data source on the datagrid (or do any other sort of updates),
then you have to create a delegate and pass it to the Invoke method on the
datagrid, and it will invoke the method, like so:

// The delegate to call the method to set the data source.
private delegate void SetDataSourceCallback(DataView view);

// The method that sets the data source.
private void SetDataSource(DataView view)
{
// Set the data source.
dgQueueDocuments.DataSource = view;
}

// This code is called in the thread.
// Your comment:
// breaks here, on binding.
dgQueueDocuments.Invoke(new SetDataSourceCallback(SetDataSource,
new object[]{dsLocal.Tables[0].DefaultView});


You will have to do this for any UI operation you want to perform from
another thread, including your update of the status bar.

A few other things. I don't understand the purpose of this code:

while (!objThread.IsAlive)
{
Thread.Sleep(1);
objThread.Abort();
objThread.Join();
}

Your thread should always be alive, and if you were waiting for the
process to kick off, you would end up sleeping for a second then aborting
it. It doesn't make sense. If you want to wait for the thread to kick
off, then use a ManualResetEvent and wait on it, and set it in the first
line of your method that is called on the new thread.

Also, if you bind to dsLocal.Tables[0], the data grid will
automatically bind to the DefaultView property on that table, it never
binds to the table directly.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Elliot Rodriguez said:
Hi:

I am writing a WinForm app that contains a DataGrid control and a
StatusBar control. My goal is to update the status bar using events from
a separate class, as well as some other simple things.

The method I am writing queries a large dataset. As part of my feedback
to the user, I am updating the status bar when the connection is made and
the dataset is actually retrieved. The dataset retrieval method I have
placed on a separate thread so that the status bar updates can be viewed
by the user.

Everything works except for the actual binding of my dataset to the grid
control. when I attempt to bind my dataset, i get an error along the
lines of "cannot make control on parent thread of another control" or
something to that effect (I'm sorry, I dont have the actual text handy,
and the query takes a while so forgive the paraphrasing.). I am new to
writing.NET threading code, and would prefer to learn in this instance
how it works and how I broke it rather than copy and paste code, so can
anyone please take a sec and view below and tell me what I am missing?
Some of the code I took from the MSDN's Threading tutorial.

private void mnuGetDocuments_Click(object sender, System.EventArgs e)

{

if (MessageBox.Show(this, "Retrieve list of documents waiting for
printing?", "Confirm Queue Retrieval",
MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

// my events, QueueDocs is object outside of function scope.

QueueDocs.ConnectionEstablished += new
ConnectionMadeHandler(UpdateStatusOnConnection);

QueueDocs.DatasetReceived += new
DatasetReceivedHandler(UpdateStatusOnDataset);

this.sbOperationStatus.Text = "Retrieving queues... 0% complete";

// instance our new thread

Thread objThread = new Thread(new ThreadStart(this.GetDocuments));


objThread.Start();

while (!objThread.IsAlive)

{

Thread.Sleep(1);

objThread.Abort();

objThread.Join();

}

}

}



private void GetDocuments()

{

DataSet dsLocal = QueueDocs.RetrievePrintQueues();

// breaks here, on binding.

this.dgQueueDocuments.DataSource = dsLocal.Tables[0].DefaultView;

}



Thank you,
Elliot Rodriguez
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top