2010-11-19 12 views
4

J'essaye de charger des images du Web dans mon application de wpf.Chargement d'image à partir du Web dans wpf/surface

L'idée est la suivante: Lorsque je clique sur un bouton, un popup contenant des informations supplémentaires est généré. Dans cette fenêtre, j'utilise des images du web.

Le problème: Lorsque la fenêtre contextuelle est en cours de chargement, les systèmes se bloquent en attendant les images. Je lie les images de mon code derrière. Les images sont stockées dans une ObservableCollection. J'ai essayé en utilisant un fil pour charger les images mais chaque fois que je cours dans une exception en disant que le fil n'est pas le propriétaire de l'objet.

J'ai essayé d'utiliser une invocation pour obtenir les images téléchargées sur le UserinterfaceThread mais je n'arrive pas à l'atteindre. Mon code est le suivant:

 IList<Image> imagesFromWeb = downloadImagesFromWeb(url); 


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

Dès que les images sont téléchargées et il tente d'ajouter les images à la (déjà ouvert) pop-up, je reçois l'exception de dire le fil est pas le propriétaire de l'objet

Est-ce que quelqu'un peut me diriger dans la bonne direction?

Répondre

1

Vous pouvez obtenir une variété de problèmes avec les collections, WPF, la reliure et le filetage

La meilleure chose (à mon avis) est d'utiliser une collection observable répartiteur de sécurité

est une mise en œuvre ici, avec inclut également la sécurité de thread:

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

Thanx pour la réponse. Je ne l'ai pas utilisé car j'ai déjà trouvé une autre solution (voir ci-dessous). – Marcel

1

Je me suis dit qu'il y avait une meilleure façon de charger l'image. Au lieu de se lier à une image dans le code derrière, il est préférable de lier à une chaîne contenant l'emplacement de l'image

Après cela, j'utilise un convertisseur dans le code xaml qui convertit la chaîne en une image. (Le téléchargeur d'image est maintenant à l'intérieur de la classe du convertisseur)

le code xaml:

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

Le code pour le convertisseur:


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

et bien sûr don oubliez pas de configurer la ressource dans l'app.xaml avec:

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

Si vous avez l'image disponible sur un serveur web public qui peut être adressé en utilisant alors un URI HTTP normal, vous pouvez définir la source directement que:

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

WPF se chargera de le télécharger - Je vais même le faire de façon asynchrone Je pense que je ne suis pas sûr à 100%.

Vous pouvez bien sûr faire avec databinding ainsi:

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

Et dans le viewmodel

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

Merci beaucoup. Propre et simple et fonctionne deux ans plus tard. – JoshVarty