views:

169

answers:

3

I have UI WPF application.

May by some one have any ideas why this code is not working?

Case 1:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
   //some logic
};

worker.RunWorkerAsync();

In that case i am getting exception The calling thread cannot access this object because a different thread owns it. Then i changed it to this:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate 
{ 
  this.Dispatcher.BeginInvoke(
    new Action(() =>  { //my code here }), null); 
}; 

After that UI getting frozen during executing this code. Like it is executing in the same thread

Case 2:

BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerAsync(new Action(() =>
{
 //some code here
}));

In that case code inside Action not executed.

//----------------------------------UPDATED--------------------------------------//
Thank you guy's for help The reason why my code isn't worked propally is mentioned below, i did access to some UI elements in bakground thread. Now i am getting all the values from UI elements before to call BackgroundWorker. I am declared new variables, assign them all values from UI elements i need and then i am passing this variables to BackgroundWorker instead of UI elements like it was before.

A: 

The second version won't do what you want, because the parameter to the overload of RunWorkerAsync which takes a parameter is just meant to be the value for DoWorkEventArgs.Argument. It's not meant to be the action to be executed - unless you provide an event handler which casts the value into an Action and calls it...

The first version should work - but as per Oded's comment, you haven't given any indication of what you mean by "not working"... nor have you specified whether both versions are failing or only one.

Jon Skeet
+1  A: 

The problem with the first version is that inside "some logic" you are probably accessing WPF objects - you can't do that, WPF objects can only be accessed from the same thread that created them.

The problem with the second version is that you are starting a background thread that asks the main thread to do all the work and then exits, so you are doing all the work on the main thread (that also does all the UI work) and the UI freezes, essentially this is equivalent to not using a BackgroundWorker at all.

The third version, as Jon Skeet said, is simple incorrect usage and isn't supposed to work like you think it is.

So, what do you need to do?

You need to collect all the information from the UI in the main thread before starting the background worker, you can use only simple types (string, int, double, etc.) and thread-safe classes/structs, you can not use any WPF classes in the code executed by the BackgroundWorker.

After you finished collecting all the data you can call RunWorkerAsync, in you DoWork handler you can not read data from the UI - you can only access the data you prepared before, also you can not write into the UI - you have to save it somewhere else (class member for example) and copy it into the UI after the BackgroundWorker is finished.

The only exception to the "can't access WPF from another thread" rule is Freezable (and all classes that inherit from freezable) after the Freeze method is called, this makes the object read-only and thread-safe.

Nir
@Nir Thank you Nir for very detailed explanation. Now i understand my mistake.
Juk
A: 

Another thing to be very wary of: if you're setting DoWork to an anonymous method, make sure you're not creating a closure over objects in the calling method. Those objects are on the calling method's thread, and if the DoWork method touches them, it is a Bad Thing.

A simple example:

MyClass foo = new MyClass();
worker.DoWork += delegate
{
   foo.MyProperty++;
};

I use anonymous methods pretty sparingly in general. Closures create all kinds of problems if you're not using them intentionally. And if you're using them intentionally, it's easy to end up writing code that's a little too subtle to modify a year down the line. Writing a named method with explicit parameters may take a little more time and code, but it's no harder than documenting what you're doing with a closure.

I don't use anonymous methods at all with the BackgroundWorker. Not only is there the risk that you'll rely on a closure without really thinking about all of the implications of it, but you've also very probably written code that can't be unit tested.

Robert Rossney