WPF и проблема фонового рабочего - PullRequest
2 голосов
/ 06 января 2011

В моей программе есть класс BackgroundWorker, который предварительно загружает изображения в объект BitmapImage. Мне нужно передать это предварительно загруженное изображение в основное приложение (WPF), где оно будет скопировано в другой объект BitmapImage. Это, кажется, работает, когда я пытаюсь

imgViewer.Source = imgNext;  //imgNext is a main app copy of the preloaded image

возникает ошибка, означающая, что этот объект (imgNext) принадлежит другому потоку и его нельзя использовать.

Есть идеи, как избавиться от этого и заставить работать код?

Спасибо всем за ответы!

На самом деле мне удалось решить эту проблему, создав статический BitmapImage внутри App класса. Перед его использованием я делаю

 App.iNext = null;

Затем я загружаю фактическое изображение и замораживаю его, чтобы к этому статическому свойству можно было получить доступ из любого места. Когда цикл повторяется много раз, присвоение нуля предотвращает ошибки «объект заморожен».

И, конечно, было много работы по управлению одним экземпляром BGW, задачам очередей и т. Д.

(В настоящее время я использую класс ImagesContainer, определенный также в моей программе, который имеет два свойства BitmapImage. Я использую его для получения предварительно загруженных изображений от backgroundworker.)

imgNext - публичная переменная, определенная в MainWindow. (основная тема)

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

Замораживание растрового изображения помогает только при первой фоновой загрузке (см. Комментарий под ответом ниже). Когда я вызываю BackgroundWorker во второй раз, возникает ошибка, что объект заморожен и не может быть изменен. Есть ли способ разморозить его?

Или есть ли способ скопировать данные из одного потока в другой, не копируя указание на поток?



ОБНОВЛЕНО

Спасибо всем за ответы!

На самом деле мне удалось решить эту проблему, создав статический BitmapImage внутри App класса. Перед его использованием я делаю

 App.iNext = null;

Затем я загружаю фактическое изображение и замораживаю его, чтобы к этому статическому свойству можно было получить доступ отовсюду. Когда цикл повторяется много раз, присвоение нуля предотвращает ошибки.

И, конечно, было много работы по управлению отдельным экземпляром BGW, задачам очередей и т. Д.

Но эти усилия стоили результата - я получил + 125% производительности !!! Спасибо всем!

Ответы [ 2 ]

6 голосов
/ 06 января 2011

BitmapImage равно Freezable, поэтому вы можете Freeze() после загрузки.Это разрешит доступ из любого потока.

1 голос
/ 07 января 2011

Легче всего создать все объекты пользовательского интерфейса в одном потоке. Это включает в себя любые классы, начинающиеся с DispatcherObject, например BitmapImage.

В потоке пользовательского интерфейса - перед созданием BGW - захватить результат TaskScheduler.FromCurrentSynchronizationContext. Вы можете прикрепить это в частном члене своего класса. e.g.:

private TaskScheduler ui;
public void InitiateBGW()
{
  this.ui = TaskScheduler.FromCurrentSynchronizationContext();
  this.bwImgLoader.RunWorkerAsync();
}

На BGW, когда вам нужно получить доступ к функциональности BitmapImage (создание или изменение их), поставьте ее в очередь на TaskScheduler следующим образом:

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...