开发者

WPF and backgroundworker problem

开发者 https://www.devze.com 2023-02-03 09:22 出处:网络
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 Bit

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!


BitmapImage is Freezable so you can Freeze() it after loading it. This will permit access from any thread.


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);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号