Конвертер Uri в BitmapImage - загрузка изображения из сети - PullRequest
1 голос
/ 09 февраля 2011

У меня проблема с конвертером из 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)
    {

// по умолчанию вернуть новый BitmapImage (новый Uri (PokecUrl.Avatar, UriKind.RelativeOrAbsolute)); } } * +1010 *

Моя цель - загрузить изображение из Интернета, создать из него объект BitamImage и вернуть его в качестве источника управления изображением, но мне нужно избегать зависания приложения. Также проблема в том, что если я закрою 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();
}

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

Любой совет?

1 Ответ

3 голосов
/ 10 февраля 2011

Преобразователь привязки всегда выполняется в потоке пользовательского интерфейса.Таким образом, вы можете запустить другой поток в методе 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}"/>

EDITED

Я нырнул в класс 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 };
} 

EDITED 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();
...