Silverlight et la sérialisation

La sérialisation est un besoin fréquent que le framework .NET sait parfaitement gérer. La sérialisation binaire s'obtient d'ailleurs le plus naturellement du monde en ajoutant un simple attribut [Serializable] à la définition de la classe. Mais voilà, essayez de décorer une classe avec SerializableAttribute sous Silverlight... Surprise ! Un message d'erreur vous prévient que l'espace de nom ne peut être trouvé.

Pas de sérialisation ?

Non. Pas de sérialisation binaire en tout cas sous Silverlight jusqu'à maintenant (v 3.0 au moment de ce billet). La classe SerializableAttribute n'est tout simplement pas définie dans System.

Avant d'aller crier au scandale sur les blogs ou forums officiels essayons de réfléchir...

A quoi sert la sérialisation ?

Sérialiser, cela sert à prendre un "cliché" d'une instance dans le but d'être capable de recréer une copie parfaite de cette dernière au bout d'un certain temps.

Un certain temps... cela peut être une milliseconde ou bien 1 jour ou 1 an. Dans le premier cas le stockage du "cliché" est en mémoire généralement, mais dans le second, il s'agit le plus souvent d'un stockage persistant tel qu'un fichier disque ou une base de données.

Pas de gestion de disque local en RIA

La nature même de Silverlight et le style bien particulier d'applications qu'il permet d'écrire fait qu'il n'existe aucune gestion de disque local. En réalité il n'existe rien qui permette d'accéder à la machine hôte, en lecture comme en écriture, question de sécurité.

On notera quelques exceptions à cette règle : l'OpenFileDialog qui permet d'ouvrir uniquement certains fichiers sur les disques de l'hôte et l'IsolatedStorage, espace de stockage local protégé pouvant être utilisé par une application Silverlight. Toutefois OpenFileDialog ne règle pas le problème de persistance et si l'IsolatedStorage peut être exploité en ce sens il s'agit d'un espace restreint, inlocalisable par un utilisateur "normal" donc in-sauvegardable sélectivement. Autant dire que l'IsolatedStorage peut rendre des services ponctuels (cache de données principalement) mais que ce n'est certainement pas l'endroit où stocker des données sensibles ou à durée de vie un peu longue.

Bref, en dehors de ces exceptions qui ne règlent pas tout à fait le besoin de persistance des instances, une application de type RIA ne peut sérieusement gérer des données que distantes...

En tout logique...

Reprenons : la sérialisation sert à persister des instances pour un stockage à plus ou moins longue échéance ou une communication avec un serveur distant. La nature d'une application RIA supprime la liberté d'un stockage local fiable et facilement maitrisable.

En toute logique sérialiser des instances sous Silverlight n'a donc aucun intérêt, sauf pour communiquer avec un serveur distant.

Comme le framework .NET pour Silverlight est une version light du framework complet il a bien fallu faire des coupes sombres... La sérialisation n'a pas échappé à cette rigueur. La principale raison d'être de la sérialisation sous Silverlight étant la communication (quelle que soit la technologie cela se fait sur une base XML le plus souvent) et cela réclamant une sérialisation XML plutôt que binaire, SerializableAttribute et sa sérialisation binaire ont ainsi été "zappés" !

Une solution

La situation est grave mais pas désespérée. S'il reste difficile le plus souvent d'utiliser directement sous Silverlight des classes conçues pour le framework complet (et pas seulement à cause de la sérialisation binaire), il est tout à fait possible de sérialiser des instances sous Silverlight à condition d'utiliser les mécanismes qui servent aux communications avec les serveurs distants.

Le DataContract

Silverlight utilise une partie du mécanisme WCF du DataContract (mais l'attribut DataMember n'existe pas). On trouve même une classe le DataContractSerializer qui fournit le nécessaire pour sérialiser et désérialiser des instances même si celles-ci ne sont décorées d'aucun attribut particulier.

Au final la sérialisation sous Silverlight est plus simple que la sérialisation binaire par SerializableAttribute sous le framework complet !

Un exemple de code

Einar Ingebrigtsen, un MVP nordiste travaillant en scandinavie, à eu la bonne idée de proposer deux méthodes utilitaires qui montrent comment utiliser le DataContractSerializer. Plutôt que d'imiter et réinventer la roue, regardons son code :

   1:  public string Serialize<T>(T data)
   2:          {
   3:              using (var memoryStream = new MemoryStream())
   4:              {
   5:                  var serializer = new DataContractSerializer(typeof(T));
   6:                  serializer.WriteObject(memoryStream, data);
   7:   
   8:                  memoryStream.Seek(0, SeekOrigin.Begin);
   9:   
  10:                  var reader = new StreamReader(memoryStream);
  11:                  string content = reader.ReadToEnd();
  12:                  return content;
  13:              }
  14:          }
  15:   
  16:          public T Deserialize<T>(string xml)
  17:          {
  18:              using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(xml)))
  19:              {
  20:                  var serializer = new DataContractSerializer(typeof(T));
  21:                  T theObject = (T)serializer.ReadObject(stream);
  22:                  return theObject;
  23:              }
  24:          }

C'est simple et efficace. Attention, pour utiliser ce code il faudra ajouter une référence à System.Runtime.Serialization.dll, puis un using System.Runtime.Serialization.

L'utilisation de ces méthodes tombe dès lors sous le sens, mais un petit exemple est toujours plus parlant :

   1:  { ....
   2:  // nouvelle instance 
   3:  var t = new Test { Field1 = "test de serialisation", Field2 = 7 };
   4:  t.List.Add(new test2 { Field1 = "item 1" });
   5:  t.List.Add(new test2 { Field1 = "item 2" });
   6:  t.List.Add(new test2 { Field1 = "item 3" });
   7:   
   8:  // sérialisation et affichage
   9:  var s = Serialize<Test>(t);
  10:  var sp = new StackPanel { Orientation = Orientation.Vertical };
  11:  LayoutRoot.Children.Add(sp);
  12:  sp.Children.Add(new TextBlock { Text = s });
  13:   
  14:  // désérialisation et affichage
  15:  Var t2 = Deserialize<Test>(s);
  16:  var result = t2 == null ? "null" : "instantiated";
  17:  sp.Children.Add(new TextBlock { Text = result });
  18:  }
  19:   
  20:  // la classe de test
  21:  public class Test
  22:  {
  23:    public string Field1 { get; set; }
  24:    public int Field2 { get; set; }
  25:    private ObservableCollection<test2> list = 
  26:                  new ObservableCollection<test2>();
  27:    public ObservableCollection<test2> List { get { return list; } }
  28:  }
  29:   
  30:  // la classe des éléments de la liste de la classe Test
  31:  public class test2
  32:  {
  33:    public string Field1 { get; set; }
  34:  }

Conclusion

Une fois que l'on a compris pourquoi la sérialisation binaire est absente de Silverlight, et une fois qu'on a trouvé une solution de rechange, on se sent beaucoup mieux :-)

Il reste maintenant tout à fait possible de stocker en local le résultat de la sérialisation dans un fichier texte (en utilisant l'IsolatedStorage sur lequel je reviendrai) ou bien de le transmettre à service Web ou autre...

Stay Tuned !

Bien commencer avec Silverlight 3 (les setup indispensables)

Silverlight 3 est disponible depuis juillet dernier mais il n'est pas toujours évident de savoir exactement ce qu'il est nécessaire de télécharger et où pour bien commencer.

Les vacances sont passées par dessus tout ça, du coup vous n'avez pas forcément bien suivi l'action, alors pour vous voici en mode slow motion, le replay des indispensables à installer pour s'amuser comme un fou avec Silverlight 3 et surtout être au point pour concevoir des applications RIA !

Silverlight 3

Le runtime est indispensable pour, au minimum, pouvoir exécuter les applications : Le runtime Silverlight 3
Le SDK est en revanche le minimum syndical pour concevoir des applications Silverlight 3 : Le SDK Silverlight 3

Expression Blend 3 avec Sketchflow

Blend est un l'outil indispensable pour créer des applications WPF et Silverlight. D'autant plus que depuis la version 3 de Silverlight il n'y a plus d'affichage du visuel sous VS 2008. Il reste la possibilité de tout faire à la main en mode éditeur de XAML mais franchement ce n'est pas comme ça qu'on peut créer sérieusement un visuel alors : Blend 3 et Sketchflow
Vous noterez qu'il s'agit d'une version d'essai, Blend 3 n'a pas de version gratuite.

Visual Studio 2008

En attendant la version 2010 qui intègrera le designer visuel de Silverlight, il est toujours nécessaire de posséder VS 2008. En effet, Blend ne gère pas le debug qui ne peut s'effectuer que par VS. Bien qu'un éditeur de code a été ajouté à Blend 3 il est aussi plus confortable d'utiliser Visual Studio pour toute la partie code. Si vous ne possédez pas VS 2008 : Version d'essai de VS 2008

Visual Web Developer Express 

Si vous préférez un outil gratuit, n'oubliez pas que Visual Web Express est une bonne alternative à Visual Studio.

Outils Silverlight

Tout un tas de choses indispensables notamment pour permettre de travailler sous Visual Studio ou Visual Web Express. A noter que VS doit être absolument patché avec le SP1 avant d'installer tout ça : Les Silverlight Tools.

Le Toolkit

Silverlight 3 est un produit riche, mais il l'est encore plus lorsque le toolkit est installé. De très nombreux contrôles indispensables sont ajoutés : le Silverlight Toolkit.

Les RIA Services

Grâces aux Services RIA, la gestion de données distantes (n-tiers) devient un jeu d'enfant. C'est un must pour tous ceux qui souhaitent développer des applications orientées données avec Silverlight : .NET RIA Services.

Deep Zoom Composer

Si vous souhaitez créer des images Deep Zoom, il est nécessaire de posséder le Composer : Deep Zoom Composer.

Le site officiel

Encore un lien indispensable ! La partie "getting started" vous renseigne sur les téléchargement de base et propose même un installeur global "Microsoft Web Platform". Vous trouverez aussi sur ce site de nombreuses vidéos de présentation et de formation (en anglais) : Le Site officiel Silverlight.

Dot.Blog

Et oui ! Si vous souhaitez rester à jour et être au courant, le mieux c'est encore de venir sur Dot.Blog le plus souvent possible ou mieux, de s'abonner au flux RSS et surtout : Stay Tuned !

 

Code source Silverlight 'les codes postaux'

Il y a quelques temps j'avais mis en ligne l'application exemple Codes Postaux Français sous Silverlight. Application Silverlight 2.0 montrant l'utilisation d'un service Web avec base de données.

Beaucoup de lecteurs m'ont demandé si le code source était disponible, ce qui n'était pas le cas. J'avais prévu d'écrire un article se basant sur ce code mais le temps passant vite sans pouvoir mener à bien cette écriture j'ai opté pour une publication du code source complet, brut de fonderie.

Vous trouverez le lien de téléchargement sur la page suivante : http://www.e-naxos.com/Blog/page/Exemples-Silverlight.aspx

Silverlight 3 : Un média player complet bien caché !

Silverlight 3 est livré de base avec un contrôle très versatile, MediaElement, capable de jouer de nombreux formats comme les mp3 ou les vidéos.

Si ce contrôle est très puissant il est un peu "nu" de base et il faut soi-même ajouter les boutons de commande comme "play" et looker l'ensemble. Avec Expression Blend 3 c'est un jeu d'enfant. Enfin, de grand enfant qui a un peu de temps devant lui tout de même. D'où la question : n'existerait-il pas un MediaElement déjà tout habillé ?

Si vous possédez Expression Media Encoder 3 vous savez que cet extraordinaire outil (servant principalement à encoder des médias) donne le choix entre plusieurs formats de sortie dont des projets HTML tout fait intégrant un média player tout looké, et mieux encore, avec le choix parmi de nombreux modèles.

Quel rapport entre Media Encoder et un projet Blend/VS ? C'est tout simple : lorsque vous installez Media Encoder, sous Blend 3 dans l'onglet Assets vous disposez, en plus de MediaElement d'un nouveau contrôle "MediaPlayer" !

Par défaut ce composant ressemble à l'image ci-dessous. Pour le relooker, il suffit de faire un clic droit et d'éditer un copie du template !

Reste la question à 10 centimes d'euro : oui mais Media Encoder est livré avec de nombreux modèles dont certains ont déjà un look sympa, ne pourrait-on pas récupérer ces modèles au lieu de templater à la main le MediaPlayer ?

Si, c'est possible (© Les Nuls, "Hassan Cehef").

Comment ? Là c'est plus cher... Non, comme je suis un chic type, voici la solution gratuite :

Encoder 3 s'installe avec le code source des modèles qui se trouvent dans le répertoire "C:\Program Files\Microsoft Expression\Encoder 3\Templates\en", il suffit donc de piocher le modèle qu'on désire utiliser, et grâce aux sources d'extraire le contrôle avec son template et de l'intégrer à son propre projet !

Le célèbre Tim Heuer décrit (en englais) la méthode à suivre, je vous renvoie ainsi à son billet Using Encoder Templates in your Silverlight Application si jamais vous n'arrivez pas à vous dépatouiller seul avec le code source des projets Encoder 3.

Intégrer de la vidéo, même HD, dans une application Silverlight n'a jamais été aussi simple... et beau.

Amusez-vous bien, et .. Stay Tuned !

Silverlight 3 : un Behavior ajoutant un effet de reflexion (avec les nouvelles WriteableBitmap)

Dans le précédent billet je vous proposais d'écrire un Behavior effectuant une rotation animée d'un élément visuel, aujourd'hui je vous invite à la réflexion...

Je veux parler de l'effet de réflexion tant à la mode. WPF sait parfaitement le faire grâce notamment au brosses visuelles qui permettent facilement de "peindre" un contrôle avec la copie visuelle d'un autre objet (ou arbre d'objets). Hélas, Silverlight 3 ne possède pas encore de brosse de ce type et pour obtenir l'effet désiré il faut dupliquer le ou les objets sources et leur faire subir une rotation, un flip, et l'ajout d'un masque d'opacité. Tout cela est assez fastidieux il faut bien l'avouer.

Mais grâce aux Behaviors il est possible de rendre tout cela automatique et avec l'aide des WritableBitmap il n'est même pas nécessaire de dupliquer tous les objets !

J'ai déjà détaillé dans le billet précédent la technique de création d'un Behavior, je vous renvoie ainsi à cet exemple pour la base. Pour l'effet de réflexion, le mieux est de regarder ci-dessous l'exemple live :

Pour créer cet effet nous allons utiliser une nouvelle classe de Silverlight 3 : les WritableBitmap. Cette classe permet de créer des images de toute pièce ce qui n'était pas possible jusqu'à lors.

Non seulement les WritableBitmap permettent de créer une bitmap par code, mais en plus elle possède une méthode de Render qui est capable de faire le rendu de tout objet ou arbre visuel et de le placer dans la bitmap. Nous allons utiliser cette possibilité pour remplir une bitmap à laquelle nous appliquons ensuite quelques transformations (renversement, échange gauche/droite, translation). Un masque d'opacité est ajouté pour l'impression de dégradé d'opacité.

Le Behavior fonctionne sur des sources de type Canvas uniquement, cela permet d'illustrer comment limiter un Behavior à une classe ou une branche de classes donnée. Une fois l'image du reflet créé elle est ajoutée en tant qu'élément au canvas (après avoir pris soin de supprimer la précédente image s'il y en a déjà une).

Le Behavior n'a pas de rendu en mode design sous Blend 3, c'est un petit défaut. De même l'effet de réflexion est créé une fois pour toute et n'est modifié que si le canvas source voit sa taille changée. Si vous créez dynamiquement des objets sur celui-ci, le reflet ne changera pas. Bien entendu on pourrait penser connecter le Behavior à l'événement de rendu du canvas LayoutUpdated. Cela eut été une solution élégante. Hélas on créerait une boucle infinie puisque notre Behavior ajoute un objet au canvas ce qui déclenche le renouvellement de son rendu. On pourrait améliorer les choses en ne faisant pas le rendu dans le canvas source et en ajoutant une propriété au Behavior qui serait un objet de type Image (non enfant de la source) qu'on utiliserait comme sortie de rendu de l'effet de reflet. Mais cela poserait d'autres problèmes. 

Toutes les idées sont envisageables et le code proposé n'est qu'un exemple. A vous de l'améliorer sans oublier de venir ici nous dire ce que vous avez fait !

Côté paramétrage le Behavior permet de modifier les points de départ et d'arrivée du dégradé du masque d'opacité ainsi que l'offset sur l'axe Y. Sans ce dernier le reflet serait collé au canvas source ce qui n'est pas forcément très beau. Par défaut un décalage de 3 pixels vers le bas décolle légèrement le reflet de l'original.

Code la publication d'un code un peu long n'est pas forcément très lisible sur le blog, je vous renvoie au code source du projet complet que vous n'avez plus qu'à télécharger ici : ReflectionBehavior.zip (62,91 kb) [Downloads: 83]

Et Stay Tuned !