[new:25/5/2010]La plupart du temps utiliser une image dans une application Silverlight est quelque chose de simple : l'image est ajoutée au projet, depuis les Assets on la pose sur la surface de design et c'est tout... Mais ce n'est pas forcément la façon la plus subtile de gérer l'affichage d'images de grandes tailles ou d'images nombreuses, voire d'images au contenu variable (images mises à jour par une application server par exemple). Dès lors qu'on dépasse l'utilisation du simple glyph il est en réalité souvent nécessaire de charger dynamiquement la ou les images en question.
Comme toujours avec le couple Xaml / C# il existe plus d'une façon d'atteindre le but recherché. Mais à l'origine on trouve un besoin commun : créer une URI pour pointer l'image, qu'elle soit locale à l'application ou bien distante (quelque part sur le Web). Si l'image est locale elle doit bien entendu être ajoutée au projet SL. Dans ce cas le chargement dynamique ne réduira pas la taille du Xap ni son temps de téléchargement. Mais parfois le chargement dynamique peut servir d'autres objectifs que l'optimisation de la taille d'une application.
La version la plus simple consiste à faire comme suit :
Image image = new Image();
Uri uri = new Uri("images/myImage.png", UriKind.Relative);
ImageSource img = new System.Windows.Media.Imaging.BitmapImage(uri);
image.SetValue(Image.SourceProperty, img); // ajouter ensuite "image" à l'arbre visuel
Dans cette version on ne gère pas l'événement de la classe image permettant de savoir si l'image a bien été téléchargée ou non. S'il s'agit d'une image locale on peut faire l'économie d'une telle gestion. Mais s'il s'agit d'une image distante je vous conseille fortement de programmer l'événement ImageFailed de la classe Image ! Un réseau, et encore plus Internet, pose souvent des problèmes, qu'ils soient ponctuels (engorgement, pannes temporaires) ou plus spécifiquement liés à la ressource à laquelle on accède (disparition, changement de place, altération...). Dans de tels cas vous aurez certainement à vous frotter à l'infâme erreur AG_E_NETWORK_ERROR.
Après la création de l'objet Image (dans l'exemple ci-dessus) il suffit d'ajouter un gestionnaire :
img.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(img_ImageFailed);
puis de le programmer :
void img_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
// gestion de l'erreur
}
Le code d'erreur est "open", à vous de réagir selon les circonstances et la gravité de l'absence de l'image. Très souvent j'opte pour une solution simple qui évite beaucoup de code et qui avertit immédiatement l'utilisateur : je stocke dans le projet un Png ou un Jpeg simple affichant un glyph d'erreur avec ou sans message. En cas d'erreur de chargement d'une image je charge l'image "erreur" à la place...
Dernière chose : lorsqu'on charge une image dynamiquement comme dans les exemples de ce billet il reste une étape à ne pas oublier : ajouter l'image au visuel en lui affectant un parent, un conteneur déjà présent et visible, par exemple :
LayoutRoot.Children.Add(image);
Enfin, dernière chose, vous pouvez aussi gérer l'événement ImageOpened de la classe Image. Cet événement intervient une fois l'image téléchargée et décodée avec succès. On peut l'exploiter en divers occasions comme prévenir l'utilsiateur qu'une image longue à télécharger est enfin disponible et peut être consultée ou simplement et plus pratiquement pour prendre connaissance de la taille de l'image avant qu'elle ne soit affichée (ce qui est très pratique si cette taille est inconnue de l'application).
Bien évidemment, entre l'ajout d'une balise Xaml de type <Image Source="images/MonImage.png"></Image> et la gestion des événements ici décrits il y un monde, la différence entre une application de test et une application professionnelle...
Stay Tuned !