2010-03-17 1 views
7

ich eine WPF-Anwendung zu schreiben und versuchen, ein Bild zu meiner Ansicht nach Modell mit dem folgenden XAML zu binden:Wie kann ich verhindern, dass ein WPF-Bild blockiert, wenn die ImageSource auf eine nicht erreichbare URL verweist?

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

Das Problem ist, dass die Bild-URLs werden von den Benutzern definiert sind und oft auf Bilder gehostet beziehen Intranet-Webserver. Wenn die WPF-Anwendung remote ausgeführt wird, blockiert sie beim Versuch, die Bilder zu lösen, die jetzt nicht erreichbar sind.

Ich dachte, die Bindungseigenschaft "IsAsync" würde dazu führen, dass die Last im Hintergrund auftritt, aber es scheint, dass die DNS-Auflösung im Hauptthread noch passieren kann?

Was kann ich tun, damit meine App nicht gesperrt wird, selbst wenn die Bilder nicht erreichbar sind?

Danke, Corey

Antwort

10

Hier ist eine neue Antwort für Sie, hoffentlich besser als meine frühere.

Wenn Sie erstellen Ihre Bindung mit ‚isAsync‘ wahr, es führt die Eigenschaft Zugriff auf Author.IconUrl auf einem separaten Thread aber Die Konvertierung von Uri zu ImageSource im Hauptthread. Wie Sie festgestellt haben, führt die Konvertierung eine DNS-Suche im Hauptthread durch, wodurch die Anwendung gesperrt wird.

Da Ihre Quelle http/https ist, wird WPF automatisch asynchron verarbeiten lade die Bildquelle, also vermute ich, dass man nur die DNS-Suche asynchron machen muss.

Dies kann durch die Verwendung einer angefügten Eigenschaft automatisiert werden:

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

wo ImageAsyncHelper ist wie folgt definiert:

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

    } 
    } 
} 

Die Art und Weise dies funktioniert, ist:

  1. Wenn Sie die angefügten Eigenschaft Es erstellt eine Instanz von ImageAsyncHelper und bindet die Image.Quelle asynchron an die ImageSource-Property des asynchronen Hilfsobjekts.
  2. Wenn die asynchrone Bindung Feuer ruft sie den VerifiedUri Getter, der die Adresse überprüft, kehrt zugänglich ist dann die GivenUri
  3. Wenn Ihr IconUri Eigenschaft jemals ändert, was die Bindung bewirkt, dass die angefügten Eigenschaft zu aktualisieren erstellt und bindet ein neues ImageAsyncHelper so die Bilder bleiben auf dem neuesten Stand.
+0

Ausgezeichnet! Vielen Dank! –

+1

Minor fix: neue Binding ("ImageSource") sollte jetzt neu Binding ("VerifiedUri") sein. –

+0

Danke. Ich habe den Code in meiner Antwort korrigiert. –

0

Nun, ich glaube, ich fand heraus, warum es geschieht ...

ich mit Reflektor zu versuchen, ein bisschen um gegraben, was genau herauszufinden, wurde immer genannt. Im BitmapDecoder habe ich einen einfachen Aufruf eines WebRequest.BeginGetResponseStream gefunden.

schrieb ich eine schnelle Konsole App-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(); 
} 

Ohne Fiddler Lauf, der Ausgang 2-3 Sekunden. Wenn Fiddler läuft, beträgt die Ausgabe ~ .25 Sekunden.

Wenn Sie etwas mehr graben, sieht es aus wie BeginGetResponse (was WPF unter der Haube verwendet) blockiert, bis die Namensauflösung abgeschlossen ist.

Sehen Sie diese Frage: webrequest.begingetresponse is taking too much time when the url is invalid

So verstehe ich, warum die Sperrung jetzt auftritt, aber ich weiß nicht, eine saubere Lösung für meine Anwendung. :(

Verwandte Themen