Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Quels outils et quelle chaîne graphique pour WPF et Silverlight ?

WPF, Silverlight, 2D, 3D, le monde des nouvelles interfaces desktop et Web réclame désormais de fortes compétences en infographie. S'il semble évident que la présence d'un graphiste soit devenue indispensable en phase de conception des interfaces, savoir quels outils utiliser est en soi déjà un parcours du combattant !

Pour e-naxos je forme une infographiste afin d'offrir très prochainement un service de design pour les applications WPF et Silverlight. Avant de former, il faut savoir de quoi on parle, c'est toute la difficulté pour un bon formateur : avoir la connaissance "livresque" mais surtout la connaissance pratique.

Cela fait donc de long mois que je m'entraîne. je n'ai pas forcément beaucoup évolué niveau dessin, je ne serai jamais Dali ni Da Vinci, je m'y suis fait depuis mes premiers essais en maternelle, et même si les courbes de bézier et autres splines n'ont plus beaucoup de secret, l'investigation en monde 3D n'a pas été un chemin de santé !

Bref, pour produire une application "graphisée" c'est beaucoup plus simple à dire qu'à faire, je peux vous l'assurer ! Gérer un ou plusieurs graphistes, leur faire comprendre ce qu'est une application, la différence entre une listbox et un combobox n'est pas forcément non plus la chose la plus simple. Et pourtant, c'est essentiel. Depuis des mois j'engrange donc une expérience à la fois nouvelle et exitante, car ce qui se profile est l'avènement de nouveaux logiciels conçus autrement. Et j'aime le changement.

Je ne vais pas ici vous détailler l'ensemble du processus de création d'une application WPF ou Silverlight, mais vous proposer un petit tour dans les outils qui peuvent (et doivent) être utilisés pour produire une logiciel designé.

Tour d'horizon des outils

Un dessin valant mieux qu'un long discours, regardons d'abord ce "petit" diagramme que j'ai fait rapidement sur la base de mon expérience :

 

Pour voir l'image en 100% téléchargez-la : 2D3DProduction.png (43,20 kb)

J'ai testé tous les chemins dessinés et tous les softs présentés et même bien plus... Ce qui reste sur ce diagramme sont les principaux chemins que peut prendre un objet graphique et les principaux softs qu'il faut connaître et maîtriser pour produire une solution professionnelle. Certaines personne se limiteront à des portions du diagramme, ce qui reste possible, mais seule la connaissance de la totalité des logiciels et chemins de celui-ci permet de "tout" faire.

Partons de la fin

A l'arrivée nous désirons obtenir une application WPF ou Silverlight. Sous WPF nous avons besoin de composants templatés, d'image PNG ou Jpeg et d'objets visuels en 2D ou 3D. Pour Silverlight nous avons les mêmes besoins sauf en ce qui concerne la 3D qui ne sera supportée que dans Silverlight 3 en 2009.

Chaque éléments graphique impose certains chemins incontournables:

Par exemple, Silverlight gère très bien les objets 2D créé par Expression Design, mais les effets de Expression Design ne sont pas gérés, comme le Drop Shadow. Moralité, un objet 2D créé en Xaml avec E.Design ne peut être utilisé tel quel sous Silverlight, il faudra le transformer en PNG. Sous WPF il est possible d'ajouter des effets comme le Drop Shadow et il sera donc possible d'exploiter directement l'objet 2D Xaml. Objet Xaml ou PNG on voit tout de suite que les chemins de production diffèrent légèrement et que les possibilités pour le développeurs seront très différentes...

Pour produire des objets 3D il y a un goulot d'étranglement : aucun outil Microsoft ne produit d'objets 3D. Le seul logiciel qui permet de faire cela est Zam 3D d'Electric Rain. Il est plutôt agréable à utiliser et pas très cher (environ 200 euros avec le change actuel du dollar). Seulement voilà, parfois vous avez envie d'utiliser un objet 3D un peu compliqué ou mis en scène. Et chez Electric Rain ils sont malins, ou un peu mesquin au choix, et Zam 3D ne possède pas de moteur de rendu ! Il est juste capable de sortir du code Xaml. Et si vous voulez créer un PNG d'un objet 3D ? Pas de solution sauf à acquérir en plus Swift 3D du même éditeur qui dispose d'un moteur de rendu (EMO) qui ne peut pas concurrencer les gros moteurs de ray tracing comme on en trouve sur 3DS Max ou Blender, mais on peut produire des images de belle qualité malgré tout.

On le voit le chemin se complique.. J'ai un objet 3D, pour une application WPF je vais le réutilisé directement, pour une application Silverlight je vais le "prendre en photo" en faisant un PNG, du coup j'ai besoin de Xam 3D pour créer l'objet 3D Xaml et de Swift 3D pour réimporter mon projet Xam 3D et utiliser le moteur de rendu pour créer le PNG ! Vous allez me dire autant acheter tout de suite Swift 3D ! Et bien non... Ce dernier a bien un mode d'export en Xaml mais c'est plutôt pour les animations comme en Flash, par exemple une animation de 20 images créera 20 fois l'objet Xaml sur 20 couches différentes dont l'opacité est à 0% et il y aura 20 timelime générée chacune s'activant à la suite de la précédente pour passer l'opacité de l'image numéro x à 100%. C'est un peu le principe d'animation des petits livres qu'on doit feuilleter à toute vitesse pour avoir l'impression d'un dessin animé.

Bien entendu en bout de course il faut avoir Expression Blend, c'est obligatoire. Et puis ensuite viennent Visual Studio ou Expression Web.

Produire des modèles 2D avec Expression Design c'est sympa, le soft est agréable. Mais il n'a pas le niveau d'un Illustrator CS. Si on veut créer une image 2D un peu "léchée" il faut donc posséder Illustrator CS. On exporte en Xaml directement grâce à un petit plugin gratuit, ou bien on importe le fichier AI dans Expression Design (le copier coller entre Illustrator et E.Design fonctionne aussi et c'est très pratique).

Pour produire de la 3D c'est un peu pareil... Swift 3D ou Xaml 3D sont bien sympathiques, mais le mesh editor est un peu limité, et dès qu'on entre dans l'animation on voit bien que ces logiciels sont parfaits pour produire des banners de pub à la Flash mais pas pour créer des séquences animées à la Maya !

Bref, pour de beaux objets 3D, le mieux est d'utiliser Blender (gratuit mais une vraie usine à gaz) ou 3DS Max, le meilleur que je connaisse et le plus agréable à utiliser. Un peu cher c'est tout. Surtout si on ajoute cela au budget de la quinzaine de logiciels présents sur mon diagramme !

Avec 3DS Max vous allez créer des objets 3D que vous sauvegarderez en format 3DS, pas au format Max ni Dxf ! Et oui.. Swift 3D et Xaml 3D ne savent importer que le format 3DS... Au passage vous perdrez certains éléments ou certaines textures. Rien ne sert donc de trop compliquer. Il faut finir le travail sous Xaml 3D pour être sûr que ce qui est fait est supporté par l'export 3D en Xaml.

Si vous voulez faire des animations, cela se corse ! On a vu que les animations Xaml produites par Swift 3D ne sont pas du Xaml 3D mais des séries de plans 2D en couche, en revanche Swift 3D sait produire des AVI, comme 3DS Max. Un petit AVI peut souvent être bien plus joli qu'une animation d'objets 3D faites à la main dans Expression Blend. Dans certain cas c'est donc une alternative intéressante.

Reste que l'AVI devra être encodé correctement. C'est là qu'intervient Expression Media Encoder. Il peut même produire une page Web avec lecteur Silverlight intégré. Cela est intéressant pour une longue séquence présentée comme un petit film.

Bien entendu qui dit petit film dit montage... Le montage peut être réalisé avec Windows Movie Maker, simple et gratuit. Mais simple... Il faudra certainement passer à Adobe Première pour produire un vrai petit film bien monté.

Qui dit film dit son. vous pouvez facilement prendre un MP3 et le coller dans le projet Movie Maker. Mais, droits de diffusion mis à part, cela va rester très sommaire.

Pour produire une bande son synchronisée avec les images je travaille personnellement avec Cubase. Les synthés et la musique c'est mon "vrai truc" c'est donc la partie que je préfère :-) Créer l'image puis créer le son pour cette dernière est une expérience vraiment intéressante. Si vous êtes musicien vous vous éclaterez dans cette phase ! Sinon, pensez à engager un compositeur (tarifs sur demande !).

Etc !

Je vais m'arrêter là, car il est impossible de décrire ici tous les chemins du diagrammes, toutes les justifications de prendre tel ou tel chemin pour tel ou tel résultat. Et puis c'est aussi la valeur ajoutée que je suis en train de créer pour E-naxos, j'aime partager, la preuve ce billet, mais je ne vais pas vous livrer plus d'un an d'expérimentation et d'expérience comme ça non plus :-)

J'espère en tout cas que ce petit tour d'horizon et le diagramme vous aideront à vous faire une idée du cycle de production d'interfaces professionnelles "graphisées". Si vous êtes développeur cela vous aidera à envisager votre collaboration avec des infographistes, et si vous êtes clients cela vous aidera à mieux comprendre le prix qui vous sera demandé ;-)

Dans tous les cas, bon voyage au pays des logiciels de la prochaine génération !

..Et Stay Tuned !

De la 3D pour Silverlight 3 !

On commence à avoir une petite idée de ce que va être Silverlight 3 qui sera diffusé l'année prochaine et il semble que nos attentes seront comblées...

Au menu, pour ce qui est révélé aujourd'hui :

  • Support étendu pour les médias de tout type notamment le H.264 video utilisé de plus en plus,
  • Grandes avancées en matière graphique avec notamment le support de la 3D et l'accélération matériel par accès automatique au GPU (comme WPF donc),
  • Avancées importantes côté développement avec notamment de nouveaux contrôles et un data-binding encore plus riche

C'est tout ce que l'on sait vraiment sur cette nouvelle version, mais déjà nos principales préoccupations semblent avoir été entendues. De la 3D comme WPF, accélération matérielle graphique comme WPF, et plus de contrôles que demande le peuple n'est ce pas...

Mais selon le billet de Scott Guthrie tout cela n'est qu'une petite partie des nouveautés... Scott est un vicieux, en disant cela notre attente devient un vrai suplice ! :-)

Autre grande nouvelle, Visual Studio et Visual Web Developper Express supporteront un designer interactif pour Silverlight autorisant l'édition complète des pages. Le data-binding sera lui aussi amélioré et disposera de commandes dans ces EDI pour simplifier son utilisation.

Tout ça est vraiment alléchant, vivement l'année prochaine !

Et pour des nouvelles fraîches : Stay Tuned !

Morro .. Vache ? (ou les nuits blanches d'Avast, McAfee et Symantec)

Nom de code : MORRO.

Description: Anti virus / anti spam et autres saletés.

Prix: Gratuit.

Editeur: ... Microsoft !

Après l'échec de la suite commercial "Windows Live One Care" (ventes arrêtées cet été), Microsoft a semble-t-il pris la décision de transformer cet ensemble payant en une solution gratuite plus légère toutefois. Certaines options de LOC ne seraient pas retenues comme le partage d'imprimante, mais la sécurisation des partages imprimante ne semblent pas une priorité pour la grande majorité des entreprises et encore moins des particuliers.

MORRO sera donc moins gourmand en ressources système et fonctionnera sous XP, Vista et Seven. Microsoft ne prévoit pas d'inclure le produit dans Windows certainement pour éviter un énième procès en position dominante que pourraient vouloir intenter Avast, McAfee, Symantec et les autres... Il sera donc téléchargeable depuis une plateforme dédiée, comme LOC.

Bonne nouvelle en tout cas car nous diposerons d'une alternative gratuite à Avast qui règne en maître sur ce créneau.

Quand ? La solution sera progressivement distribuée courant 2009, pas plus de précision pour l'instant...

 (source IT World)

De la 2,5D pour Silverlight !

La 2D vous connaissez, forcément, un dessin plat sur une surface plate. La 3D vous connaissez aussi, les jeux s'en gavent, les pubs télés en sont pleines. Mais la 2,5D ? Hein ? ... Là vous séchez...

Bon, imaginez des dessins plats mais qu'on peut positionner dans l'espace. Une sorte d'intermédiaire entre 2D et 3D donc.

Des gens de l'équipe Expression ont développé un composant 2,5D pour Silverlight et c'est assez amusant. A tester et à voir, pour imaginer ce qu'on pourrait en faire...

Code source et exemple visuel sont dans le billet "2.5D Usercontrol for Silverlight 2" sur le blog de l'équipe Expression Blend et Design.

En espérant que vous ne trouverez pas la 2,5D à ... demi intéressante ! :-)

Stay Tuned !

MEF - Managed Extensibility Framework - De la magie est des plugins !

Une gestion de plugin simplifiée 

Actuellement encore en preview mais très utilisable depuis la Preview 2 du mois d’octobre, MEF est un nouveau procédé de gestion des plugins pour le Framework .NET.

Projet Open Source se trouvant sur CodePlex (http://www.codeplex.com/MEF) MEF facilite l’implémentation des addins (ou plugins) en automatisant la liaison entre les propriétés du programme qui importe des valeurs et les addins qui exportent les valeurs. Sachant que tout module peut être importateur et exportateur à la fois, permettant des chaînes de addins eux-mêmes supportant des addins…

MEF et les autres

Microsoft a intégré dans le Framework 3.5 une gestion des plugins  qui se base sur l’espace de nom System.Addin. L’approche est différente de MEF et le choix n’est pas évident entre ces deux solutions.  D’autant qu’il en existe une troisième ! En effet, Microsoft a aussi publié le Composite Application Guidance for WPF, spécifiquement dédié aux applications de ce type donc, dont la dernière version date de juin…

MEF est utilisable aussi sous WPF, même sous Silverlight mais je n’ai pas encore testé cet aspect là.

Comment choisir ?

Personnellement l’approche de MEF me convient très bien, c’est assez simple et cela répond aux besoins d’une gestion de plugins (ou addins). En ces temps d’avalanche de technologies toutes plus alléchantes les unes que les autres chez Microsoft il est vrai que je suis assez tenté par la simplicité de MEF qui évite de trop s’encombrer les neurones déjà bien saturés ! Simple et complet, je préfère donc MEF, mais je suis convaincu que dans certains cas la solution spécifique à WPF est mieux adaptée ou que System.Addin apporte certains petits plus (sécurité par exemple). J’avoue bien humblement que je n’ai pas encore trouvé le temps de tester à fond System.Addin ni la solution WPF. A vous de voir donc, et le mieux c’est de regarder de près en testant chaque approche. Ici je vais vous parler de MEF, pour les autres solutions suivez les liens suivants :

Composite Applicationn Guidance for WPF (http://msdn.microsoft.com/en-us/library/cc707819.aspx)

Pour System.Addin je vous conseille les 12 billets de Jason He qui sont plus parlant que l’aride documentation de l’espace de nom sur MSDN. (http://blogs.msdn.com/zifengh/archive/2007/01/04/addin-model-in-paint-net-1-introduction.aspx)

MEF – Le principe

Le but est de simplifier l’écriture d’applications dites  extensibles. MEF automatise la découverte des modules (les plugins) ainsi que la composition des valeurs, c'est-à-dire un lien automatique entre les valeurs exportées et le module importateur. De prime abord c’est pas forcément très clair, mais le code qui va venir va vous éclairez (je l’espère en tout cas !). En première approximation disons que la composition dans MEF est une sorte de Databinding qui relie une propriété déclarée dans l’importateur à une ou plusieurs valeurs du ou des modules exportateurs (les plugins).

MEF – Les avantages

MEF est assez simple, je l’ai dit, et c’est un gros avantage (mais pas simpliste, nuance). Il est Open Source c’est un plus. Mais surtout MEF évite de réinventer la poudre à chaque fois qu’on désire implémenter une gestion de plugins. Et toute application moderne se doit d’être extensible ! Qu’il s’agisse d’applications à usage interne ou bien de logiciels d’éditeur, c’est souvent l’extensibilité et les plugins qui font le succès d’une application, ou aide grandement à celui-ci. Disposer d’une solution fiable pour résoudre ce problème d’architecture très courant est donc l’avantage principal de MEF.

Les extensions créées avec MEF peuvent être partagées par plusieurs applications, elles peuvent elles-mêmes utiliser des extensions et MEF saura les charger dans le bon ordre automatiquement.

MEF propose un ensemble de service de découverte simplifiant la localisation et le chargement des extensions. On trouve aussi un système de lazzy loading et un mécanisme de métadonnées riches permettant aux plugins d’informer l’hôte sur sa nature ou transmettre des données complémentaires.

Un Exemple ! Un Exemple !

Bon, je ne vais pas refaire la doc de MEF, qui n’existe pas d’ailleurs (enfin si mais c’est encore très partiel), et pour illustrer le propos je vais expliquer le fonctionnement de MEF au travers d’un exemple le plus simple possible (ce billet s’annonce déjà bien long !).

Installer MEF

Avant toute chose, et pour faire tourner l’exemple (et vous amuser avec MEF), il faut que vous installiez MEF. Rassurez-vous, c’est très simple, sur le site de MEF (indiqué en introduction) vous trouverez dans l’onglet Releases le dernier zip à télécharger. Il suffit de prendre le contenu du zip et de le copier quelque part sur votre disque. C’est tout.  Le source est fourni ainsi que la lib compilée. Il suffit dans une application d’ajouter une référence à la DLL « System.ComponentModel.Composition.dll » se trouvant le répertoire /Bin du fichier téléchargé et l’affaire est jouée. Un Using de System.ComponentModel.Composition sera nécessaire dans l’application hôte ainsi que dans les applications fournissant un service (les DLL des plugins).

L’application exemple

Je vais faire très simple comme annoncé : prenons une application console. Cette application doit appliquer des calculs à des valeurs. Tous les algorithmes de calcul seront des plugins. Algorithme est un bien grand mot puisque dans cet exemple j’implémenterai l’addition et la multiplication. Dans la réalité les plugins découverts seraient par exemple ajoutés à un menu ou une toolbar. Ici nous nous contenterons de les lister à la console et de les appeler sur des valeurs codées en dur (dans une vraie calculette les valeurs seraient saisies par l’utilisateur).

Le contrat

Le plus intelligent pour une gestion de plugin est bien entendu d’avoir une ou plusieurs interfaces décrivant ce que sait faire un plugin. C’est le contrat (ou les contrats) entre l’hôte et ses plugins.

Ainsi nous allons ajouter à notre solution un projet de type DLL pour décrire cette interface. Cela se fait dans un projet séparé puisque l’interface doit être techniquement connu à la fois de l’hôte et des plugins et qu’il faut éviter à tout prix l’existence de dépendances entre ces deux niveaux de l’architecture. De plus l’interface peut ainsi être diffusée avec l’exécutable pour que des tiers puissent écrire des plugins.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace MEFInterface
{
    public interface ICompute
    {
        double Compute(double x, double y);
        string OperationName { get; }
    }
}

Le code ci-dessus est très simple. Comme on le voit ce projet DLL ne fait aucune référence à MEF ni à notre application ni à rien d’autre d’ailleurs (en dehors du Framework). L'interface ICompute expose une méthode Compute() et une propriété de type chaîne OperationName. Compute réalise le calcul sur les deux valeurs passées en paramètre et OperationName retourne le nom de l'algorithme pour que l'hôte puisse éventuellement fabriquer un menu listant tous les plugins installés.

Les plugins

Cela peut sembler moins naturel de commencer par les plugins que par l’application hôte mais je pense que vous comprendrez mieux dans ce sens là. Donc comment implémenter un plugin ?

Nous ajoutons à notre solution un nouveau projet de type librairie de classes qui sera faite d’une seule classe implémentant l’interface que nous venons de voir. Prenons l’exemple de la DLL de l’addition, sachant que celle gérant la multiplication est identique (au calcul près) et que nous pourrions créer ainsi une ribambelle de projets implémentant autant d’algorithmes de calculs que nous en voulons.

Le projet ModuleAddition contient ainsi un fichier Addition.cs, fichier contenant le code suivant :

(code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition; // MEF
using MEFInterface; // interface des plugins
 
namespace ModuleAddition
{
 
    [Export(typeof(ICompute))] // exporter la classe vue sous l'angle de l'interface partagée
    public class Addition : ICompute
    {
        // Réalise l'addition de deux doubles.
        public double Compute(double x, double y)
        {
            return x + y;
        }
 
        public string OperationName { get { return "Addition"; } }
 
    }
}

Ce code est redoutablement simple et n’a pas grand-chose de spécial. Il implémente l’interface que nous avons définie (d’où le using à MEFInterface, nom de notre projet interface, rien à voir avec un module de MEF donc, et la référence ajoutée cette DLL).

Ce qui est spécifique à MEF se résume à deux choses : d’une part le using de System.ComponentModel.Composition qui implique l’ajout dans les références de la DLL de MEF et d’autre part l’attribut Export qui décore la classe Addition (implémentant l’interface ICompute que nous avons créée).

L’attribut Export possède des variantes, ici l’usage que nous en faisons indique tout simplement à MEF que la classe en question est un fournisseur de service plugin et qu’il faut la voir non pas comme une classe mais comme l’interface ICompute. C’est un choix, il peut y en avoir d’autres, mais concernant une gestion de plugin cette approche m’a semblé préférable.

Concernant le code de la classe Addition on voit qu’elle implémente les éléments de notre interface donc la méthode Compute qui retournera ici l’addition des deux paramètres. Dans la classe Multiplication (l’autre plugin non visible ici) c’est la même chose, sauf que Compute calcule la multiplication. La propriété OperationName de l’interface est implémentée en retournant le nom de l’algorithme de calcul exposé par le plugin. Ce nom sera utile à l’hôte pour créer un bouton, une entrée de menu, etc.

On notera que MEF supporte la notion de métadonnées. Il existe ainsi des attributs permettant de décorer une classe en ajoutant autant de couple clé / valeur qu’ont le souhaite. Le nom du plugin, sa version, le nom de l’auteur et bien d’autres données peuvent ainsi être transmis à l’hôte via ce mécanisme que je ne démontre pas ce billet.

Pour simplifier les tests notons que j’ai modifié les propriétés du projet de chaque plugin pour qu’en mode debug les DLL soient directement placées dans le répertoire de debug de l’application hôte. Jai choisi ici aussi la simplicité : les plugins doivent être dans le répertoire de l’exécutable. Bien entendu dans la réalité vous pouvez décider de créer un sous-répertoire à votre application pour les plugins, ce qui est même plus propre.

L’hôte

Le voici ! Application en mode console (quel mode merveilleux pour les tests !), son fonctionnement est rudimentaire : nous voulons que l’application découvre grâce à MEF tous les plugins installés et que pour chacun elle affiche le nom de l’opération et effectue un appel au calcul correspondant. Les valeurs servant à ces derniers sont figées dans le programme, pas question d’ajouter une gestion de saisie utilisateur dans cette démo.

Les using et références

Pour fonctionner le programme doit faire référence à la fois à la MEF et au projet interface (aux projets interfaces si nous supportions plusieurs types de plugins par exemple).

Ces références trouvent leur pendant dans les using :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition; // MEF
using MEFInterface;
using System.IO; // interface des addins

La section Main

Elle aura pour rôle de créer une instance de l’objet programme et d’appeler sa méthode Run (nom arbitraire bien entendu). Rien de spécial à comprendre donc.

static void Main(string[] args)
{
   new Program().Run();
}

La propriété importée

Nous avons vu que les plugins exportent une (ou plusieurs) valeur(s), dans notre exemple il s’agit d’instances de classes supportant l’interface ICompute. Du côté de l’hôte il faut « quelque chose » pour importer ces valeurs.

C’est le rôle de la propriété  Operations dans notre code :

[Import] // attribut marquant une propriété importée depuis les addins découverts
        public IEnumerable<ICompute> Operations { get; set; }

Comme vous le voyez cette propriété est une liste. Cela s’explique par le fait que nous supportons plusieurs plugins à la fois. Dans le cas contraire (un seul plugin) la propriété aurait été directement du type de l’interface ICompute. Un seul plugin peut sembler étrange mais cela peut correspondre à une partie spécifique de votre application que vous désirez pouvoir personnaliser à tout moment en fournissant simplement une nouvelle DLL qui écrasera celle installée chez les utilisateurs. Un mode d’utilisation à ne pas négliger… Mais ici nous supportons plusieurs plugins à la fois et notre propriété est ainsi une liste.  Autre spécificité liée à la MEF, la propriété est décorée par l’attribut Import qui indique à MEF qu’il devra faire le binding entre la propriété et les plugins supportant le type de celle-ci.

Le code du Run

L’application se déroule ici. Nous commençons par appeler une méthode Compose() dont le rôle sera justement de mettre en route toute la magie de MEF. Nous verrons cela plus bas. Ensuite nous ne faisons que boucler sur la collection représentée par notre propriété comme si nous y avions déjà placé du contenu et nous l’utilisons « normalement » c'est-à-dire comme si MEF n’existait pas. Rien de bien sorcier là non plus, un foreach énumère toutes les entrées (des instances vues comme des interfaces ICompute) et utilise les méthodes et les propriétés accessibles (le nom du plugin et son unique méthode Compute).

private void Run()
{
    Compose();
    Console.WriteLine("Operation Addins detected (test on x=5 and y=6) :");
    foreach (var operation in Operations)
    {
        Console.WriteLine(operation.OperationName+" : "+operation.Compute(5,6));
    }
    
    Console.ReadLine();
}

Comme vous le constatez, il n’y aurait pas MEF que nous aurions écrit le même code, sauf que nous trouverions quelque part, à la place de l’appel à Compose que nous allons voir maintenant, un code qui aurait « rempli » la propriété Operations en créant des instances de classes supportant ICompute.

Le code de Compose

Ecrire un bout de code qui créée des instances de classes supportant ICompute et remplir la liste Operations (la propriété liste de notre application), c’est ce que fait la méthode Compose. Mais c'est en réalité c’est MEF qui va le faire pour nous, et mieux encore, il va aller chercher tout ça dans des DLL de plugins.

Regardons le code de Compose :

private void Compose()
{
    // création du catalogue. Les addins sont dans le répertoire de l'exe
    var catalog = new DirectoryPartCatalog(System.Reflection.Assembly.GetExecutingAssembly().Location);
    // créé le conteneur
    var container = new CompositionContainer(catalog.CreateResolver());
    // ajoute this pour qu'il puisse être lié via ses attributs 'import'
    container.AddPart(this); 
    // réalise la composition (connexion de tous les exports à tous les imports)
    container.Compose();
}

4 lignes. 4 lignes seulement pour : 

  1. créer un catalogue de plugins d’après un répertoire disque,
  2. créer un conteneur qui est le point de fusion où se rencontre les exportateurs et les importateurs,
  3. ajouter l’instance de notre application au conteneur (puisqu’elle est consommatrice via sa propriété marquée Import), d) demander au conteneur d’effectuer le binding entre tous les exportateurs et tous les importateurs. Incroyable non ?

Et c’est tout !

Ce qui va se passer au lancement de l’application est assez simple : MEF va balayer tout le répertoire indiqué dans le catalogue (qui peut contenir plusieurs répertoires), chercher les DLL et tester pour chacune celles qui exportent des éléments compatibles avec les importations. Qu’il s’agisse ici de notre application (consommatrice via son Import) ou même des plugins entre eux qui peuvent aussi avoir des relations consommateur / fournisseur.

C’est là que réside la magie de MEF qui va ensuite faire le nécessaire pour renseigner automatiquement toutes les propriétés ayant l’attribut Import.  Dans notre cas MEF va détecter que deux DLL sont compatibles avec l’Import de notre application. Il va créer des instances des classes supportant l’interface ICompute, en faire une liste qui sera placée dans la propriété Operations automatiquement.

Il ne reste plus à notre application qu’à utiliser la propriété Operations comme n’importe quelle autre propriété, sauf qu’ici son contenu a été créé par MEF automatiquement.

Conclusion

Simple et un peu magique, c’est ça MEF.

MEF règle un problème récurrent d’architecture, l’extensibilité, de façon simple et élégante. Bien entendu MEF permet de faire plus choses que ce que j’en montre ici. A vous de le découvrir en le téléchargeant et en étudiant les exemples et le début de documentation fourni !

Le code du projet VS2008 : MEFExample.zip (16,08 kb)

Contourner le problème de l'appel d'une méthode virtuelle dans un constructeur

Ce problème est source de bogues bien sournois. J'ai déjà eu l'occasion de vous en parler dans un billet cet été (Appel d'un membre virtuel dans le constructeur ou "quand C# devient vicieux". A lire absolument...), la conclusion était qu'il ne faut tout simplement pas utiliser cette construction dangereuse. Je proposais alors de déplacer toutes les initialisations dans le constructeur de la classe parent, mais bien entendu cela n'est pas applicable tout le temps (sinon à quoi servirait l'héritage).

Dès lors comment proposer une méthode fiable et systématique pour contourner le problème proprement ?

Rappel

Je renvoie le lecteur au billet que j'évoque en introduction pour éviter de me répéter, le problème posé y est clairement démontré. Pour les paresseux du clic, voici en gros le résumé de la situation : L'appel d'une méthode virtuelle dans le constructeur d'une classe est fortement déconseillé. La raison : lorsque qu'une instance d'une classe dérivée est créée elle commence par appeler le constructeur de son parent (et ainsi de suite en cascade remontante). Si ce constructeur parent appelle une méthode virtuelle overridée dans la classe enfant, le problème est que l'instance enfant elle-même n'est pas encore initialisée, l'initialisation se trouvant encore dans le code du constructeur parent. Et cela, comme vous pouvez l'imaginer, ça sent le bug !

Une première solution

La seule et unique solution propre est donc de s'interdire d'appeler des méthodes virtuelles dans un constructeur. Et je serai même plus extrémiste : il faut s'interdire d'appeler toute méthode dans le constructeur d'une classe, tout simplement parce qu'une méthode non virtuelle, allez savoir, peut, au gréé des changements d'un code, devenir virtuelle un jour. Ce n'est pas quelque chose de souhaitable d'un pur point de vue méthodologique, mais nous savons tous qu'entre la théorie et la pratique il y a un monde...

Tout cela est bien joli mais s'il y a des appels à des méthodes c'est qu'elles servent à quelque chose, s'en passer totalement semble pour le coup tellement extrême qu'on se demande si ça vaut encore le coup de développper ! Bien entendu il existe une façon de contourner le problème : il suffit de créer une méthode publique "Init()" qui elle peut faire ce qu'elle veut. Charge à celui qui créé l'instance d'appeler dans la foulée cette dernière pour compléter l'initialisation de l'objet.

Le code suivant montre une telle construction :

 // Classe Parent
    public class Parent2
    {
        public Parent2(int valeur)
        {
            // MethodeVirtuelle();
        }
 
        public virtual void Init()
        {
            MethodeVirtuelle();
        }
 
        public virtual void MethodeVirtuelle()
        { }
    }
 
    // Classe dérivée
    public class Enfant2 : Parent2
    {
        private int val;
 
        public Enfant2(int valeur)
            : base(valeur)
        {
            val = valeur;
        }
 
        public override void MethodeVirtuelle()
        {
            Console.WriteLine("Classe Enfant2. champ val = " + val);
        }
    }

La méthode virtuelle est appelée dans Init() et le constructeur de la classe de base n'appelle plus aucune méthode.

C'est bien. Mais cela complique un peu l'utilisation des classes. En effet, désormais, pour créer une instance de la classe Enfant2 il faut procéder comme suit :

 // Méthode 2 : avec init séparé
 var enfant2 = new Enfant2(10);
 enfant2.Init(); // affichera 10

Et là, même si nous avons réglé un problème de conception essentiel, côté pratique nous sommes loin du compte ! Le pire c'est bien entendu que nous obligeons les utilisateurs de la classe Enfant2 à "penser à appeler Init()". Ce n'est pas tant l'appel à Init() qui est gênant que le fait qu'il faut penser à le faire ... Et nous savons tous que plus il y a de détails de ce genre à se souvenir pour faire marcher un code, plus le risque de bug augmente.

Conceptuellement, c'est propre, au niveau design c'est à fuir...

Faut-il donc choisir entre peste et choléra sans aucun espoir de se sortir de cette triste alternative ? Non. Nous pouvons faire un peu mieux et rendre tout cela transparent notamment en transférant à la classe enfant la responsabilité de s'initialiser correctement sans que l'utilisateur de cette classe ne soit obligé de penser à quoi que ce soit.

La méthode de la Factory

Il faut absolument utiliser la méthode de l'init séparé, cela est incontournable. Mais il faut tout aussi fermement éviter de rendre l'utilisation de la classe source de bugs. Voici nos contraintes, il va falloir faire avec.

La solution consiste à modifier légèrement l'approche. Nous allons fournir une méthode de classe (méthode statique) permettant de créer des instances de la classe Enfant2, charge à cette méthode appartenant à Enfant2 de faire l'intialisation correctement. Et pour éviter toute "bavure" nous allons cacher le constructeur de Enfant2. Dès lors nous aurons mis en place une Factory (très simple) capable de fabriquer des instances de Enfant2 correctement initialisées, en une seule opération et dans le respect du non appel des méthodes virtuelles dans les constructeurs... ouf !

C'est cette solution que montre le code qui suit (Parent3 et Enfant3 étant les nouvelles classes) :

    // Classe Parent
    public class Parent3
    {
        public Parent3(int valeur)
        {
            // MethodeVirtuelle();
        }
 
        public virtual void Init()
        {
            MethodeVirtuelle();
        }
 
        public virtual void MethodeVirtuelle()
        { }
    }
 
    // Classe dérivée
    public class Enfant3 : Parent3
    {
        private int val;
 
        public static Enfant3 CreerInstance(int valeur)
        {
            var enfant3 = new Enfant3(valeur);
            enfant3.Init();
            return enfant3;
        }
 
        protected Enfant3(int valeur)
            : base(valeur)
        {
            val = valeur;
        }
 
        public override void MethodeVirtuelle()
        {
            Console.WriteLine("Classe Enfant3. champ val = " + val);
        }
    }

La création d'une instance de Enfant3 s'effectue dès lors comme suit :

 var enfant3 = Enfant3.CreerInstance(10);

C'est simple, sans risque d'erreur (impossible de créer une instance autrement), et nous respectons l'interdiction des appels virtuels dans le constructeur sans nous priver des méthodes virtuelles lors de l'initialisation d'un objet. De plus la responsabilité de la totalité de l'action est transférée à la classe enfant ce qui centralise toute la connaissance de cette dernière en un seul point.

Dans une grosse librairie de code on peut se permettre de déconnecter la Factory des classes en proposant directement une ou plusieurs abstraites qui sont les seuls points d'accès pour créer des instances. Toutefois je vous conseille de laisser malgré tout les Factory "locales" dans chaque classe. Cela évite d'éparpiller le code et si un jour une classe enfant est modifiée au point que son initialisation le soit aussi, il n'y aura pas à penser à faire des vérifications dans le code de la Factory séparée. De fait une Factory centrale ne peut être vue que comme un moyen de regrouper les Factories locales, sans pour autant se passer de ces dernières ou en modifier le rôle.

Conclusion

Peut-on aller encore plus loin ? Peut-on procéder d'une autre façon pour satisfaire toutes les exigences de la situation ? Je ne doute pas qu'une autre voie puisse exister, pourquoi pas plus élégante. Encore faut-il la découvrir. C'est comme en montagne, une fois qu'une voie a été découverte pour atteindre le sommet plus facilement ça semble une évidence, mais combien ont du renoncer au sommet avant que le découvreur de la fameuse voie ne trace le chemin ?

Saurez-vous être ce premier de cordée génial et découvrir une voie alternative à la solution de la Factory ? Si tel est le cas, n'oubliez pas de laisser un commentaire !

Dans tous les cas : Stay Tuned !

Et pour jouer avec le code, le projet de test VS 2008 : VirtualInCtor.zip (5,99 kb)

Silverlight 2 for Mobile : Une CTP pour le 1er timestre 2009 !

Il y a encore quelques jours je vous informais de la mise à jour française des outils pour Silverlight 2 et j'en profitais pour rappeler qu'il s'agit d'une solution multi plateforme unique en son genre. Une voix s'est fait entendre pour rappeler que sous Linux ce n'est pas encore au niveau de SL 2, c'est vrai. Mais preuve de ce dynamisme multi plateforme c'est maintenant au tour de Silverlight pour Mobile d'être annoncé par MS !

Et l'avancée est de taille puisque il n'y aura pas besoin de modifier son code pour qu'une application SL puisse tourner sur Mobile, même si elle utilise des vidéos. C'est incroyable quand on sait que sur un Mobile la place est réduite pour arriver à y faire entrer le framework .NET, même la version allégée de SL. Prouesse technique donc. Et encore plus d'ouverture pour les développeurs.

Autre annonce de taille, Silverlight est pour l'instant une technologie "navigateur", mais il semble que les équipes en charge de ce produit désire le faire évoluer en quelque chose de stand-alone, hors de tout navigateur. Une plateforme .NET multi OS fonctionnant directement sur Mac, PC, Linux, Mobile ? Il est vrai que techniquement il "suffit" de se passer du browser. Mais quelle perspective ! Avec WCF et les sockets pour la communication la panoplie semble alléchante.

Pour ce qui est des plateformes mobiles supportées, on retrouve bien entendu Windows Mobile 6.x. Là aussi, bien que les choses ne soient pas arrêtées, il semblerait que le support Windows Embedded CE soit sous le coude des développeurs SL...

Si vous voulez en savoir plus je vous conseille la video de "Silverlight 2 for Mobile" enregistrée aux dernières PDC et déjà diffusée sur le célèbre Channel 9 !

Obama for President !

Il est  5h50 du matin... 338 grands électeurs contre 155, la victoire de Obama est large et sans discussion. McCain a, selon la tradition, appelé le gagnant et fait en premier son discours annonçant et reconnaissant la victoire de Obama. Il est 6h01 maintenant (je relis ce billet) et Obama s'exprime à la tribune.

Sans bavure, sans discussion. Obama devient le premier président noir des USA.

L'instant est historique et démontre la capacité du peuple américain à se dépasser, à bouleverser les clivages les plus infranchissables. Autant l'élection de GW Bush fut une erreur déprimante, autant celle de Obama montre au monde la voie d'une démocratie vivante.

A quand un président se prénomant Mohamed ou Fet-Nat en France ? Serons-nous capable, au-delà de notre fâcheuse habitude de donneurs de leçons, de prouver que nous sommes nous aussi un grand peuple ? ...

Plus loin que le symbole humaniste et l'espoir qu'il soulève, notons sur le plan strictement politique qu'au moment où nous avons à peine élu un président à la solde de Bush et voulant, entre autres choses, privatiser notre système social, les USA, le pays le plus libéral et le plus capitaliste du monde, viennent d'élire un noir de gauche qui veut instaurer un système de sécurité sociale universel...

Il y a de quoi se sentir petit. Tout petit. Il paraît qu'on a le président qu'on mérite. ça fiche un coup.

Mais les américains viennent de montrer que tout est possible, Yes We Can ! doit devenir notre slogan à nous aussi. Balayons les guignols qui nous gouvernent, autant que la fausse gauche caviar qui veut prendre sa place, osons avancer vers plus d'humanisme, vers plus de Fraternité et d'Egalité, et en ces temps où le sénat vient de voter la riposte graduée consistant à appliquer une peine collective à toute une famille sans qu'aucun juge n'intervienne, donc surtout osons protéger la Liberté.

La réalisation du vrai rêve américain c'est l'élection de Obama. Sachons copier ce qu'elle a de meilleure au lieu de vouloir appliquer ce qu'elle a de pire.

La démocratie est ce matin victorieuse. L'amérique est victorieuse. Elle peut être fière d'elle-même. Que cela puisse nous inspirer...

 

Bonne nouvelle, Mise à jour en FR des outils Silverlight ! (et autres infos)

Silverlight, le truc le plus exitant avec LINQ dans toutes les technos MS du moment... La version 2.0 qui est sortie depuis quelques semaines est la première qui propose un CLR portable fonctionnant sous Mac ou Linux avec l'adoption de C# au lieu de JavaScript comme langage de programmation. Ce n'est pas la seule nouveauté de SL 2 (voir mes billets à ce sujet) mais c'est essentiel et c'est ce qui rend la V 2 réellement intéressante.

Les outils pour gérer SL 2.0 étaient déjà dispos depuis quelques temps mais en US seulement et les patches ne pouvaient pas passer sur les versions FR.

ça y est ! Microsoft de vient de mettre de ligne les outils SL 2 pour Visual Studio FR et aussi (et surtout) le SP1 de Blend 2 FR !

Si vous ne possédez pas Blend, achetez-le, c'est le seul vrai moyen de développer sérieusement sous WPF et Silverlight (en tandeme avec Visual Studio). Je vous conseille aussi au passage Expression Design (plus simple à prendre en main que Illustrator) ainsi que l'excellent Xaml 3D de Electric Rain (ou son grand frère Swift 3D), des softs de création 3D que j'adore (j'aurai des choses à vous montrer dans quelques temps). SL 2 ne gère pas encore la 3D come WPF mais il existe un projet CodePlex d'un tel moteur qui est pas mal avancé (projet Balder)

Pour ce qui est des Tools Visual Studio avec le SDK c'est ici : http://www.microsoft.com/downloads/details.aspx?displaylang=fr&FamilyID=c22d6a7b-546f-4407-8ef6-d60c8ee221ed

Pour la mise à jour de Blend 2 SP1 c'est ici : http://www.microsoft.com/downloads/details.aspx?displaylang=fr&FamilyID=eb9b5c48-ba2b-4c39-a1c3-135c60bbbe66

A noter que les outils SL 2 peuvent être installés sur VS Express Web Edition pour obenir une solution totalement gratuite de dev sous SL 2 !

Eclatez-vous bien avec Silverlight, et Stay Tuned !

Un utilitaire en or (Launchy)

Juste un petit billet pour vous signaler l'existence d'un petit utilitaire WIndows génial dont je ne plus me passer, sous XP comme sous Vista.

C'est Launchy. Alt-Barre d'espace, taper une lettre ou deux et hop! vous lancez vos applications et même vos liens internet favoris (paramétrable d'ailleurs)...

C'est une pure merveille de gain de temps. Fini de chercher dans les menus, dans les cent icones qui encombre le double écran !

C'est open source, c'est pas laid, gratuit, et c'est "the" utility qu'il faut avoir.

L'adresse du site : http://www.launchy.net

Téléchargement sur SourceForge: http://sourceforge.net/project/showfiles.php?group_id=132975

Queques copies d'écran :

Launchy opening an .mp3 using the Default skin
Launchy opening Mozilla Firefox using the Black Glass skin
Launchy opening a .doc file using the Gantchy Blue Skin

 

Essayez-le, vous ne pourrez plus vous en passer !

Et... Stay Tuned !