Monday, April 5, 2010

BackgroundWorker Component Questions

I recently received an e-mail with a few questions regarding the BackgroundWorker Component (original article).  I decided to share the answers here.

Page Navigation with the BackgroundWorker
Question: I want to navigate to another page in the BackgroundWorker thread -- for example: "frameNavigator.Navigate(new OtherPage());" in the DoWork method.  I tried this, but an exception occurred because the Page class uses the main thread whereas the BackgroundWorker uses a separate thread.  How can I get around this?

Answer:  As you noticed, things like Pages, Winodws and ListBoxes are all items on the UI thread (and they need to stay on the UI thread).  And the BackgroundWorker component processes items on a different thread (not the UI thread).  So, you will not be able to do things like "Navigate" in the DoWork method.

But here's what you should do: try to figure out why the navigation process is slow.  This is usually due to something that happens when your new Page loads -- it could be loading data from a database, transferring a file across the network, or calling a web service.  So, even though you cannot put the "Navigate" into the background, you may be able to take whatever is in your Page Load process and put that in the background.  In this type of scenario, you could put the database call method or the file transfer method into the background, not the entire Page object.  This will have the effect of keeping your UI thread running and responsive while your data loads in the background.

Using the Progress Bar
Question: I want to get the progress completed from the actual process time, not from a predetermined time as you used in the walkthrough.  The application must know how much time the BackgroundWorker process will take to navigate to the target page and then show a progress bar based on this information.  How can I accomplish this?

Answer: Progress is a tricky subject.  In the example in my walkthrough, I am not technically doing the progress bar based on a predetermined time; I am calculating the progress (a percentage) based on the number of iterations completed in the loop.  I purposely kept my background process simple so that I could focus on the methods, events and properties of the BackgroundWorker component itself.  But you can use the progress event for anything you can calculate a percentage complete for.  For a more complex example, you can look at the BackgroundWorker examples on MSDN: http://msdn.microsoft.com/en-us/library/c8dcext2.aspx.  One in particular, calculates a Fibonacci sequence using a recursive method call and calculates a percentage for a progress bar.

As an example of a calculation: if you are doing a file transfer, you can calculate a percentage based on the number of bytes received compared to the total number of bytes.  So, if you have received 257 bytes of a 1,356 byte file, you are 19% complete.  The limitation is that you have to know how far along you are in the process.  If you are doing something like making a database call or a web service call, you may not know how long it will take to complete or how much data you are getting back.  In those situations, you cannot calculate a percentage and so you may be better off using a "busy animation" (and there are plenty of examples available on the web for WPF and Silverlight).

Complex Results
Question: I don't like examples of BackgroundWorker using results of "string" or "int".  I want a real object.  How would I do this?

Answer: The e.Result parameter in the DoWork and RunWorkerCompleted can be as simple or as complex as you like.  In my example, I simply use an "int", but e.Result is of type "object", so you can put whatever you want in there (including an entire database result set).  There are a few limitations such as the objects that need to be on the UI thread (like we mentioned above).  As another option, you can use a separate variable (or set of variables) that is accessible to both the UI and the background process.  If you do this, you will want to look into the "lock" method to ensure that you don't end up with multiple processes trying to modify the value at the same time.

Round Up
The BackgroundWorker component is not the right solution for everything.  But it can be helpful in quite a few situations.  As we saw above, you may not be able to do exactly what you want (such as navigating in the background), but if you think about the problem a little differently, you can often come up with a workable solution.

Happy Coding!

No comments:

Post a Comment