2010-11-19 12 views
4

Ich versuche, Bilder aus dem Internet in meiner WPF-Anwendung zu laden. Die Idee ist die folgende: Wenn ich auf eine Schaltfläche klicke, wird ein Popup mit zusätzlichen Informationen ausgelöst. In diesem Popup verwende ich einige Bilder aus dem Internet. Das Problem: Wenn das Popup geladen wird, hängt das System beim Warten auf die Bilder. Ich bin verbindlich die Bilder von meinem Code hinter. Die Bilder werden in einer ObservableCollection gespeichert. Ich probierte mit einem Thread zum Laden der Bilder, aber jedes Mal, wenn ich auf eine Ausnahme laufe, sagte der Thread ist nicht der Besitzer des Objekts.Image Laden von Web in WPF/Oberfläche

Ich habe versucht, eine Invoke zu verwenden, um die heruntergeladenen Bilder auf das UserinterfaceThread zu bekommen, aber ich kann es nicht erreichen. Mein Code ist folgende:

 IList<Image> imagesFromWeb = downloadImagesFromWeb(url); 


     DispatcherHelper.UIDispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate() 
     { 
      foreach (Image img in imagesFromWeb 
      { 
       this.ObservableCollection_Images.Add(img); 
      } 
    } 

Sobald die Bilder heruntergeladen werden, und es versucht, die Bilder auf den (bereits geöffnet) Popup ich die Ausnahme erhalten hinzuzufügen sagen der Faden ist nicht Eigentümer des Objekts

Kann mir bitte jemand in die richtige Richtung zeigen?

Antwort

1

Sie eine Reihe von Problemen mit Sammlungen bekommen, WPF, Binde- und

Das Beste (meiner Meinung nach) Einfädeln eine Dispatcher-safe beobachtbaren Sammlung

hier zu verwenden ist, ist eine Implementierung, mit enthält auch Thread-Sicherheit:

public class SafeObservable<T> : IList<T>, INotifyCollectionChanged, INotifyPropertyChanged 
{ 
    private readonly IList<T> collection = new List<T>(); 
    private readonly Dispatcher dispatcher; 
    public event NotifyCollectionChangedEventHandler CollectionChanged; 
    public event PropertyChangedEventHandler PropertyChanged; 
    private readonly ReaderWriterLock sync = new ReaderWriterLock(); 

    public SafeObservable() 
    { 
     dispatcher = Dispatcher.CurrentDispatcher; 
    } 

    public void Add(T item) 
    { 
     if (Thread.CurrentThread == dispatcher.Thread) 
      DoAdd(item); 
     else 
      dispatcher.BeginInvoke((Action)(() => DoAdd(item))); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
    } 

    private void DoAdd(T item) 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     collection.Add(item); 
     if (CollectionChanged != null) 
      CollectionChanged(this, 
       new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); 
     sync.ReleaseWriterLock(); 
    } 

    public void Clear() 
    { 
     if (Thread.CurrentThread == dispatcher.Thread) 
      DoClear(); 
     else 
      dispatcher.BeginInvoke((Action)(DoClear)); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
    } 

    private void DoClear() 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     collection.Clear(); 
     if (CollectionChanged != null) 
      CollectionChanged(this, 
       new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
     sync.ReleaseWriterLock(); 
    } 

    public bool Contains(T item) 
    { 
     sync.AcquireReaderLock(Timeout.Infinite); 
     var result = collection.Contains(item); 
     sync.ReleaseReaderLock(); 
     return result; 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     collection.CopyTo(array, arrayIndex); 
     sync.ReleaseWriterLock(); 
    } 

    public int Count 
    { 
     get 
     { 
      sync.AcquireReaderLock(Timeout.Infinite); 
      var result = collection.Count; 
      sync.ReleaseReaderLock(); 
      return result; 
     } 
    } 

    public bool IsReadOnly 
    { 
     get { return collection.IsReadOnly; } 
    } 

    public bool Remove(T item) 
    { 
     if (Thread.CurrentThread == dispatcher.Thread) 
      return DoRemove(item); 
     var op = dispatcher.BeginInvoke(new Func<T, bool>(DoRemove), item); 
     if (op == null || op.Result == null) 
      return false; 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
     return (bool)op.Result; 
    } 

    private bool DoRemove(T item) 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     var index = collection.IndexOf(item); 
     if (index == -1) 
     { 
      sync.ReleaseWriterLock(); 
      return false; 
     } 

     var result = collection.Remove(item); 
     if (result && CollectionChanged != null) 
      CollectionChanged(this, new 
       NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 

     sync.ReleaseWriterLock(); 
     return result; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return collection.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return collection.GetEnumerator(); 
    } 

    public int IndexOf(T item) 
    { 
     sync.AcquireReaderLock(Timeout.Infinite); 
     var result = collection.IndexOf(item); 
     sync.ReleaseReaderLock(); 
     return result; 
    } 

    public void Insert(int index, T item) 
    { 
     if (Thread.CurrentThread == dispatcher.Thread) 
      DoInsert(index, item); 
     else 
      dispatcher.BeginInvoke((Action)(() => DoInsert(index, item))); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
    } 

    private void DoInsert(int index, T item) 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     collection.Insert(index, item); 
     if (CollectionChanged != null) 
      CollectionChanged(this, 
       new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); 
     sync.ReleaseWriterLock(); 
    } 

    public void RemoveAt(int index) 
    { 
     if (Thread.CurrentThread == dispatcher.Thread) 
      DoRemoveAt(index); 
     else 
      dispatcher.BeginInvoke((Action)(() => DoRemoveAt(index))); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
    } 

    private void DoRemoveAt(int index) 
    { 
     sync.AcquireWriterLock(Timeout.Infinite); 
     if (collection.Count == 0 || collection.Count <= index) 
     { 
      sync.ReleaseWriterLock(); 
      return; 
     } 
     collection.RemoveAt(index); 
     if (CollectionChanged != null) 
      CollectionChanged(this, 
       new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
     sync.ReleaseWriterLock(); 
    } 

    public T this[int index] 
    { 
     get 
     { 
      sync.AcquireReaderLock(Timeout.Infinite); 
      var result = collection[index]; 
      sync.ReleaseReaderLock(); 
      return result; 
     } 

     set 
     { 
      sync.AcquireWriterLock(Timeout.Infinite); 
      if (collection.Count == 0 || collection.Count <= index) 
      { 
       sync.ReleaseWriterLock(); 
       return; 
      } 
      collection[index] = value; 
      sync.ReleaseWriterLock(); 
     } 
    } 
} 
+0

Danke für die Antwort. Ich habe es nicht benutzt, weil ich bereits eine andere Lösung gefunden habe (siehe unten). – Marcel

1

Ich denke, es gibt eine bessere Möglichkeit, das Bild zu laden.

Anstatt an ein Bild im Code dahinter zu binden, ist es besser, an eine Zeichenfolge zu binden, die den Speicherort des Bildes enthält. Danach verwende ich einen Konverter im XAML-Code, der die Zeichenfolge in ein Bild konvertiert. (Das Bild Downloader ist nun innerhalb der Wandlerklasse)

der Code in XAML:

<Image Source="{Binding imageUrl, Converter={StaticResource url}}" Height="200" Width="200"></Image> 

Der Code für den Wandler:


    class ImageDownloader : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      string url =(string)value; 
      return getImage(url);

} 

    private object getImage(string imagefile) 
    { 
     /// IMPLEMENT FUNCTION TO DOWNLOAD IMAGE FROM SERVER HERE 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return null; 
    } 
} 

und natürlich don Vergessen Sie nicht, die Ressource in der app.xaml mit:

einzurichten 10
<Application.Resources> 
    <ResourceDictionary> 
     <namespace:ImageDownloader x:Key="ImageDownloader" /> 
    </ResourceDictionary> 
</Application.Resources> 
9

Wenn Sie das Bild auf einem öffentlichen Web-Server, die URI mit einem normalen HTTP angesprochen werden können, dann können Sie die Quelle direkt auf dieses Set:

<Image Source="http://www.someserver.com/myimage.png" /> 

WPF kümmert es das Herunterladen - es wird es sogar asynchron tun, denke ich, obwohl ich mir nicht 100% sicher bin.

Sie können dies natürlich auch mit Datenbindung tun:

<Image Source="{Binding TheImage}" /> 

Und in dem Viewmodel

public string TheImage 
{ 
    get { return "http://www.someserver.com/myimage.png"; } 
}  
+0

Vielen Dank. Sauber und einfach und funktioniert zwei Jahre später. – JoshVarty

Verwandte Themen