Comment charger des images en arrière-plan?

J’essaie de charger une image en arrière-plan, puis de mettre à jour l’interface utilisateur. J’ai joué avec ça toute la journée et je ne sais pas ce qui me manque. Je continue à avoir l’erreur suivante:

“Le thread appelant ne peut pas accéder à cet object car un autre thread le possède.”

J’ai cherché exemple après exemple, mais je n’arrive pas à trouver de réponse. J’ai également enveloppé le code qui touche l’interface utilisateur dans un autre BeginInvoke.

Mise à jour 3: La morale de l’histoire. ImageSource n’est pas thread-safe pour l’access.

Mise à jour 2: Cela doit être une solution simple :). J’ai essayé le clonage, mais cela n’a pas abouti, mais j’ai obtenu une erreur différente: “Une exception a été générée par la cible d’une invocation.”

Mise à jour 1: J’ai essayé BackgroundWorker, mais je reçois toujours la même erreur, mais cela se produit sur la brosse. ImageSource.Height. Est-ce que je signale l’interface correctement? Aucune suggestion?

Voici mon XAML:

         

Voici une partie du code derrière:

 namespace Slideshow { public class Show { public Show() { BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); bw.RunWorkerAsync(); } void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { BitmapSource bitmap = e.Result as BitmapSource; if (bitmap != null) { this.Dispatcher.BeginInvoke(DispatcherPriority.Normal (ThreadStart)delegate() { Image image = new Image(); image.Source = bitmap; background.Child = image; }); } } void bw_DoWork(object sender, DoWorkEventArgs e) { BitmapSource bitmapSource = CreateBrush(GetRandomFile()); e.Result = bitmapSource; } } } 

Ce que j’ai tendance à utiliser ThreadPool.QueueUserWorkItem pour charger l’image, puis lorsque l’opération est terminée, je rappelle au thread de l’interface utilisateur à l’aide de l’object Dispatcher thread-safe. La source de l’image n’est pas thread-safe, vous devrez utiliser quelque chose comme JpegBitmapDecoder , il y a aussi un PngBitmapDecoder .

Par exemple:

 public Window() { InitializeComponent(); ThreadPool.QueueUserWorkItem(LoadImage, "http://soffr.miximages.com/delegates/simp2006_HomerArmsCrossed_f.jpg"); } public void LoadImage(object uri) { var decoder = new JpegBitmapDecoder(new Uri(uri.ToSsortingng()), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); decoder.Frames[0].Freeze(); this.Dispatcher.Invoke(DispatcherPriority.Send, new Action(SetImage), decoder.Frames[0]); } public void SetImage(ImageSource source) { this.BackgroundImage.Source = source; } 

Une dernière chose que nous avions dans notre projet. ImageSource étant placé dans l’interface utilisateur, vous devez vérifier s’il est gelé:

 public void SetImage(ImageSource source) { ImageSource src = null; if(!source.IsFrozen) src = source.GetAsFrozen(); else src = source; this.BackgroundImage.Source = src; } 

Vous souhaitez incorporer plusieurs threads dans votre application WPF. Comme indiqué dans l’article ci-dessous, WPF vous oblige à effectuer tous les travaux d’interface utilisateur sur le thread qui a créé l’interface utilisateur.

Vérifiez ceci: http://pjbelfield.wordpress.com/2007/10/29/worker-threads-and-the-dispatcher-in-wpf/

et pour le travail d’interface utilisateur intensif:

http://soffr.miximages.com/delegates/pp Votre code pour charger l’image (.jpg) en mémoire doit être fait en arrière-plan. Le code permettant de charger l’image de la mémoire dans un contrôle WPF doit être effectué dans le thread WPI d’interface utilisateur normal. Seules les tâches lentes / longues doivent être placées dans un fil d’arrière-plan. Une fois la tâche longue terminée, il doit signaler au thread d’interface utilisateur de mettre à jour la vue avec le résultat de la tâche longue.

Ma manière préférée utilise la classe BackgroundWorker:

 var bg = new System.ComponentModel.BackgroundWorker(); bg.DoWork += Work_Function; bg.RunWorkerCompleted += UI_Function; bg.RunWorkerAsync(); void Work_Function(object sender, DoWorkEventArgs e) { ... } void UI_Function(object sender, RunWorkerCompletedEventArgs e) { ... } 

Lorsque vous appelez RunWorkerAsync (), votre fonction Work_Function () est d’abord appelée. Une fois terminé, la fonction UI_Function () est appelée. La fonction de travail peut placer une variable dans la propriété DoWorkEventArgs.Result, accessible à partir de la propriété RunWorkerCompletedEventArgs.Result.

Je pense que cet article pourrait également vous intéresser: http://www.codeproject.com/KB/WPF/WPF_Explorer_Tree.aspx Il montre comment configurer le chargement différé dans une vue arborescente dans WPF. Je pense que le concept de base (append un noeud factice, intercepter l’affichage du noeud factice en chargeant l’image suivante à la place) s’appliquera à votre travail.

L’erreur que vous obtenez maintenant est une bonne nouvelle, cela signifie que BackgroundWorker fonctionne. Vous pouvez essayer de le cloner dans RunWorkerCompleted avec brush.Clone(); .

Essayez de supprimer la partie Dispatcher.BeingInvoke. L’argument fondamental de BackgroundWorker est que l’événement est automatiquement réorganisé dans le fil de l’interface utilisateur pour que vous n’ayez pas à vous inquiéter.