Загрузка изображения в теме с помощью WPF - PullRequest
1 голос
/ 16 ноября 2009

Я пытаюсь создать список, отображающий картинки из интернета. Элементы предоставляются путем привязки источника данных к модели, которая содержит URL-адрес изображения и некоторые другие свойства (title, desc и т. Д.).

К сожалению, список загружается очень медленно, потому что WPF пытается загрузить все изображения из Интернета, прежде чем показывать список, и заставляет приложение зависать на 15-25 секунд.

Я читал, что мне нужно загрузить картинку в другой ветке, но я не знаю, где мне это делать и как? Лучше загружать все изображения непосредственно в модель (создавая пул потоков только для этого - но проблема в том, что это на самом деле не является частью представления модели / модели) или лучше создать фоновый поток, который будет обновлять непосредственно список когда у него есть данные?

Спасибо!

Ответы [ 4 ]

7 голосов
/ 16 ноября 2009

Самый простой способ - просто установить свойство Binding.IsAsync следующим образом:

<Image ImageSource="{Binding propertyThatComputesImageSource, IsAsync=true}" />

Каждый доступ к propertyThatComputesImageSource будет осуществляться из потока ThreadPool. Если поток создает изображение с помощью ImageCacheOptions.OnLoad, он будет блокироваться до тех пор, пока изображение не будет загружено. Таким образом, пользовательский интерфейс сразу запустится, и изображения будут загружены в фоновом режиме и появятся, когда они станут доступны.

Binding.IsAsync - это хорошее решение для десяти или двадцати изображений, но, вероятно, это не очень хорошее решение, если у вас есть сотни изображений и задержка загрузки велика, так как вы можете получить сотни потоков. В этом случае загрузите изображения вне привязки данных полностью, используя ThreadPool напрямую:

ThreadPool.QueueUserWorkItem((state) =>
{
  foreach(var model in _models.ToArray())
    model.ImageSource = LoadOneImage(model.ImageUrl);
});

Это может потребоваться расширить с помощью Dispatcher.Invoke или двух, если свойствами модели являются DependencyProperty, поскольку к ним нельзя получить доступ из отдельного потока.

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

0 голосов
/ 16 ноября 2009

Спасибо всем вам!

Все решения должны работать :) В моем случае использование IsAsync на изображении ListBoxItem достаточно хорошо (не более 50 элементов). На самом деле, это не получение изображения из сети, которая занимала слишком много времени!

К сожалению, моя проблема была где-то еще ... Это связано с ошибкой с обнаружением прокси в .NET 3.5, из-за которой приложение загружалось очень медленно:

Если в папке приложения нет файла your_application_name.exe.config со следующим кодом - .NET может потребоваться много времени для обнаружения прокси, который останавливает приложение при первом обращении к сети:

<configuration>   
  <system.net>
    <defaultProxy enabled="false"/>   
  </system.net> 
</configuration>
0 голосов
/ 16 ноября 2009

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

Для примера того, как написать такую ​​ветку, взгляните на документацию BackgroundWorker .

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

0 голосов
/ 16 ноября 2009

Очень простой подход заключается в использовании System.ComponentModel.BackgroundWorker ( больше информации ) в модели представления. Вот тривиальный пример:

using (BackgroundWorker bg = new BackgroundWorker())
{
    bg.DoWork += (sender, args) => FetchImages(viewModelObjectsNeedingImages);
    bg.RunWorkerAsync();
}

BackgroundWorker также очень удобно отменять фоновое задание.

Возможно, вы также захотите взглянуть на Виртуализация пользовательского интерфейса .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...