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

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

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

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

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

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

Спасибо, Corey

7
задан Corey O'Brien 17 March 2010 в 16:07
поделиться

2 ответа

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

Когда вы создаете привязку с 'IsAsync' true, он выполняет доступ к свойству 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 вспомогательного объекта async.
  2. Когда срабатывает асинхронная привязка, она вызывает метод получения VerifiedUri, который проверяет доступность адреса, а затем возвращает GivenUri.
  3. Если ваше свойство IconUri когда-либо изменяется, привязка вызывает обновление присоединенного свойства, которое создает и связывает новый ImageAsyncHelper, так что изображения остаются актуальными.
10
ответ дан 7 December 2019 в 01:19
поделиться

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

Я немного покопался в 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 вывод составляет ~ 0,25 секунды.

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

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

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

0
ответ дан 7 December 2019 в 01:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: