Обновление свойства пользовательского интерфейса изображения из потока BackgroundWorker - PullRequest
2 голосов
/ 13 сентября 2010

В приложении WPF, которое я пишу, у меня есть свойство TransformedBitmap, которое связано с объектом Image в пользовательском интерфейсе.Всякий раз, когда я изменяю это свойство, изображение обновляется (и, таким образом, изображение, отображаемое на экране, обновляется).Чтобы пользовательский интерфейс не зависал или не отвечал на запросы во время получения следующего изображения, я пытаюсь получить снимок с помощью BackgroundWorker, например:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
  e.Result = this.snapshotHelper.GetSnapshot(ImageFormat.Bmp);
}

, затем в моем методе RunWorkerCompleted яследующее:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  this.CurrentImage = (TransformedBitmap)e.Result;
  ....
}

Кажется, что это работает нормально до тех пор, пока не будет использован метод NotifyPropertyChanged, используемый для указания объекту Image обновляться при обновлении свойства CurrentImage;Я получаю ошибку в нескольких потоках.

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
  if (PropertyChanged != null)
  {
  //The following causes a "the calling thread cannot access this object because a different thread owns it" error!
    PropertyChanged(this, new PropertyChangedEventArgs(info));      
  }
}

Я действительно не знаю, как что-то изменить или что сделать по-другому, чтобы обойти эту ошибку.Последние пару часов я читал о BackgroundWorkers, и мне кажется, что у меня должна быть возможность правильно настроить CurrentImage в методе RunWorkerCompleted;по крайней мере из того, что я могу сказать.Любая помощь по этому вопросу будет принята с благодарностью!Спасибо!

Ответы [ 3 ]

2 голосов
/ 13 сентября 2010

Элемент управления (изображение) может быть изменен только в потоке, который его создал. По сути, происходит то, что ваш фоновый поток изменяет свойство вашего объекта, что, в свою очередь, вызывает событие PropertyChanged, которое затем используется WPF, который затем пытается изменить элемент управления Image (помните, что мы все еще находимся на BackgroundThread в этой цепочке событий).

К счастью, исправление довольно простое:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  myWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() 
     {
        this.CurrentImage = (TransformedBitmap)e.Result;
        ....
     });
}

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

2 голосов
/ 13 сентября 2010
Dispatcher.Invoke((Action<TransformedBitmap>) (obj => this.CurrentImage = obj), e.Result as TransformedBitmap);

Это должно работать ...

Обновление

В вашем случае вы используете объект freezable, и проблема заключается в том, что созданный вами растровый файл необходимо заморозить перед отправкой в ​​потоке пользовательского интерфейса. Так что у вас DoWork будет выглядеть следующим образом:

void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var bmp = snapshotHelper.GetSnapshot(ImageFormat.Bmp);
            bmp.Freeze();
            e.Result = bmp;            
        }

Затем в RunWorkerCompleted вы обновляете свойство, как я писал выше.

0 голосов
/ 13 сентября 2010

Только диспетчеру разрешено взаимодействовать с пользовательским интерфейсом.Посмотрите здесь:

http://www.jeff.wilcox.name/2010/04/propertychangedbase-crossthread/

...