Как я могу сохранить изображение WPF от блокировки, если ImageSource ссылается на недостижимый URL-адрес? - PullRequest
7 голосов
/ 17 марта 2010

Я пишу приложение WPF и пытаюсь привязать изображение к моей модели представления с помощью следующего XAML:

<Image Source="{Binding Author.IconUrl, IsAsync=True}" />

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

Я думал, что свойство привязки "IsAsync" приведет к загрузке в фоновом режиме, но, похоже, разрешение DNS все еще может происходить в основном потоке?

Что я могу сделать, чтобы мое приложение не блокировалось, даже если изображения недоступны?

Спасибо, Corey

Ответы [ 2 ]

10 голосов
/ 18 марта 2010

Вот новый ответ для вас, надеюсь, лучше, чем мой предыдущий.

Когда вы создаете привязку с true 'IsAsync', она выполняет доступ к свойству Author.IconUrl в отдельном потоке, но выполняет преобразование из Uri в ImageSource в основном потоке. Как вы обнаружили, преобразование выполняет поиск DNS в основном потоке, вызывая блокировку приложения.

Поскольку ваш источник http / https, WPF автоматически обрабатывает асинхронную загрузку источника изображения. Поэтому я подозреваю, что все, что вам нужно сделать, это сделать только поиск DNS асинхронным.

Это можно автоматизировать с помощью прикрепленного свойства:

<Image my:ImageAsyncHelper.SourceUri="{Binding Author.IconUrl}" />

где ImageAsyncHelper определяется как:

public class ImageAsyncHelper : DependencyObject
{
  public static Uri GetSourceUri(DependencyObject obj) { return (Uri)obj.GetValue(SourceUriProperty); }
  public static void SetSourceUri(DependencyObject obj, Uri value) { obj.SetValue(SourceUriProperty, value); }
  public static readonly DependencyProperty SourceUriProperty = DependencyProperty.RegisterAttached("SourceUri", typeof(Uri), typeof(ImageAsyncHelper), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      ((Image)obj).SetBinding(Image.SourceProperty,
        new Binding("VerifiedUri")
        {
          Source = new ImageAsyncHelper { GivenUri = (Uri)e.NewValue },
          IsAsync = true,
        });
    }
  });

  Uri GivenUri;
  public Uri VerifiedUri
  {
    get
    {
      try
      {
        Dns.GetHostEntry(GivenUri.DnsSafeHost);
        return GivenUri;
      }
      catch(Exception)
      {
        return null;
      }

    } 
  } 
}

Как это работает:

  1. Когда вы устанавливаете присоединенное свойство, оно создает экземпляр ImageAsyncHelper и асинхронно связывает Image.Source с типом ImageSource объекта асинхронного помощника.
  2. Когда срабатывает асинхронная привязка, она вызывает метод получения VerifiedUri, который проверяет доступность адреса, а затем возвращает GivenUri
  3. Если свойство IconUri когда-либо изменяется, привязка приводит к обновлению присоединенного свойства, которое создает и привязывает новый ImageAsyncHelper, поэтому изображения остаются актуальными.
0 голосов
/ 17 марта 2010

Ну, думаю, я узнал, почему это происходит ...

Я немного покопался с Reflector, чтобы попытаться выяснить, что именно вызывается. Внутри BitmapDecoder я нашел простой вызов WebRequest.BeginGetResponseStream.

Я написал быстрое консольное приложение для проверки:

static void Main(string[] args)
{
    DateTime start = DateTime.Now;

    WebRequest request = WebRequest.Create("http://nonexistserver/myicon.jpg");
    IAsyncResult ar = request.BeginGetResponse((AsyncCallback)delegate(IAsyncResult result)
    {
        try
        {
            WebResponse response = request.EndGetResponse(result);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }, null);

    Console.WriteLine(DateTime.Now - start);
    ar.AsyncWaitHandle.WaitOne();

    Console.WriteLine("Done");
    Console.ReadKey();
}

Если Fiddler не запущен, выходной сигнал составляет 2-3 секунды. При работающем Fiddler выход составляет ~ .25 секунд.

Выполняя еще одно копание, похоже, что блоки BeginGetResponse (именно это WPF использует под капотом) блокируют до тех пор, пока не будет завершено разрешение имен.

См. Этот вопрос: webrequest.begingetresponse занимает слишком много времени, когда URL недействителен

Итак, я понимаю, почему блокировка происходит сейчас, но я не знаю чистого решения для своего приложения. (

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