Reputation: 1191
In my program there is a BackgroundWorker class that preloads the images to BitmapImage object. I need to pass that preloaded image to the main application(WPF), where it will be copied to another BitmapImage object. This seems to be working, however, when i try
imgViewer.Source = imgNext; //imgNext is a main app copy of the preloaded image
an error occurs meaning that that object(imgNext) is owned by another thread and it cannot be used.
Any ideas how to get rid of it and get the code working?
Thanks everybody for answering!
In fact, I managed to solve this problem by creating a static BitmapImage
inside App
class.
Before using it, I do
App.iNext = null;
Then I load the actual image and freeze it, so this static property can be accessed from everywhere. When the cycle repeats many times, assigning null prevents 'object is frozen' errors.
And of course, there was a lot of work with managing single BGW instance, queuing tasks etc.
(Currently I'm using ImagesContainer class defined also in my program that has two BitmapImage properties. I use it to receive preloaded images from backgroundworker. )
imgNext is a public variable defined in MainWindow. (main thread)
void bwImgLoader_DoWork(object sender, DoWorkEventArgs e)
{
backgrLoadNextPrevList list = e.Argument as backgrLoadNextPrevList;
ImagesContainer result = new ImagesContainer();
if (list.HasNextPath) result.imgPrev = PrepareImage(list.NextPath);
if (list.HasPrevPath) result.imgNext = PrepareImage(list.PrevPath);
e.Result = result;
}
void bwImgLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ImagesContainer result = e.Result as ImagesContainer;
if (result.imgNext != null)
{
setNextDelegate s = new setNextDelegate(setNext);
object[] t = { result.imgNext };
imgNext.Dispatcher.Invoke(s, t);
}
// do not take into account this line, just ignore it.
//if (result.imgPrev != null) imgPrev = result.imgPrev;
}
public void setNext(BitmapImage b)
{
imgNext = b;
}
public delegate void setNextDelegate(BitmapImage b);
Freezing the bitmapimage helps only on the first background load(see the comment under the answer below). When I call BackgroundWorker second time, an errors occures that the object is frozen and cannot be modified. Is there a way to un-freeze it?
Or, is there any way to copy data from one thread to another without copying an attribution to thread?
UPDATED
Thanks everybody for answering!
In fact, I managed to solve this problem by creating a static BitmapImage
inside App
class.
Before using it, I do
App.iNext = null;
Then I load the actual image and freeze it, so this static property can be accessed from everywhere. When the cycle repeats many times, assigning null prevents errors.
And of course, there was a lot of work with managing single BGW instance, queuing tasks etc.
But these efforts were worth the result - I got +125% in performance!!! Thank everyone!
Upvotes: 2
Views: 1243
Reputation: 178630
BitmapImage
is Freezable
so you can Freeze()
it after loading it. This will permit access from any thread.
Upvotes: 6
Reputation: 456322
It's easiest to create all UI objects on the same thread. This includes any classes descending from DispatcherObject
, such as BitmapImage
.
On the UI thread - before creating the BGW - capture the result of TaskScheduler.FromCurrentSynchronizationContext
. You can stick it in a private member of your class. e.g.:
private TaskScheduler ui;
public void InitiateBGW()
{
this.ui = TaskScheduler.FromCurrentSynchronizationContext();
this.bwImgLoader.RunWorkerAsync();
}
On the BGW, whenever you need to access BitmapImage
functionality (creating them or modifying them), queue it to the TaskScheduler
like this:
private BitmapImage PrepareImage(string path)
{
// This code runs in a BGW.
// Load underlying bitmap (non-UI)...
var bitmap = ..;
// Prepare the bitmap (non-UI)...
return Task.Factory.StartNew(() =>
{
var ret = new BitmapImage();
// Load "bitmap" into "ret"
return ret;
}, CancellationToken.None, TaskCreationOptions.None, this.ui);
}
Upvotes: 1