Converter Uri to BitmapImage - загрузка изображения из Интернета

У меня проблема с конвертером из Uri в BitmapImage. Uri - это URL-адрес изображения в Интернете. Я использую этот конвертер для элемента в списке.

Я загружаю изображение с веб-страницы и создаю из этого потока BitampImage

Проблема в том, что если список содержит около 100–250 элементов, приложение зависает, я пытаюсь вызвать WebRequestMethod в другом потоке, но он не работает.

Вот корневая часть кода:

private static BitmapImage GetImgFromAzet(int sex, Uri imgUri)
{
    try
    {
        if (imgUri == null)
        {
            if (sex == (int)Sex.Man)
            {
                return new BitmapImage(new Uri(@"pack://application:,,,/Spirit;Component/images/DefaultAvatars/man.jpg",
                        UriKind.RelativeOrAbsolute));
            }
            else
            {
                return new BitmapImage(new Uri(@"pack://application:,,,/Spirit;Component/images/DefaultAvatars/woman.jpg",
                        UriKind.RelativeOrAbsolute));
            }
        }
        else
        {
            BitmapImage image = null;

            Task.Factory.StartNew(() =>
              {
                  WebRequest webRequest = WebRequest.CreateDefault(imgUri);
                  webRequest.ContentType = "image/jpeg";
                  WebResponse webResponse = webRequest.GetResponse();

                  image = new BitmapImage();
                  image.CreateOptions = BitmapCreateOptions.None;
                  image.CacheOption = BitmapCacheOption.OnLoad;
                  image.BeginInit();
                  image.StreamSource = webResponse.GetResponseStream();
                  image.EndInit();
                  return image;

                  //((System.Action)(() =>
                  //{


                  //    //webResponse.Close();

                  //})).OnUIThread();

              });

            return image;
        }
    }
    catch (Exception)
    {

// по умолчанию return new BitmapImage (новый Uri (PokecUrl.Avatar, UriKind.RelativeOrAbsolute)); }}

Моя цель - загрузить изображение из Интернета, создать из него объект BitamImage и вернуться в качестве элемента управления Source of Image, но мне нужно избежать зависания приложения. Также проблема в том, что если я закрою webResponse, он сломает весь код.

ИЗМЕНИТЬ:

Я пробую это:

BitmapImage image;
WebRequest req = WebRequest.CreateDefault(imgUri);
req.ContentType = "image/jpeg";

using (var res = req.GetResponse())
{
        image = new BitmapImage();
        image.CreateOptions = BitmapCreateOptions.None;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.BeginInit();
        image.UriSource = imgUri;
        image.StreamSource = res.GetResponseStream();
        image.EndInit();
}

но где-то должна быть ошибка, код битый.

Любой совет?


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


Ответы (1)


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

Например, чтобы решить эту проблему, вы можете использовать Свойство Binding.IsAsync:

public class ListItemViewData
{
   private readonly Uri _uri;
   private readonly Sex _sex; 

   ListItemViewData(Uri uri, Sex sex)
   {
      this._uri = uri;
      this._sex = sex;
   }

   public BitmapSource Image
   {
       get
       {
           // Do synchronous WebRequest 
       }
   }
}

Использование в xaml (внутри DataTemplate элемента списка):

<Image Source="{Binding Path=Image, IsAsync=True}"/>

ИЗМЕНЕНО

Я погрузился в класс BitmapImage и обнаружил, что у него есть симпатичный ctor с параметром Uri, который работает асинхронно.

Так что вам не следует выполнять WebRequest самостоятельно. Сделайте так:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var uri = (Uri)value;
    return new BitmapImage(uri) { CacheOption = BitmapCacheOption.None };
} 

ИЗМЕНИТЬ 2

Ваш класс данных представления.

public class ListItemViewData : INotifyPropertyChanged
{
    public ListItemViewData(Uri uri)
    {
        this._uri = uri;
    }

private readonly Uri _uri;
public Uri Uri
{
    get 
    {
        return this._uri; 
    }
}

private BitmapSource _source = null;
public BitmapSource Image
{
    get
    {
        return this._source;
    }
    set 
    {
        this._source = value;
        this.OnPropertyChanged("Image");
    }
}

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string p)
{
    var pc = this.PropertyChanged;
    if (pc!=null)
    { 
        pc(this, new PropertyChangedEventArgs(p));
    }
}
}

Помощник, выполняющий загрузку изображений:

public static class WebHelper
{
    public static Stream DownloadImage(Uri uri, string savePath)
    {
        var request = WebRequest.Create(uri);
        var response = request.GetResponse();
        using (var stream = response.GetResponseStream())
        {
            Byte[] buffer = new Byte[response.ContentLength];
            int offset = 0, actuallyRead = 0;
            do
            {
                actuallyRead = stream.Read(buffer, offset, buffer.Length - offset);
                offset += actuallyRead;
            }
            while (actuallyRead > 0);
            File.WriteAllBytes(savePath, buffer);
            return new MemoryStream(buffer);
        }
    }   
}

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

this._listItems.Add(new ListItemViewData(new Uri(@"http://lifeboat.com/images/blue.ocean.jpg")));
//...
var sc = SynchronizationContext.Current;
new Thread(() =>
{
    foreach (var item in this._listItems)
    { 
        var path = "c:\\folder\\"+item.Uri.Segments.Last();
        var stream = WebHelper.DownloadImage(item.Uri, path);

        sc.Send(p =>
            {
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = (Stream)p;
                bi.EndInit();
                item.Image = bi;
            }, stream);
    }
}).Start();
person Alex Zhevzhik    schedule 10.02.2011
comment
Привет, спасибо за обратную связь, я пытаюсь использовать асинхронную привязку, но приложение все еще зависает, проблема должна быть в том, что изображения загружаются с веб-сайта, например, в списке есть 200 элементов, а конвертер должен создать 200 запросов на веб-сервере - ›на мой взгляд, это root проблемы. Что ты думаешь? - person ; 10.02.2011
comment
Я запутался, моя цель - загрузить изображение из Интернета и создать из него BitmapImage. Что-то вроде этого stackoverflow.com/ вопросы / 2097152 /. Создайте экземпляр BitmapImage из MemoryStream. - person ; 11.02.2011
comment
Спасибо, господин Жевжик, я пытаюсь реализовать ваше решение и считаю, что оно решает мою проблему. - person ; 13.02.2011