Как получить доступ к объекту WPF в диспетчере?

Я использую шаблон MVVM для разработки приложения WPF.

Приложение загружает изображение captcha с сервера и назначает его Image в форме WPF, когда оно будет готово. Я использую BackgroundWorker для потоковой передачи, а именно:

Когда окно загружается, вызывается следующее:

BackgroundWorker _bgWorker = new BackgroundWorker();

_bgWorker.DoWork += GetCaptchaImage;
_bgWorker.RunWorkerAsync();

Функция GetCaptchaImage довольно проста, загружая изображение в другой поток:

BitmapSource _tempBitmap = GetCaptchaFromServer();

Мне нужно знать, как вызвать диспетчер, чтобы назначить этот ImageSource источнику изображения моего окна. В настоящее время я вызываю диспетчер после загрузки _tempBitmap следующим образом:

Application.Current.Dispatcher.Invoke(
new Action(() => CaptchaBitmap = _tempBitmap));

Где CaptchaBitmap привязан к моему источнику изображения.

Однако, когда я это делаю, выдается исключение InvalidOperationException, и любая ссылка на _tempBitmap возвращает ошибку в потоке графического интерфейса. Я знаю это, потому что обращаюсь к нему из потока графического интерфейса диспетчера, когда он был создан в потоке BackgroundWorker, но как мне обойти это?

Помощь будет принята с благодарностью! :)


person Community    schedule 04.08.2009    source источник


Ответы (4)


Просто вызовите BitmapSource.Freeze перед вызовом Dispatcher.Invoke

BitmapSource _tempBitmap = GetCaptchaFromServer();
_tempBitmap.Freeze();
Application.Current.Dispatcher.Invoke(
new Action(() => CaptchaBitmap = _tempBitmap));

Ко всем объектам WPF можно получить доступ только из потока, который их создал, за исключением Dispatcher (по понятным причинам) и Freezable после вызова метода Freeze.

После вызова Freeze объект может быть доступен из любого потока (но не может быть изменен), к счастью для вас BitmapSource наследуется от Freezable.

person Nir    schedule 04.08.2009
comment
Последний час рвал волосы из-за этой проблемы. Большое спасибо! - person Jamie Love; 04.08.2009

У меня была такая же проблема WPF: передача объектов между потоком пользовательского интерфейса и фоном ветка Так и не понял, что такое "правильное" решение. Закончился заменой моего BackgroundWorker на DispatcherTimer, который запускался только один раз, и это сработало.

person Sharun    schedule 04.08.2009

Просто из любопытства ... Почему бы вам не выполнить все извлечение и установку изображения в потоке Dispatcher вместо класса BackgroundWorker?

Dispatcher.Invoke(DispatcherPriority.Background,
    new Action(() => { CaptchaBitmap = GetCaptchaFromServer(); } )
);
person Arcturus    schedule 04.08.2009

@Arcturus, по моему опыту, если вы это сделаете, вы не получите никакой реакции пользовательского интерфейса, пока загрузка с сервера не будет завершена ... потому что вы фактически прерываете поток пользовательского интерфейса и ТОГДА загружаете материал ... я делал эта ошибка в паре моих проектов и вопрос, почему пользовательский интерфейс не отвечает ....

person Community    schedule 05.08.2009
comment
Да, это правда ... вот почему я обычно делаю работу в фоновом потоке, чтобы поток пользовательского интерфейса привлекал наибольшее внимание и постоянно обновлялся .. - person Arcturus; 05.08.2009