crossthread error when using backgroundWorker

R

Rich P

I have a simple app that downloads pictures from an ftp server and also
has a user control which is just a blinking arrow. The user control was
hanging during the download process, so I added a backgroundworker
control to the app. This solved the user control haning problem, but I
have another problem now - a crossthread error because I was displaying
each fileName being downloaded on a label on the form. I commented out
that part of the download process and it runs OK.

How can I display the filenames from my list on the label on the form in
the BackgroundWorker_DoWork event without getting a crossthread error?

Do I need to use

if (InvokeRequired)
Invoke(new Change(OnChange), i); //how to use this?



Any suggestions appreciated.

Thanks

Rich
 
J

Jesse Houwing

* Rich P wrote, On 10-9-2009 23:24:
I have a simple app that downloads pictures from an ftp server and also
has a user control which is just a blinking arrow. The user control was
hanging during the download process, so I added a backgroundworker
control to the app. This solved the user control haning problem, but I
have another problem now - a crossthread error because I was displaying
each fileName being downloaded on a label on the form. I commented out
that part of the download process and it runs OK.

How can I display the filenames from my list on the label on the form in
the BackgroundWorker_DoWork event without getting a crossthread error?

Do I need to use

if (InvokeRequired)
Invoke(new Change(OnChange), i); //how to use this?



Any suggestions appreciated.

Yes, you need to use InvodeRequired.

Create a method to update the label like this:

class TestForm : Form
{
Label label;

delegate void TextUpdate(string text);
public void UpdateLabel(string text)
{
if (InvokeRequired)
{
Invoke(new TextUpdate(UpdateLabel), text);
}
else
{
label.Text = text;
}
}
}
 
R

Rich P

Amazing! I just copied your code into my project and made a call to
UpdateLabel(...) from BackgroundWorker_DoWork(...) and it worked!

For my own edification - may I ask if I am seeing what is happening - if
I see it correctly?

public void UpdateLabel(string text)
{
if (InvokeRequired)
{
Invoke(new TextUpdate(UpdateLabel), text);
}
else
{
label.Text = text;
}
}

BgWkr calls UpdateLabel - Invoke is required = true, so it recursively
calls itself and goes to the "else" part on the 2nd call and returns
back to BgWkr in my for next loop?

Thanks for the help.



Rich
 
P

Peter Duniho

[...]
Do I need to use

if (InvokeRequired)
Invoke(new Change(OnChange), i); //how to use this?



Any suggestions appreciated.

Yes, you need to use InvodeRequired.

Create a method to update the label like this:

class TestForm : Form
{
Label label;

delegate void TextUpdate(string text);
public void UpdateLabel(string text)
{
if (InvokeRequired)
{
Invoke(new TextUpdate(UpdateLabel), text);
}
else
{
label.Text = text;
}
}
}

I'll inject my standard "InvokeRequired is a waste of time" comment here.
Just write it like this:

public void UpdateLabel(string text);
{
Invoke((MethodInvoker)delegate
{
label.Text = text;
});
}

But really, given that the OP is using BackgroundWorker, the use of
Invoke() is unnecessary anyway. The whole point of BackgroundWorker is so
that one can avoid dealing with the mechanics of cross-thread
invocations. The right way to handle this is for the form code to
subscribe to the BackgroundWorker's ProgressChanged event, and for the
BackgroundWorker's DoWork event handler to call ReportProgress() whenever
it wants to notify the form code of progress (e.g. a new filename being
processed).

BackgroundWorker will correctly handle the cross-thread issue; the
ReportProgress() call is made on the worker thread, but ProgressChanged
event handler will be executed on the same thread that created the
BackgroundWorker (assuming that thread was a GUI thread, which it should
be).

Pete
 
R

Rich P

The right way to handle this is for the form code to
subscribe to the BackgroundWorker's ProgressChanged event, and for the
BackgroundWorker's DoWork event handler to call ReportProgress()
whenever it wants to notify the form code of progress (e.g. a new
filename being processed).
<<

how would I set this up? How do I call the ProgressChanged event? I
was actually thinking about that as an option but not familiar enough at
this time to consider it.

...BackgrounWorkder_DoWork(...)
{
...
--how to announce that fileX.jpg is being downloaded?
--how to call ProgressChanged event? how to notify form thread?
//download fileX.jpg
...
}


Rich
 
P

Peter Duniho

The right way to handle this is for the form code to
subscribe to the BackgroundWorker's ProgressChanged event, and for the
BackgroundWorker's DoWork event handler to call ReportProgress()
whenever it wants to notify the form code of progress (e.g. a new
filename being processed).
<<

how would I set this up? How do I call the ProgressChanged event?

As I wrote, when you call the ReportProgress() method, the ProgressChanged
event is raised.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx
http://msdn.microsoft.com/en-us/library/a3zbdb1t.aspx
I was actually thinking about that as an option but not familiar
enough at this time to consider it.

The documentation does go into some detail and includes examples as to how
to use the ProgressChanged event. If after reading the documentation you
have specific questions about what you've read and how to use the class,
please do feel to post them here and we'll do our best to help.

Pete
 
R

Rich P

OK. Thank you very much. I got this going. Wasn't that painful. But
the UpdateLabel method with the delegate was also kind of interesting to
me because I can sort of see some delegate usage - how to use a
delegate. But the ProgressChanged event is native to the
BackgroundWorker, and it seems to do the trick.


Rich
 
T

Tom Spink

Rich said:
OK. Thank you very much. I got this going. Wasn't that painful. But
the UpdateLabel method with the delegate was also kind of interesting to
me because I can sort of see some delegate usage - how to use a
delegate. But the ProgressChanged event is native to the
BackgroundWorker, and it seems to do the trick.


Rich

I'd also heed Pete's advice about streamlining the call to Invoke, and
the code being invoked - should you need this technique in the future
(if you're not using a BackgroundWorker, say).

There are a bajillion different techniques that people have come up with
out there to handle this sort of scenario, or make some sort of
rudimentary design pattern - but Pete's is by far one of the best.
 

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