Я пишу приложение WPF и пытаюсь связать изображение со своей моделью представления со следующим XAML:
<Image Source="{Binding Author.IconUrl, IsAsync=True}" />
Проблема состоит в том, что URL изображения определяются пользователями и могут часто обращаться к изображениям, размещенным на веб-серверах интранет. Когда приложение WPF запущено удаленно, оно запирается при попытке разрешить изображения, которые теперь недостижимы.
Я думал, что свойство привязки "IsAsync" заставит загрузку происходить в фоновом режиме, но кажется, что разрешение DNS может все еще произойти в основном потоке?
Что я могу сделать, чтобы помешать моему приложению блокировать, даже если изображения недостижимы?
Спасибо, Corey
Вот вам новый ответ, надеюсь, лучше, чем мой предыдущий.
Когда вы создаете привязку с '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;
}
}
}
}
Это работает следующим образом:
Думаю, я выяснил, почему это происходит ...
Я немного покопался в 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-адрес недействителен.
Я понимаю, почему происходит блокировка сейчас, но я не знаю чистого решения для своего приложения. : (