How can I keep a WPF Image from blocking if the ImageSource references an unreachable Url?

后端 未结 2 1325
闹比i
闹比i 2021-01-12 10:01

I\'m writing a WPF application and trying to bind an image to my view model with the following XAML:



        
相关标签:
2条回答
  • 2021-01-12 10:19

    Well I think I found out why it is happening...

    I dug around with Reflector a bit to try to find out what exactly was getting called. Inside the BitmapDecoder I found a simple call to a WebRequest.BeginGetResponseStream.

    I wrote a quick console app to test:

    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();
    }
    

    Without Fiddler running, the output is 2-3 seconds. With Fiddler running, the output is ~.25 seconds.

    Doing some more digging, it looks like BeginGetResponse (which is what WPF is using under the hood) blocks until name resolution is complete.

    See this question: webrequest.begingetresponse is taking too much time when the url is invalid

    So I understand why the blocking occurs now, but I don't know a clean solution for my application. :(

    0 讨论(0)
  • 2021-01-12 10:23

    Here is a new answer for you, hopefully better than my earlier one.

    When you create your binding with 'IsAsync' true, it executes the property access to Author.IconUrl on a separate thread but does the conversion from Uri to ImageSource in the main thread. As you discovered, the conversion does a DNS lookup on the main thread causing the application to lock up.

    Since your source is http/https, WPF will automatically handle asynchronously loading the image source. So I suspect all you need to do is to make just the DNS lookup asynchronous.

    This can be automated by using an attached property:

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

    where ImageAsyncHelper is defined as:

    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;
          }
    
        } 
      } 
    }
    

    The way this works is:

    1. When you set the attached property it creates an instance of an ImageAsyncHelper and asynchronously binds the Image.Source to the ImageSource propety of the async helper object.
    2. When the asynchronous binding fires it calls the VerifiedUri getter which verifies the address is accessible then returns the GivenUri
    3. If your IconUri property ever changes, the binding causes the attached property to update which creates and binds a new ImageAsyncHelper so the images stays up to date.
    0 讨论(0)
提交回复
热议问题