Dot.Blog

C#, XAML, Xamarin, WinUI/Android/iOS, MAUI

Deux règles pour programmer Silverlight & WPF

Des règles et des bonnes pratiques pour développer des applications Silverlight il en existe bien plus que deux, faire croire le contraire ne serait pas honnête. Ne serait-ce que par la richesse des EDI utilisés (Visual Studio, Expression Blend, Expression Design…) il faut accumuler de l’expérience et mémoriser de nombreuses patterns pour architecturer et designer correctement une application.

Alors, en vrac, voici deux règles qui me passent par la tête et que je voulais vous communiquer:

Programmation par Templates

Sous Silverlight lâchez les vieilles habitudes. L’une de celles-ci que je rencontre souvent est celle qui consiste à se jeter sur son clavier pour créer un UserControl (ou un Control par héritage) dès qu’on a besoin d’un nouveau composant qui semble absent de la bibliothèque.

Erreur. Méthode du passé.

En effet, Silverlight et WPF impliquent des changements radicaux de mode de pensée et d’habitudes. Sous ces environnements, la majorité des besoins sont couverts par les composants existants, il “suffit” juste d’en modifier le template.

Nous sommes passés d’une programmation par héritage à une programmation par modèle (template).

Deux exemples pour bien cerner ce que j’entends par là.

Sticky Note

Prenons pour premier exemple un cas d’école, déjà ancien mais en plein dans notre propos : sticky notes pour WPF (sticky notes listbox). Ce “composant” se présente comme une liste verticale de petites notes de type “post-it” légèrement décalées les unes par rapport aux autres.

L’effet est sympathique et donne un peu de fantaisie et de vie à ce qui ne serait qu’une liste rectangulaire dans une grille en programmation classique.

Ce magnifique “composant” n’utilise aucun code “'classique”. Inutile dans les sources de chercher la classe StickyNotes qui hériterait de Control ou même de chercher dans les fichiers de l’application le UserControl créé pour l’occasion.

Rien de tout cela.

Sticky Notes est créé uniquement en jouant sur le templating d’une simple… Listbox ! Une poignée de Xaml et des templates, c’est tout.

Language button

Dernièrement dans une application Silverlight je devais implémenter un bouton permettant de choisir depuis la page d’accueil la langue à utiliser (français ou anglais). Forcément on pense à un ToggleButton ou quelque chose d’équivalent (ce qui n’existe pas de base). On pourrait aussi associer deux RadioButton dans une grille.

Mais le plus intéressant consiste à se demander “quel control existant se rapproche le plus du comportement que je souhaite implémenter”. En y réfléchissant quelques secondes on s’aperçoit assez vite qu’une CheckBox possède deux états stables (oublions ici l’état indéterminé). Ce composant comporte toute la logique et les états visuels permettant de gérer deux états.

Par défaut une CheckBox c’est une case à cocher avec un bout de texte devant ou derrière.

Le plus dur consiste à se l’imaginer comme deux drapeaux (français et US) côte à côte, celui qui est sélectionné étant à 110% de sa taille et l’autre à 50%. Par convention arbitraire et chauvinisme inconscient certainement j’ai considéré que IsChecked = True était le Français, à False l’anglais. En partant d’une CheckBox que j’ai totalement vidée de son contenu, et en ajoutant les deux drapeaux (et quelques autres ingrédients et animations via le VSM) j’ai obtenu un merveilleux “ToggleButton” sans jamais “sous-classer” le moindre contrôle. Juste en écrivant un template.

Le ViewModel de la page d’accueil offre une propriété IsFrench de type booléen qui est simplement bindée à la propriété IsChecked du CheckBox (en mode TwoWay) et l’affaire est jouée !

Règle 1 : De l’héritage au templating

La programmation Xaml (Silverlight / WPF) est une programmation visuelle qui s’effectue par templating. Créer des UserControl et encore plus sous-classer des contrôles existants devient quelque chose de rarissime.

Nous sommes passés de l’ère de la programmation objet par héritage à celle de la programmation visuelle par templating. Bien comprendre toute la signification de ce changement est un point primordial et un préliminaire indispensable pour comprendre cette technologie et donc la programmer intelligemment.

Programmation par Properties

Il s’agit du même genre de glissement, une pente douce mais dont la longueur finit par conférer une vitesse tellement grande à celui qui s’y laisse glisser que le décor n’a plus rien à voir avec celui qu’on a tout le temps d’admirer en balade à dos d’âne…

Dans l’exemple précédent on touchait du doigt cette nouvelle approche mais sans la mettre en exergue.

En effet, dans la pattern M-V-VM plutôt que de créer un code incompatible avec le visuel d’un côté pour ensuite créer de l’autre des tripotés de convertisseurs (programmation classique non M-V-VM sous WPF et Silverlight dans une moindre mesure) il semble bien plus simple d’exposer dans les ViewModels des propriétés directement exploitables par l’UI. Si adaptation des données il doit y avoir c’est le ViewModel qui s’en charge (directement si cela est ponctuel, ou via une classe de service si la chose doit être réutilisées ailleurs).

Dans l’exemple précédent du Checkbox transformé en ToggleButton de langue, aucun événement n’est programmé, aucun gestionnaire n’est écrit pour réagir au clic. Tout se joue dans le ballet automatique de IsChecked de la CheckBox et de IsFrench du ViewModel sous l’égide discrète mais indispensable d’un binding two way…

Quant l’utilisateur clique sur la CheckBox (enfin sur le ToggleButton avec les deux drapeaux) le composant sous-jacent bascule sa propriété IsChecked à vrai ou faux selon le cas. Comme cette dernière est liée à IsFrench du ViewModel, une propriété de type booléen aussi pour assurer la compatibilité des comportements, le ViewModel reçoit pas un événement mais l’une de ces propriétés (IsFrench) se voit modifiée. Ce qui déclenche le Setter de cette dernière. Ce dernier s’occupant de modifier le fichier de ressource utilisé pour puiser les chaines de caractères. De là et par une série de notification de changement de propriétés, il avertit en retour la Vue que toutes les propriétés de type texte ont été modifiées. La vue (et ses binding) y réagit en rafraichissant les affichages…

Toute cette mécanique s’est déroulée sans aucun gestionnaire d’événement, sans aucune programmation “classique” (en dehors du ViewModel et de ces propriétés gérant INotifyPropertyChanged, ce qui peut être automatisé ou simplifié en utilisant Mvvm-light par exemple).

Règle 2 :  de l’événementiel à binding

La seconde règle d’or à bien comprendre pour tirer totalement partie de Xaml et de ses enfants (Silverlight et WPF) est ainsi d’opter pour un modèle de développement de type Model-View-ViewModel se basant presque exclusivement sur des couples de propriétés mis en relation via binding.

On est passé de l’ère du développement dit “événementiel” des premières versions de Windows à ce qu’on pourrait appeler la “programmation par Binding” ou par “properties”.

Conclusion

Pour résumer :

Règle 1 on cherche à templater des contrôles existants sans créer des UserControl ni dériver des contrôles existants.

Règle 2 : on base la dynamique de l’application sur le binding entre propriétés et non plus sur les gestionnaires des événements des contrôles.

Si vous avez déjà fait vôtre ses règles alors vous allez devenir, si ce n’est pas déjà le cas, de très bons développeurs Silverlight et Xaml très recherchés !

Si vous n’aviez pas encore vu les choses sous cet angle là, je serai très heureux si par chance j’ai réussi à tirer le voile qui vous empêchait de les voir ainsi. Vous ne ferez qu’entrer plus vite dans la catégorie précédente !

Bon Dev, et, œuf corse,

Stay Tuned !

Xaml Dynamique

Xaml n’est pas compilé, ce qui signifie que le runtime l’interprète à l’exécution. Cet interpréteur est donc présent aussi bien quand on exécute une application WPF que Silverlight. Cela peut permettre des choses intéressantes comme la création de code Xaml dynamiquement au runtime…

Cette possibilité n’est que peu souvent évoquée alors que dans certains cas elle autorise de dynamiser tout ou partie d’une interface. Rien n’interdit en effet de créer une mise en page complète par ce procédé, VSM et animations comprises. La génération de Vues (partielles ou totales) en fonction d’une description paramétrée par l’utilisateur fait partie des cas concrets où de la génération dynamique de code Xaml peut grandement simplifier la mise en œuvre du code.

Le potentiel est grand, il suffit juste de savoir que cela existe car techniquement cela est finalement très simple. L’essentiel tient dans XamlReader.Load() qui sait rendre un objet à partir d’un code Xaml.

Pour sortir des sentiers battus, voyons comment on peut utiliser cette stratégie pour créer dynamiquement un composant à partir de son nom de classe non pas en utilisant la réflexion sous C#, juste en générant du code Xaml…

L’exemple live ci-dessous permet de créer un contrôle (pré-saisi par défaut pour un textBlock) qui sera ajouté dans un StackPanel dans la seconde moitié verticale de l’affichage. Créez plusieurs TextBlock de cette façon en modifiant le texte ou d’autres paramètres. Puis créez-en un auquel vous ajouterez dans les paramètres: Name=’Test’. Enfin cliquez sur le bouton “changer couleur des TextBlock”.

Tous les TextBlocks créés passeront en Foreground noir, et celui qui a pour non ‘Test’ vera sa taille de fonte augmentée de 4.

[silverlight:source=/SLSamples/DynXaml/DynamicXaml.xap;width=480;height=400]

Le code de création du contrôle est le suivant :

namespace DynamicXaml{    
public static class DynamicXamlHelper
{
public static UIElement CreateControlFromName(string controlName, 
string controlNamespace, string xmlnsPrefix, string properties)
{
var sb = new StringBuilder();
sb.Append("<" + controlName + " xmlns");
if (xmlnsPrefix.Length > 0) sb.Append(":" + xmlnsPrefix);
sb.Append("=\"" + controlNamespace + "\" " + properties + "/>");
try
            { return (UIElement)XamlReader.Load(sb.ToString()); }
catch
            {
return null;
}
}
}
}

Les paramètres sont ceux que vous pouvez taper dans les textBox de l’exemple live ci-dessus:

Pour le reste, le plus simple est jouer avec le code source de l’exemple : DynamicXaml.zip (7,08 kb)

Silverlight et le multi-thread (avec ou sans MVVM)

La “parallélisation” des traitements est devenu un enjeu majeur et paradoxal du développement et Silverlight n’échappe pas à ce nouveau paradigme.

Nouveau Paradigme ?

Nouveau paradigme, cela fait toujours un peu “attention je suis en train de dire quelque chose d’intelligent et j'utilise une tournure à la mode qui fait savante pour le faire remarquer”… Mais parfois cela a aussi un vrai sens et une vraie justification !

Nouveau paradigme donc que la programmation multitâche, ou pour être encore plus chébran, parlons de programmation parallèle, voire de parallélisme tout en évitant le trop prétentieux “massivement parallèle” (nos PC ne comptent pas encore la centaine de “cores” – plutôt que “cœurs” pour faire totalement geek – qu’Intel prévoit pour dans quelques temps).

On le sait depuis un moment, la fréquence des processeurs n’évoluera plus comme elle l’a fait jusqu’à lors. Le fondeur de processeurs se retrouve ainsi face à un mur infranchissable, un peu comme l’était le mur du son pour l’aviation jusqu’à ce jour d’octobre 47 ou le pilote Yeager à bord du Bell-X-1 finit par traverser les briques de ce mur qu’on avait cru impénétrable. Pour nous il s’agit du “mur des performances”. On miniaturise, on diminue l’épaisseur des couches, on tasse les transistors, mais on butte sur la fréquence. Le seul moyen d’aller plus vite reste de multiplier les processeurs… Et comme multiplier les cartes mères atteint vite une limite en termes de prix et de consommation électrique, on essaye désormais de tasser des cœurs sur les puces comme on tassait les transistors à l’étape précédente. Ce n’est pas une option et il n’y a pas de “plan B”, c’est la seule solution jusqu’à ce qu’existent les ordinateurs quantiques (dont la faisabilité reste totalement à prouver).

Deux, quatre, huit, trente-deux, bientôt cent cœurs dans les machines de monsieur Tout-Le-Monde. 

Hélas ce n’est pas avec les techniques de programmation utilisées jusqu’aujourd’hui que les informaticiens vont s’en sortir. Les fondeurs ont trouvé le moyen de percer le “mur des performances”, et si les informaticiens ont une idée de comment ils vont relever le défi, ils ne se sont pas encore dotés des outils permettant de le faire : une formation ad hoc et une approche radicalement différente de la programmation.

On peut ainsi réellement, et sans emphase exagérée, parler de “nouveau paradigme” de la programmation parallèle car seule cette nouvelle approche va permettre aux logiciels de bénéficier des accélérations offertes par les processeurs modernes et encore plus par ceux à venir. Et pour cela il va falloir que les développeurs se mettent à niveau alors qu’ils sont déjà à la traîne de cette révolution…

J’ai dernièrement acquis une machine à 4 cœurs hyperthreadés, donc à 8 cœurs apparents. Avec certaines applications elle va à peine aussi vite que mon double cœurs E6700 overclocké (et pas toujours)… Je regarde la jolie fenêtre des performances de Windows, je vois ces huit magnifiques graphiques qui sont autant de preuves formelles de la puissance de la bête (et un enchantement de geek), mais un seul s’active vraiment, un autre le faisant mollement, et les 6 autres piquant un roupillon désespérant ! Ces applications je les maudis et dans la foulée leurs concepteurs incompétents qui ont programmé comme on le faisait il y a 20 ans : en pensant qu’un seul processeur anime la machine et que Windows va se débrouiller pour partager le temps (et que si l’utilisateur trouve que “ça rame” il n’a qu’à acheter une machine récente…).

En revanche je suis plein de compliments confraternels à l’égard des développeurs qui ont programmé les quelques applications qui savent se servir de mes huit cœurs ! Ces programmes qui s’essoufflaient sur le E6700 deviennent des bombes sur le i870, ou, au minimum de mes attentes, vont nettement plus vite, ce qui n’est déjà pas si mal.

Faire partie de ceux qu’on maudit ou de ceux qu’on félicite, il va falloir choisir son camp !

Programmer “parallèle” devient ainsi une obligation de fait, techniquement parlant. Mais il ne faut pas ignorer non plus les cas plus classiques où le parallélisme est le seul moyen de programmer certaines fonctions. C’est ce que nous verrons dans un instant au travers d’un exemple de code.

Mais paradoxal !

En effet, ce nouveau paradigme n’en est pas moins paradoxal . La raison est simple : d’un côté les multi-cœurs se généralisent dans les PC et de l’autre l’informatique mobile explose avec des téléphones aux interfaces graphiques à faire pâlir les progiciels PC les plus chers, des iPad, des EEEPC, et autres merveilleuses petites machines ayant toutes en commun d’être dotées d’un seul processeur rikiki et monocœur !

Alors que .NET 4.0 arrive avec des dizaines de méga de couches plus sophistiquées les unes que les autres, simultanément Silverlight et son mini Framework sont annoncés (avec XNA) comme l’interface des Phones 7, voire bientôt de certaines tablettes PC…

Faire le grand écart entre ces deux tendances extrêmes et néanmoins concomitantes relève du tour de force et place l’informaticien dans une situation paradoxale. Et pourtant ce sont deux défis que le développeur doit dès aujourd’hui relever, et simultanément ! Même en s’y mettant tout de suite, il part en retard (les “utilisateurs de base” possèdent depuis plusieurs années des machines à double cœurs !). Il faut donc programmer en visant la “scalability” ou “capacité à s’adapter”, s’adapter à la charge, s’adapter au matériel. Tirer le meilleur d’un Phone 7, faire cracher ses chevaux à un PC de bureau 8 cœurs. En gros il faut maîtriser la programmation parallèle.

En pratique on fait comment ?

On sort ses neurones du mode économie d’énergie certifié Energy Star et on se sert de l’ordinateur le plus massivement parallèle de l’univers : son cerveau :-)

Cela signifie adopter une nouvelle “attitude” face à la programmation quotidienne. La moindre classe doit être pensée dans un contexte multi-tâche. Cela doit devenir un automatisme, comme de finir chaque instruction par un point-virgule en C#.

Mais comme il n’est pas question dans ce blog de faire un cours complet sur le multi-tâche, attardons-nous sur un exemple concret et voyons comment introduire un peu de ces réflexes qui tendent vers une meilleur utilisation des ressources physiques sous-jacentes offertes par la machine hôte de nos chefs-d’œuvres compilés….

Un exemple concret

programmer en vue d’une utilisation multi-tâche n’est pas forcément simple. On peut être ainsi tenter d’y échapper jusqu’au jours où on tombe sur un cas tout simple qui va déboucher sur un magnifique plantage.

Comme le titre du billet l’indique, cela n’a rien de spécifique à MVVM, même si l’exemple sera programmé dans un mode de ce type (mais très édulcoré, ce n’est donc pas un exemple MVVM, pour cela se reporter à l’article complet que j’ai écrit sur le sujet).

Imaginons ainsi une vue présentant des données issues d’un ViewModel ou directement d’un Modèle (ce que MVVM autorise). Supposons ce dernier cas pour simplifier la “plomberie” de l’exemple. Ce Modèle simule une source de données un peu particulière puisqu’elle fonctionne en permanence et dépend de données externes qui arrivent un peu quand elles veulent. Cela n’a rien d’exotique, prenez un simple Chat, il y aura bien un Modèle fournissant les messages de l’interlocuteur fonctionnant en permanence et la fréquence d’arrivée des données (les messages) est aléatoire (quand l’interlocuteur a envi d’écrire).

Le modèle

Le Modèle de notre exemple sera bien plus simple encore : il fabrique des nombres aléatoires. Mais il les fabrique dans un thread séparé qui s’endort pour une durée aléatoire après chaque génération de nombre. Pour ce faire la classe RandomNumberPusher utilise un BackgroundWorker, classe du Framework permettant assez simplement de créer des tâches de fond (code source du projet à télécharger plus bas).

la Vue

Créons une vue (la MainPage sera suffisante pour cet exemple minimaliste) et attachons une instance de la classe RandomNumberPusher au DataContext de la grille principale. Ajoutons un TextBlock bindé (voir mon article sur le Binding Xaml) à la propriété Number. Et exécutons…

Le plantage…

A intervalle irrégulier l’instance du Modèle va générer en tâche de fond un nombre aléatoire et va déclencher la notification de changement d’état de la propriété Number (événement PropertyChanged). La Vue et son TextBlock étant bindés à cette dernière, la mise à jour du texte va se faire automatiquement. Oui mais… cela va planter. Pour une raison simple : seule le thread principal ayant créé les objets d’interface peut modifier ces derniers, or le générateur aléatoire fonctionne depuis un thread différent. On peut mettre le plantage encore plus en évidence si on tentait de modifier directement la propriété Text du TextBlock depuis la tâche de fond. Le modèle de programmation MVVM nous oblige à penser le code autrement (et c’est une bonne chose) et le plantage ne se verra pas forcément (le TextBlock ne changera pas de valeur). Mais à un moment donné il risque de s’afficher ceci :

Accès inter-thread non valide. Le couperet tombe.

Pour obtenir ce message de façon sûre il faut placer un point d’arrêt dans le thread de fond, s’arrêter dessus et repartir car le plus souvent le texte n’est tout simplement pas mis à jour. Comme je le disais plus haut, le plantage serait systématique si la tâche de fond mettait à jour directement le TextBlock ce que le montage de type MVVM évite. Le bug n’en est que plus sournois car il ne se passe rien, et rien, ce n’est pas grand chose ! Sur un écran un peu chargé il sera difficile de détecter le problème durant les tests.

Le mécanisme est simple :

Le thread de fond modifie la propriété Number, ce qui déclenche l’événement de changement de propriété (PropertyChanged), l’objet de binding est alors activé et tente de mettre à jour la valeur de la propriété Text du TextBlock. La plantage résulte du fait que la modification de ce dernier est effectué au travers d’un thread différent de celui gérant l’UI.

Le code xaml

   1:  <UserControl x:Class="SLMultiThread.MainPage"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   6:      xmlns:model="clr-namespace:SLMultiThread.Models"
   7:      mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
   8:      <UserControl.Resources>
   9:          <model:RandomNumberPusher x:Key="random" />
  10:      </UserControl.Resources>
  11:      <Grid x:Name="LayoutRoot" DataContext="{StaticResource random}">
  12:          <TextBlock Text="{Binding Number, Mode=OneWay}"/>
  13:      </Grid>
  14:  </UserControl>

On note : la déclaration du namespace de notre Modèle, la déclaration d’une instance du Modèle en ressource, la définition du DataContext du LayoutRoot depuis cette dernière et enfin, le TextBlock bindé à la propriété Number.

Le code C# de la classe RandomNumberPusher n’est pas en cause dans le mécanisme du plantage. Elle est programmée en répondant aux exigences d’une classe dite Thread-Safe. Donc pour l’instant oublions là.

Les solutions

Plusieurs aspects sont à prendre en compte lors d’une programmation multi-thread. Le plantage que nous venons de mettre en évidence n’est qu’un aspect des choses, limité uniquement à la spécificité de Xaml qui ne prend pas en charge la mise à jour de l’UI via un thread autre que le principal. On trouvait déjà ce problème sous Windows Forms d’ailleurs.

Concernant ce problème spécifique il y a plusieurs approches, mais au final il n’y a qu’un moyen : faire en sorte que la manipulation de l’UI ne passe que par son thread propre. Ponctuellement on peut utiliser, comme sous Windows Forms, des mécanismes d’invocation transférant le contrôle au thread de l’UI. Mais ce que nous cherchons est une solution globale et “industrialisable”. La plupart des auteurs qui a abordé le sujet arrive à la même conclusion, il faut passer par un mécanisme centralisé de dispatch. Cette solution peut se mettre en œuvre de différentes façons, voyons directement celle qui m’apparait la plus simple à appliquer.

Le SmartDispatcher et la classe de base

Le principe peut se schématiser comme suit :

Au début les choses sont, par force, identiques : la tâche de fond modifie la propriété (Number dans notre exemple) et déclenche PropertyChanged dans le Modèle. Mais au lieu que cet événement soit traité directement par le binding, nous intercalons le Dispatcher, c’est lui qui va vérifier que l’appel a lieu depuis le thread d’UI. Dans l’affirmative le traitement se poursuivra normalement, dans la négative, ce qui est le cas de notre exemple, il transfèrera la notification de changement de propriété au binding via une invocation appropriée. Comme le Dispatcher se trouve dans le thread de l’UI, et qu’il va faire en sorte de rendre “sien” les appels qui proviennent d’autres threads, le binding ne verra que des appels intra-thread UI et non inter-threads.

Cette solution fonctionne en deux étapes. Il y a le Dispatcher, mais il y a aussi le routage des notifications vers ce dispatcher. Modifier dans chaque classe l’appel à PropertyChanged pour qu’il pointe vers le Dispatcher serait fastidieux et source d’erreur. Le plus simple est donc de créer une classe de base dont hériterons les Modèles ou les ViewModels plus généralement (la connexion directe d’une Vue à un Modèle utilisée dans l’exemple est certes autorisé par MVVM mais ce n’est pas le “montage” le plus courant, le ViewModel étant souvent nécessaire pour la prise en charge des commandes, l’adaptation des données et la persistance de la Vue).

Le mécanisme du Binding et des patterns comme MVVM reposent sur un présupposé : que les classes reliées par binding à l’UI prennent en charge INotifyPropertyChanged. Il est donc raisonnable de mettre en œuvre une classe de base implémentant cette interface et s’occupant automatiquement de la liaison avec le Dispatcher.

Le seul problème que peut poser cette approche est que le ViewModel soit déjà dans l’obligation d’hériter d’une classe de base. Cela peut être le cas si vous utilisez un framework MVVM ou autre. C# ne gérant pas l’héritage multiple (bien heureusement), il faudra alors fusionner la solution du Dispatcher à la classe de base déjà utilisée. A moins que celle-ci ne prenne déjà en charge un mécanisme équivalent, mais dans ce cas je peux aller me coucher vous n’avez plus besoin de mes conseils ! :-)

Le code du Dispatcher

Parmi les implémentations existantes j’ai une préférence pour celle de Jeff Wilcox qui est un Senior Software Development Engineer chez Microsoft dans l’équipe de Silverlight… Autant se servir aux bonnes sources ! Son implémentation est sous licence Ms-PL (licence libre publique spécifique à Microsoft). J’avais dans mes tablettes un code assez proche mais pas aussi complet et on peut trouver des choses assez équivalentes dans le portage de Cairngorm sur Silverlight (Cairngorm sur CodePlex) , un framework MVC traduit depuis l’original de même nom conçu pour Flex de Adobe (un produit proche de Silverlight pour ceux qui ne connaissent pas).

   1:  using System;
   2:  using System.Windows;
   3:  using System.ComponentModel;
   4:  using System.Windows.Threading;
   5:   
   6:  namespace SLMultiThread.ThreadTools
   7:  {
   8:      // (c) Copyright Microsoft Corporation.
   9:      // This source is subject to the Microsoft Public License (Ms-PL).
  10:      // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  11:      // All other rights reserved.
  12:      
  13:      
  14:          /// <summary>
  15:          /// A smart dispatcher system for routing actions to the user interface
  16:          /// thread.
  17:          /// </summary>
  18:          public static class SmartDispatcher
  19:          {
  20:              /// <summary>
  21:              /// A single Dispatcher instance to marshall actions to the user
  22:              /// interface thread.
  23:              /// </summary>
  24:              private static Dispatcher instance;
  25:   
  26:              /// <summary>
  27:              /// Backing field for a value indicating whether this is a design-time
  28:              /// environment.
  29:              /// </summary>
  30:              private static bool? designer;
  31:   
  32:              /// <summary>
  33:              /// Requires an instance and attempts to find a Dispatcher if one has
  34:              /// not yet been set.
  35:              /// </summary>
  36:              private static void requireInstance()
  37:              {
  38:                  if (designer == null)
  39:                  {
  40:                      designer = DesignerProperties.IsInDesignTool;
  41:                  }
  42:   
  43:                  // Design-time is more of a no-op, won't be able to resolve the
  44:                  // dispatcher if it isn't already set in these situations.
  45:                  if (designer == true)
  46:                  {
  47:                      return;
  48:                  }
  49:   
  50:                  // Attempt to use the RootVisual of the plugin to retrieve a
  51:                  // dispatcher instance. This call will only succeed if the current
  52:                  // thread is the UI thread.
  53:                  try
  54:                  {
  55:                      instance = Application.Current.RootVisual.Dispatcher;
  56:                  }
  57:                  catch (Exception e)
  58:                  {
  59:                      throw new InvalidOperationException("The first time SmartDispatcher is used must be from a user interface thread. Consider having the application call Initialize, with or without an instance.", e);
  60:                  }
  61:   
  62:                  if (instance == null)
  63:                  {
  64:                      throw new InvalidOperationException("Unable to find a suitable Dispatcher instance.");
  65:                  }
  66:              }
  67:   
  68:              /// <summary>
  69:              /// Initializes the <see cref="SmartDispatcher"/> system, attempting to use the
  70:              /// RootVisual of the plugin to retrieve a Dispatcher instance.
  71:              /// </summary>
  72:              public static void Initialize()
  73:              {
  74:                  if (instance == null)
  75:                  {
  76:                      requireInstance();
  77:                  }
  78:              }
  79:   
  80:              /// <summary>
  81:              /// Initializes the <see cref="SmartDispatcher"/> system with the dispatcher
  82:              /// instance.
  83:              /// </summary>
  84:              /// <param name="dispatcher">The dispatcher instance.</param>
  85:              public static void Initialize(Dispatcher dispatcher)
  86:              {
  87:                  if (dispatcher == null)
  88:                  {
  89:                      throw new ArgumentNullException("dispatcher");
  90:                  }
  91:   
  92:                  instance = dispatcher;
  93:   
  94:                  if (designer == null)
  95:                  {
  96:                      designer = DesignerProperties.IsInDesignTool;
  97:                  }
  98:              }
  99:   
 100:              /// <summary>
 101:              ///
 102:              /// </summary>
 103:              /// <returns></returns>
 104:              public static bool CheckAccess()
 105:              {
 106:                  if (instance == null)
 107:                  {
 108:                      requireInstance();
 109:                  }
 110:   
 111:                  return instance.CheckAccess();
 112:              }
 113:   
 114:              /// <summary>
 115:              /// Executes the specified delegate asynchronously on the user interface
 116:              /// thread. If the current thread is the user interface thread, the
 117:              /// dispatcher if not used and the operation happens immediately.
 118:              /// </summary>
 119:              /// <param name="a">A delegate to a method that takes no arguments and
 120:              /// does not return a value, which is either pushed onto the Dispatcher
 121:              /// event queue or immediately run, depending on the current thread.</param>
 122:              public static void BeginInvoke(Action a)
 123:              {
 124:                  if (instance == null)
 125:                  {
 126:                      requireInstance();
 127:                  }
 128:   
 129:                  // If the current thread is the user interface thread, skip the
 130:                  // dispatcher and directly invoke the Action.
 131:                  if (instance.CheckAccess() || designer == true)
 132:                  {
 133:                      a();
 134:                  }
 135:                  else
 136:                  {
 137:                      instance.BeginInvoke(a);
 138:                  }
 139:              }
 140:          }
 141:  }

Le SmartDispatcher est une enveloppe intelligente autour de la classe Dispatcher du Framework de Silverlight. Cette dernière a été conçue justement pour gérer le code de mise à jour de l’UI depuis d’autres threads. SmartDispatcher “habille” le Dispatcher de base en s’assurant qu’une instance est disponible. En fait, appeler le Dispatcher de SL serait suffisant, il est conçu pour cela, mais cela n’est pas possible depuis une tâche de fond, il faut donc que l’instance existe déjà. Le SmartDispatcher permet de s’assurer de cette condition liminaire et fournit un point d’accès connu et centralisé à l’instance en question.

Bien que SmartDispatcher s’occupe d’obtenir l’instance du Dispatcher automatiquement il est préférable de l’initialiser très tôt dans le code de l’application, pour s’assurer que le SmartDispatcher est bien opérationnel et disponible avant tout événement impliquant l’interface. C’est pourquoi il est préférable d’ajouter dans App.xaml le code suivant dans le constructeur de l’objet App, juste avant l’appel à InitializeComponent() :

SmartDispatcher.Initialize(Deployment.Current.Dispatcher); 

Le SmartDispatcher est initialisé avec le Dispatcher fourni par le namespace Deployment qui s’occupe majoritairement du manifest et d’informations sur l’application en cours.

La classe de base

Il y a plusieurs façons de l’implémenter, mais puisque j’ai fait le choix de vous montrer l’implémentation de SmartDispatcher, autant utiliser la classe de base qui a été écrite par Wilcox pour fonctionner avec ce dernier, ça semble logique…

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel;
   4:   
   5:  namespace SLMultiThread.ThreadTools
   6:  {
   7:      // (c) Copyright Microsoft Corporation.
   8:      // This source is subject to the Microsoft Public License (Ms-PL).
   9:      // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  10:      // All other rights reserved.
  11:   
  12:          /// <summary>
  13:          /// A base class for data objects that implement the property changed
  14:          /// interface, offering data binding and change notifications.
  15:          /// </summary>
  16:          public class PropertyChangedBase : INotifyPropertyChanged
  17:          {
  18:              /// <summary>
  19:              /// A static set of argument instances, one per property name.
  20:              /// </summary>
  21:              private static readonly Dictionary<string, PropertyChangedEventArgs> argumentInstances = 
  22:                  new Dictionary<string, PropertyChangedEventArgs>();
  23:   
  24:              /// <summary>
  25:              /// The property changed event.
  26:              /// </summary>
  27:              public event PropertyChangedEventHandler PropertyChanged;
  28:   
  29:              /// <summary>
  30:              /// Notify any listeners that the property value has changed.
  31:              /// </summary>
  32:              /// <param name="propertyName">The property name.</param>
  33:              protected void NotifyPropertyChanged(string propertyName)
  34:              {
  35:                  if (string.IsNullOrEmpty(propertyName))
  36:                  {
  37:                      throw new ArgumentException("PropertyName cannot be empty or null.");
  38:                  }
  39:   
  40:                  var handler = PropertyChanged;
  41:                  if (handler == null) return;
  42:                  PropertyChangedEventArgs args;
  43:                  if (!argumentInstances.TryGetValue(propertyName, out args))
  44:                  {
  45:                      args = new PropertyChangedEventArgs(propertyName);
  46:                      argumentInstances[propertyName] = args;
  47:                  }
  48:   
  49:                  // Fire the change event. The smart dispatcher will directly
  50:                  // invoke the handler if this change happened on the UI thread,
  51:                  // otherwise it is sent to the proper dispatcher.
  52:                  SmartDispatcher.BeginInvoke(() => handler(this, args));
  53:              }
  54:          }
  55:   
  56:  }

Cette classe implémente INotifyPropertyChanged qui est à la base du mécanisme de propagation “automatique” des changements de valeurs utilisé par le binding et sur lequel repose des patterns comme MVVM.

La classe utilise un astuce pour améliorer les performances : elle créée un dictionnaire des PropertyChangedEventArgs de telle façon à ce que ce soit toujours la même instance d’arguments qui soit utilisée pour une propriété donnée. Ainsi, et surtout lorsque les valeurs changent très souvent, le Garbage Collector n’est pas submergé de petits objets à durée de vie ultra-courte. Il est certes optimisé pour cela, mais moins il y a de créations / destructions moins le GC a de travail et tout va plus vite.

Une autre optimisation est importante, elle concerne la détection de la nécessité ou non de transiter via le Dispatcher. Dans le cas où cela n’est pas nécessaire le message de notification suit son cours. Il est ainsi traité immédiatement alors que le passage via le Dispatcher créé un délai (même infime).

Le code exemple

Le projet complet (VS2008 SL3) est téléchargeable ici: SLMultiThread.zip (9,55 kb)

Les autres facettes du multi-thread

Ici nous n’avons fait qu’effleurer le sujet en abordant un problème très spécifique du multithreading sous Silverlight. Si vous regardez le code de plus près, notamment la classe RandomNumberPusher que nous n’avons pas étudiée dans ce billet, vous y trouverez des techniques simples propres au mutithreading comme l’utilisation d’un objet “locker” pour assurer le verrouillage des lectures et écritures entre les threads.

Le sujet est vaste et il serait possible d’en parler des heures. Je réserve cela pour un éventuel futur article, il faut bien en laisser un peu pour plus tard… Une raison de plus de …

Stay Tuned !

WPF : Le vote du mois

Je parle beaucoup de Silverlight ici, c’est vrai que j’ai un penchant pour cet enfant de WPF. On sait aussi que ce dernier a plus de mal à s’imposer. En vrai, vous en êtes où avec WPF ?

Pour le savoir, le vote est ouvert tout le mois alors VOTEZ !
(c’est facile, juste une question, juste une réponse en un clic)

c’est ici : Votre position vis à vis de WPF ?

On fera le point le mois prochain, alors…

Stay Tuned !

Article ! Le Binding Xaml, sa syntaxe, éviter ses pièges… (WPF/Silverlight)

imagePromis dans mon dernier billet, le voici enfin ! Ce nouvel article dédié au binding Xaml pèse 77 pages (le record était tenu jusqu’à lors par mon article M-V-VM avec Silverlight avec 70 pages) et se présente sous la forme d’un PDF et de 9 projets exemples.

Le binding cet inconnu … J’en parlais dans mon billet Le retour du spaghetti vengeur, le binding avec sa syntaxe pleine d’accolades et de chaînes de caractères non contrôlées à la compilation est un piège à bugs et la porte ouverte au code spaghetti.

Plutôt que de me lamenter et vous laisser vous embourber dans la sauce de ces spaghetti là, j’ai pris ma plume, et voici le résultat : 77 pages sur le binding Xaml, son fonctionnement, ses différentes syntaxes le tout illustré par des exemples clairs. L’article aborde aussi le débogue du binding assez délicat puisque Xaml échappe au débogueur de Visual Studio, et que le binding n’est que partiellement couvert par Intellisense.

Des conseils, du vécu, beaucoup d’exemples, voilà ce que vous trouverez dans cet article à télécharger en suivant le lien suivant :

Le Binding Xaml – Maîtriser sa syntaxe et éviter ses pièges (WPF/Silverlight)

(Attention, avant de cliquer sur le bouton “télécharger” attendez que la fiche détail de l’article soit bien affichée, sinon le site pensera que vous tentez un accès à une ressource non publique et vous demandera un login… Je reçois tous les jours des demandes d’ouverture de compte provenant de lecteurs trop impatients qui cliquent trop vite sur le bouton ! La prochaine version du site en Silverlight est en préparation et corrigera ce petit problème, mais d’ici là : slow down cowboy !).

Pour vous aider à vous faire une idée du contenu de l’article voici son sommaire :

Sommaire

  • Références    5
  • Code Source    6
  • Préambule    7
  • Le Binding Xaml : Ange ou Démon ?    7
    • Le Binding    8
    • Définition    8
    • Utilisations    9
    • Schéma du principe    9
    • Déclaration    10
      • Par code    10
      • En Xaml    11
  • Les modes de binding    13
    • Le mode OneTime    13
    • Le mode OneWay    13
    • Le mode TwoWay    14
      • Gestion du timing    14
    • Le mode Default    16
    • Le mode OneWayToSource    16
  • Hiérarchie de valeur    18
    • Règles de précédences    19
  • La notion de  DataContext    20
  • Les convertisseurs de valeur    21
    • Définition    21
    • Scénario    21
    • Implémentation    22
    • Utilisation    23
      • Instanciation    23
      • Invocation    23
      • Bonnes pratiques    24
      • Pour résumer    26
  • Les dangers du Binding    27
    • Des chaînes non contrôlées    27
    • Un langage dans le langage    28
  • On fait quoi ?    28
  • Déboguer le Binding    28
    • Vigilance    28
    • Une code Xaml court    29
    • Refactoring sous contrôle    29
    • Utiliser des outils intelligents    29
    • Utiliser Expression Blend    29
    • Vérifier les erreurs de binding dans la fenêtre de sortie    30
    • Créer un fichier des erreurs de Binding    30
    • La feinte du convertisseur inutile    32
    • Quelques outils supplémentaires    33
      • Spy++    33
      • ManagedSpy    33
      • Snoop    34
      • Mole    34
      • Reflector    34
      • Vos neurones    34
  • Les syntaxes du Binding    34
    • Le binding simple    34
      • Binding direct    35
      • Binding sur une propriété    35
      • Binding sur une sous-propriété du contexte    36
      • L’Element Binding    37
      • Convertisseurs de valeur    37
        • Paramètres de conversion    38
      • StringFormat    40
        • Injection de culture    41
        • Le ContentStringFormat    42
        • Gérer les nuls    43
    • Le Binding Multiple    44
      • La classe Personne    44
      • Le code du multi convertisseur    45
      • Le code Xaml    46
    • Le Binding XML    47
      • Binding sur une source Web    47
      • Binding sur une source XML en ressource    48
      • Binding sur une requête Linq To XML    49
    • Le Binding Relatif    52
      • Binding to Self    52
      • Le TemplatedParent Binding    53
      • Le Binding sur recherche d’ancêtre    54
      • Le Binding PreviousData    56
        • La source de données    58
        • La visibilité des flèches : la magie de PreviousData et du Binding Multiple    58
        • Le contenu des textes    61
      • Le Template binding    61
        • Utilité    61
      • Le Collection Binding    67
      • Le Priority Binding    69
  • Les propriétés de tous les bindings    74
  • Conclusion    76

Avril est froid et pluvieux mais les soirées rallongent, c’est le printemps même si cela n’y ressemble pas encore beaucoup, grâce à moi vous savez maintenant comment occuper vos soirées et vos weekend au lieu de faire l’idiot dans les embouteillages des vacances : Lisez cet article !

Et Stay Tuned !

Le retour du spaghetti vengeur

Le code spaghetti est de retour ! Fuyez braves gens !

Sous-titré : Du Rififi dans le Xaml.

Avertissement au lecteur : ce billet, bien que bâti sur un fond technique préoccupant et une expérience réelle, utilise un formalisme un peu romancé. Ne cherchez pas d’extraits de code ici. Mais si vous avez un peu de temps, laissez vous porter par l’histoire. Si vous êtes pressés, revenez plus tard lire ce billet !

Genèse d’un malaise

Comme vous le savez je suis un passionné de WPF et de Silverlight, la puissance de Xaml servi par un Framework aussi riche que .NET et des outils de qualité comme Blend ou Visual Studio ne peuvent laisser de marbre (comment peut-il y avoir encore des gens travaillant sous Java ou Delphi ?). J’en suis tellement passionné que j’évangélise à tour de bras, ici et ailleurs, et que mes compétences autant techniques que de gardien des brebis égarées me valent d’avoir l’honneur d’être Microsoft MVP, notamment en 2010 pour les technologies de développement d’applications clientes (Client Application Development, “CAD”" MVP).

Bref, si je dis tout cela c’est pour faire comprendre que bien que cultivant mon objectivité comme un joyau précieux garant de la liberté de mes neurones, je suis plutôt “pro” Microsoft et que, bien entendu, cela peut déplaire à certains comme en séduire d’autres… La diversité du monde en fait sa richesse, isn’t it.

Et un partisan de Microsoft, MVP de surcroît, fan de Silverlight, ne dira jamais le moindre mal de sa technologie fétiche… Et soyez-en convaincu je ne briserai pas cette loyauté, essentielle à mes yeux, mais il faut pourtant savoir tirer les sonnettes d’alarme de temps en temps. D’ailleurs c’est une question de crédibilité, que vaudrait un expert sans liberté de parole ni de pensée… Et puis au fond vous verrez que c’est bien plus, comme à chaque fois, l’humain qui en prend pour son grade dans ce récit que les outils, innocents, par nature.

Tout cela pour parler franchement d’un risque, d’une dérive, et surtout d’un grand danger : le retour du code spaghetti ! Et, comble de l’infamie, la peur de cette tare qu’on croyait du passé, je ne la brandis pas à propos de langages ésotériques comme F#, ou de solutions vieillissantes comme Java. Non. Le drame est bien là : c’est de WPF et de Silverlight dont je veux vous entretenir, et ce, au travers d’une anecdote récente.

J’ai fait il y a quelques temps un audit dont je tairais, vous le comprendrez aisément, le nom du client visité ainsi que la date exacte. Au demeurant une société ni trop jeune pour n’être pas assez structurée, ni trop vieille pour en être devenue ringarde. Une entreprise de bonne taille, assez dynamique et assez typique de ma clientèle, se targuant de posséder une équipe de développement à la pointe du progrès, la preuve, puisque maniant les balises Xaml avec la même dextérité que la truelle l’est par un vrai maçon diplômé.

Le trait n’est pas forcé, il n’y en a nul besoin. Ce fut au début un audit “classique” c’est à dire durant lequel j’ai vu du code “normal” donc assez médiocre. Je dis “normal” au sens de la loi normale statistique ce qui signifie que grâce à  messieurs Laplace et Gauss il m’arrive de voir des choses épouvantables comme de pures merveilles, mais que ces deux cas représentent un pourcentage faible au deux bouts de la cloche… Je ne savais pas encore que j’allais atteindre le bout de la cloche. Le mauvais. Bien entendu.

Espèce de spaghetti !

Le code “normal” est médiocre généralement. C’est finalement une définition en soi. Le code exceptionnel, étant, par le même genre de raisonnement, plus rare. C’est finalement une lapalissade. Donc, en général, je vois du code médiocre, donc normal (dans l’autre sens c’est intéressant aussi, non ?).

Et j’appelle médiocre un code qui se caractérise par plusieurs aspects distinctifs très techniques que je détaille avec moult délicatesse dans mes rapports d’audit pour expliquer les choses en y mettant les formes mais qui se résument en général à quelques cas généraux qu’on pourrait caractériser très doctement. Je m’exprimerais ici de façon bien plus directe puisqu’on est “entre nous” :

  •  
    • La “putain” c’est à dire le code sur lequel tout le monde est passé, sauf le train et vous, mais ça, c’est même pas sûr, puisque vous êtes là… C’est du code typique des boîtes où les gens sont mal payé et où les salariés défilent plus vite qu’une hirondelle dans un ciel de printemps…
    • Les migrations de migrations de portage d’intégration (de milles sabords, version 24 bis modifiée E). En général du code qu’on trouve dans les administrations. Avec des documentations de plusieurs centaines de pages, que personne n’a jamais lu bien entendu.
    • Le code mille-feuille. Savoureuse pâtisserie constituée de tant de couches qu’on ne peut les compter, comme les pattes du iule (autrement appelé mille-pattes). C’est un peu un mélange des deux précédents mais en plus structuré que le premier (beaucoup plus, et c’est la son problème) et en moins documenté que le second (beaucoup moins, et c’est aussi là son problème). C’est du code de SSII “in” avec de vrais morceaux de “nouvelles technos” dedans.
    • Le code “start-up”, celui-là est bourré de technos innovantes, hélas non maîtrisées, peu documentées et en bêta ne tournant que sur les versions US de l’environnement. Un code d’essayiste pur travaillant pour la beauté du discours qui va autour plus que pour l’efficacité, des gens qui devraient être en agence de pub plutôt que devant un environnement de développement.
    • Le code à papa, ou l’objet est utilisé comme du C procédural. C’est le code C# écrit par de vieux delphistes ou cobolistes reconvertis sur le tard par exemple.
    • Le code d’ingénieur, un des pires. Sortant de l’école et voulant montrer ses muscles en complexifiant tout et l’enrobant dans une prose technique pleine de sigles bizarres et de référence à des bouquins lus de lui seul et de ses potes de promo. Quand il arrêtera de sucer son pouce, il deviendra un bon développeur sévèrement burné comme disait Tapie. Mais en attendant son code c’est l’enfer…
    • Et enfin, le célèbre, le magnifique, le code spaghetti, marquant l’incompétence à maîtriser la complexité du sujet. Celui-là est typique des mauvaises équipes, tous contextes confondus.

Il y en a bien d’autres dans mon bestiaire, en 20 ans d’audit vous imaginez ce que j’ai pu voir (heureusement je rencontre aussi des équipes compétences et même parfois de l’excellent code, sinon ça serait désespérant !).

Et WPF et Silverlight dans tout ça ?

C’est là que je voulais en venir, mais il fallait bien passer par ce détour pour vous plonger dans l’ambiance trouble de cette descente aux enfers binaires. Sinon cela aurait été d’une platitude redoutable. Un peu de lecture ça change des extraits de code en Xaml. Justement. Parlons de lui et de ce dernier audit (qui n’est pas le dernier, confidentialité oblige comme je le disais plus haut). Disons que c’est assez récent pour être en Xaml mais pas suffisamment pour être en Silverlight 3.

Qu’avait ce code qui vaille ce billet un peu particulier ?

Le code qui rend fou

il m’a rendu fou. Tout simplement ! Et en plus il m’a filé les chocottes !

WPF et Silverlight sont des technologies merveilleuses, Xaml a un pouvoir descriptif exceptionnel à tel point qu’il permet d’économiser beaucoup de code behind.

Malgré le génie des équipes MS ayant travaillé sur le Framework j’aurais malgré tout préféré que cette révolution se fasse à 100% dans le respect du paradigme objet et du fortement typé. Or ce ne fut pas totalement le cas, et si je comprends bien les contraintes techniques sous-jacentes qui ont interdit cet idéal, certains choix sont hélas autant de portes ouvertes sur des risques que je viens de palper de près. Et ça fait peur.

Il y a en premier lieu l’architecture. Il ne suffit pas de prendre Prism et ses Dll pour avoir un bon logiciel. Il faut comprendre et maîtriser la chose. Ce qui ne s’improvise pas. Mais il y a pire, car plus lié à la technologie elle-même.

Par exemple, prenez la syntaxe du Binding, en Xaml donc. Vous avez une balise, vous la complétez d’une propriété et là, au lieu de mettre une valeur, vous ouvrez des accolades américaines suivies du mot Binding, le tout entre guillemets, une simple chaine de caractères. Quant à ce qu’il y a après le mot Binding, je suis convaincu qu’aucun aficionado de WPF ou de Silverlight n’est capable de me citer de tête toutes les combinaisons possibles et astuces disponibles. Parfois  c’est {Binding} tout court, parfois c’est une longue phrase intégrant imbriquées d’autres accolades faisant référence à des ressources statiques ou dynamiques, des paramètres, des convertisseurs, etc… Une puissance énorme, un peu comme les expressions régulières : puissant mais pas très clair (et c’est un euphémisme). Pas clair, on peut s’y faire… mais pas clair et pas fortement typé ni même contrôlé, c’est là que la chute aux enfers commence…

Le code que j’ai audité était bourré d’element binding, de datacontext pointant des conteneurs de services avec des indirections dans tous les sens “pour la souplesse”. Quand MVC et MVVM ne sont pas compris, mieux vaut tout mettre en procédural dans le code-behind de chaque fiche, c’est plus simple à débugger ! Le pire c’est que chacun dans l’équipe y allait de sa petite couche, de sa petite modification. Et je fais du “refactoring” par là, et je refactore par ici… Oui mais voilà, dans les balises Xaml ça commence à danser le cha-cha-cha toutes ses chaînes de caractères non contrôlées, tous ces paramètres de convertisseurs qui ont évolués sans qu’on ait mis à jour les références dans le Xaml, ces ressources statiques et d’autre dynamiques dans des dictionnaires chargés dynamiquement !

Le soft ne tenait plus que par un fil qu’un joyeux drille a du couper juste avant que je n’intervienne. Dommage. Je n’arrivais même pas à dépasser un ou deux écrans avant que ça me pète à la figure. Même avec des outils très intelligents comme NDepend, comprendre le soft était virtuellement impossible.

Quant à savoir d’où vient “le” problème ! C’est le soft lui-même tout entier qui était “le” problème… Ainsi que ceux l’avaient écrit (et ceux qui les dirigent, car un mauvais soft est toujours la cause d’une mauvaise direction bien plus que de mauvais développeurs).

En fait, le Binding Xaml est une porte ouverte sur l’inconnu. Une feature d’une grande puissance, sans laquelle beaucoup du charme disparaîtrait, mais assez déraisonnable dans cette implémentation libre sous forme de chaînes de caractères non compilées. La porte sur le néant, l’incontrôlé, et pire : l’incontrôlable. Un trou noir syntaxique. La damnation du testeur envoyé moisir au purgatoire des pisseurs de ligne. Et l’enfer de l’auditeur.

le Binding au pays des X-Files

Le Data binding Xaml est une jungle syntaxique pas très bien … balisée et totalement déconnectée de toute forme d’analyse à la compilation. du “Late Bugging” comme je m’amuse à appeler cette stratégie de type “late binding”, principe de ligature tardive utilisé d’ailleurs en d’autres endroits et même sous d’autres frameworks. Pire que les Dynamic de C# 4 (pratiques mais dangereux), pire que F# (stimulant mais pas industrialisable), le Binding de Xaml est un gouffre à bugs, un chien fou s’il n’est pas tenu en laisse fermement.

En réalité un marteau ne pourra jamais être jeté aux fers (!) pour le meurtre de qui que ce soit. Un marteau est un outil, et même s’il a servi et servira encore dans de nombreux crimes, un outil est un objet sans âme, sans conscience et donc sans responsabilité. Même un fusil mitrailleur est un objet innocent, même un canon de 105 ou une bombe atomique sont plus innocents que l’agneau qui vient de naître et qui, comme tout être vivant, et à la mesure de son intelligence, portera le poids de la responsabilité de ses agissements. Un outil de développement restera donc à jamais hermétique à tout procès d’intention. A celui qui s’en sert de le faire correctement.

Il en va de même de Xaml, de son data Binding et de bien d’autres de ces facettes. La responsabilité incombera toujours à l’humain qui tient le marteau. A l’informaticien qui tient la souris. Au développeur qui tape un code affreux.

Mais certaines features de Xaml, certains choix conceptuels comme l’utilisation de chaînes de caractères non contrôlées et non parsées à la compilation sont à mon sens des erreurs. Si des projets comme celui que j’ai audité et dont je vous parle ici devenaient courants, nul doute que cela signerait l’arrêt de mort de WPF et de Silverlight. La faute aux mauvais développeurs ? Pas seulement. A ceux aussi qui ont décidé de programmer Xaml de cette façon trop ouverte, trop permissive. On voit bien comment Silverlight a été verrouillé dès le départ par Microsoft. Si le moindre virus, le moindre phishing avait été réalisé avec Silverlight 1 ou 2 c’en était fini des espoirs portés par cette technologie. Microsoft a été méfiant pour préserver l’avenir de la technologie et c’est une bonne chose. Ce que j’ai vu dans le projet WPF dont je parle ici, c’est un peu de la même nature, mais à l’inverse : Microsoft n’a pas verrouillé Xaml comme cela a été fait avec Silverlight. Et si de tels détournements se généralisent c’est toute la technologie qui trinquera. D’ici un an environ, lorsque les projets lancés ces derniers temps seront finalisés, et qu’il faudra compter ceux qui n’aboutiront pas ou qui ne marcheront jamais bien, la note peut être salée pour Xaml. Microsoft a pris un sacré risque en faisant des choix de conception comme celui des balises non compilées (et dans lesquelles même Intellisence se prend les pieds dans le tapis).

Déjà sous Delphi je voyais souvent ce genre de code spaghetti avec des variables globales référencées n’importe où, des fiches utilisant des variables d’autres fiches jusqu’à créer des chaines de dépendances ingérables. J’ai vu des codes de ce type ne pouvoir jamais aboutir. Il m’est même arrivé une fois de réécrire en 15 jours proprement à partir de zéro un soft développés en 1 an par deux personnes sans le dire au client histoire que les 15 jours d’expertise qu’il m’avait payés servent à quelque chose… Je tairais ici le nom du client (une administration). J’étais plus jeune et je voulais me prouver des choses certainement, mais personne ne l’a jamais su jusqu’à ce billet (et encore vous en savez peu!). En tout cas ce projet là je l’ai sauvé. Mais être un pompier de l’ombre n’est pas ma vocation. Les gars étaient malgré tout étonnés ne pas vraiment s’y retrouver dans “leur” code. Amusante situation. Je n’avais rien gardé de “leur” code :-) Mais le soft marchait…

Mais avec Xaml, et une puissance décuplée, je viens de voir des horreurs du même genre alors que depuis des années, je commençais à trouver que C# incitait plutôt à faire du code “correct”, le niveau étant globalement meilleur que celui que je voyais sur Delphi. Et patatras ! Xaml arrive avec ces chausses-trappes dans lesquels les développeurs s’engouffrent. La faute à Xaml ? Pas totalement, mais si tout était typé et contrôlé à la compilation certaines mauvaises utilisations ne seraient pas possibles. Après tout, le fortement typé en soi ça ne sert à rien si on suppose que tous les développeurs sont “bons” ! Mais tous les langages modernes tentent de l’être car on sait que l’humain est faillible. En créant une brèche incontrôlable dans un édifice aussi beau de Xaml, ses concepteurs ont fait un pari risqué. Celui que tous les développeurs sauraient maîtriser le potentiel sans tomber dans ses pièges. Un pari forcément perdu d’avance.

Vision d’horreur

Ce que j’ai vu est donc indescriptible. Un peu comme si j’essayais de décrire Cthulhu. Ceux qui ont essayés sont souvent morts avant de finir d’écrire son nom (ah Lovecraft…) !

Imaginez vous un logiciel de 250 fiches WPF environ utilisant de l’ADO.NET, des bouts de LINQ to SQL, et des nouveautés en Entity Framework, le tout à la sauce Prism / MVC (mais en ayant lu le manuel certainement à l’envers car Prism c’est très bien !) avec des tentatives d’inversion de contrôle (et des dérapages non contrôlés) farci d’un code Xaml bourré de Binding renvoyant vers on ne sait quoi (et hélas pas contrôlé à la compilation), le tout planqué dans 4 ou 5 couches DAL, BOL pré-BOL, post-BOL, et j’en passe, histoire de faire court. La note s’élève a 6000 jours/homme (5 ans/homme environ). Pas un truc gigantesque mais qui commence à faire mal au budget malgré tout.

Agrémentez le tout de deux systèmes de versionning dont aucun n’avait vraiment une version complète du soft, des essais épars de tests unitaires avec MbUnit et VSTS. Vous obtiendrez le tableau. Une œuvre qui dépasse le classicisme habituel de ce point de vue, plus proche du Cubisme que de l’Hyperréalisme tout de même, avec au moins la bonne volonté de faire des petites choses (du mauvais testing prouve au moins qu’on a essayé de tester, ce qui est encore bien rare…). Mais l’enfer est pavé de bonnes intentions, c’est un peu ça le problème.

Le code était impossible à maintenir, les problèmes de performances impossible à cerner sans y passer plus de temps qu’à toute refaire, le code Spaghetti, avec un grand S avait frappé de son coup de poignard vengeur et lâche dans le dos. Une complexité non maîtrisée qui tel l’horizon d’un trou noir aspirait irrémédiablement le bon code vers l’enfer central laissant le hasard faire le tri entre les chemins à prendre… Démêler l’écheveau n’était pas possible, pas plus que de formuler des guidelines sérieusement applicables dans un tel contexte ni aucun conseil pour se sortir d’une telle situation.

Vous imaginez peut-être l’horreur qu’a été l’écriture du rapport d’audit. Entre dire une vérité que personne n’était prêt à entendre et mentir sachant que peu importe ce que je dirais cela passerait mal et qu’au fond mieux valait ne pas trop déplaire… Mon éthique a tranché, j’ai dit la vérité. En l’enrobant. Des heures passées à réécrire deux fois, dix fois certains paragraphes. Ils ne s’en douteront jamais… Et au final un rapport qui sera peut-être classé au fond d’un tiroir car personne ne voulait vraiment savoir ce qui n’allait pas. La remise en question d’un tel échec dépasse de loin le cadre technique et peu de gens savent admettre leurs erreurs, surtout quand toute la chaîne hiérarchique de la base au sommet doit participer à ce questionnement.

Le salaire de la peur

La peur dont je parlais plus haut, les chocottes que cet audit m’a données, c’est d’être confronté au fleuron de la technologie informatique, à des choses en lesquelles je crois car elles marquent un réel progrès et que (bien utilisées) elles permettent justement un code limpide. Cet audit a brisé ce fantasme en me rappelant qu’un marteau pouvait servir au meilleur, comme au crime. Xaml, WPF et Silverlight n’échapperaient pas à la règle et il faudra être vigilant. Surtout que l’avalanche de technologies et de patterns (WCF Ria Services, Entity Framework, MVVM, Prism, …) rendent presque impossible la maîtrise de tous ces sujets. Je suis payé pour ça. Au minimum 50% de mon temps est passé en autoformation pour apporter un conseil éclairé sur les technos à venir. Ce sont les 50% vendus qui financent cette formation et cette recherche permanente. Comment un développeur qui fait ces 48h (35 de base + le dépassement obligatoire non payé car il est aux “cadres”) peut-il se former à tout cela en travaillant sur d’autres projets la journée ? C’est impossible. Et cela créé une situation très dangereuse. Technologie sophistiquée + manque de formation = code spaghetti !

David Vincent au pays des tags

Il était donc urgent de vous en parler, d’attirer votre attention sur certaines dérives, car dans une moindre mesure, je sais, je l’ai vu chez d’autres, ces chausses-trappes savent s’ouvrir sous les pieds des développeurs les mieux formés et les mieux intentionnés !  Je les ai vues, et je dois convaincre un monde incrédule…

Ma mission, que j’ai acceptée (forcément avant de voir, on a pas vu…), était de faire un audit de pré-release. C’est à dire de venir faire le point sur l’état de la situation, les derniers fameux “boulons à serrer” en quelque sorte, et surtout de venir signer un satisfecit au DI, très fier de son bébé (Rosemary’s baby plutôt que Dora de TF1 au final), afin qu’il puisse faire monter sa prime de fin d’année je suppose.

Hélas, déception et vilenie. Ce n’est pas avec le prix d’un audit (fort raisonnable, devis gratuit sur simple demande) qu’on achète ma conscience. Il m’a donc fallu trouver les mots et les formules diplomatiques pour rendre un audit policé et mesuré sur cet édifice in-maintenable, voué à l’échec et bon à mettre à la broyeuse. Ce n’est pas la partie la plus difficile, car tout rapport d’audit se doit d’être policé et tourné de façon neutre et technique. Même si parfois on a envie de crier “bande de nuls !”, non, ce n’est pas bien, on ne le fait pas…

Que les futurs clients ne s’affolent pas trop, ce que je raconte aujourd’hui est malgré tout à classer dans les exceptions, le fameux bout de la cloche de la courbe Normale. Même si je ne suis que rarement content du code que je vois, on est, heureusement en moyenne, assez loin de l’horreur qui justifie le présent billet !

O Tempora, O Mores !

Parfois je rêve, j’imagine un monde où l’un de ces clients m’appellerait en me proposant un pont d’or pour lui avoir ouvert les yeux et l’avoir empêché de plonger plus encore tant qu’il était encore temps… Mais ce n’est qu’un rêve, bien entendu. Je suppose que je n’entendrais plus parler de ce client comme de quelques autres dont, les années passant et les vœux annuels restant sans réponse m’ont forcé à une résignation réaliste. Comme le chantait l’artiste, “il a dit la vérité, il doit être exécuté” !

Hélas, les temps ne sont pas à la prise de conscience ni à la bravoure. Nous vivons une époque de lâches où chacun ouvre son parapluie pour que les gouttes en atteignent d’autres. Forcément celui qui est au plus bas de la pyramide se prend tout sur la figure. Souvent c’est le développeur qui trinque pour une chaîne hiérarchique défaillante qui n’a pas fait son métier et qui n’assume pas ses responsabilités. Un développeur n’est jamais coupable seul d’un mauvais code, c’est toute la chaine de commandement qui faillit.

Il y a encore un rêve que je fais et que j’aimerais voir se réaliser avant ma (encore lointaine) retraite : que les entreprises fassent intervenir un conseil, un auditeur avant que l’irréparable ne soit commis, pas après pour constater les dégâts ! … ça serait tellement mieux et plus gratifiant pour moi et mes confrères ! Conseiller, orienter, former, en un mot aider, c’est tout de même plus chouette que de passer pour le père fouettard et l’inspecteur des travaux finis !

Faut-il brûler Xaml ?

Certes non.

Mais ce que je veux qu’il vous reste de ce récit hallucinant autant que réel, c’est que Xaml est utilisé par certains comme un langage dans le langage. Un moyen de vider le code-behind de son C# pour remplir des balises ésotériques. Un langage dans le langage avec son Binding qui est, telles le sont les poupée Russes, encore un langage dans le langage.

A vouloir tout faire par binding, à tout dynamiser, à appliquer tout Prism et le chargement dynamique des modules, la découvertes des plugins avec MEF, le tout mis en forme façon MVVM, sans compter les ruses purement graphiques du templating sous Blend, des DataTemplate à double face pivotante en 2,5 D, à vouloir appliquer tout cela dans un seul soft sans avoir pris les 2 ou 3 ans nécessaires à acquérir la parfaite maîtrise méthodologique pour faire marcher tout cela ensemble, on peut facilement arriver à une situation désastreuse. Encore plus facilement que sous C, encore plus que sous Delphi, pire que sous Windows Forms. Tout cela n’était que de l’amateurisme face aux dérapages délirants que WPF et Silverlight peuvent permettre… Comment faire des tests unitaires parlants sur des balises de Binding imbriquées à un tel point que même Intellisense de VS s’y perd ?

L’extrémisme essayiste est un fléau dans notre métier. Il m’a toujours effrayé, mais il a toujours eu sa sanction “naturelle” : une technologie qui finit par être si mal utilisée créée une telle publicité négative qu’elle tend à disparaître. Je ne voudrais pas que cela arrive à WPF et à Silverlight qui apportent tant d’autres progrès !

Il faut sauver le soldat Xaml !

Pitié pour Xaml, un bon geste pour WPF et Silverlight : ne laissez pas des sagouins faire du mal à toutes ces belles technologies que Microsoft nous offre depuis quelques années. Laissez leur une chance, celle que les développeurs deviennent plus matures et mieux formés aux méthodologies qui vont avec. Ne commettez pas le crime d’être complice de ce genre de chose. Intégrer les technologies les unes après les autres, ne sautez pas d’une bêta à l’autre sans avoir l’assurance que vous maîtrisez la précédente !

Mieux vaut une application Silverlight fonctionnant avec un Service Web classique et dont les champs sont connectés par code, qui marche et qui est maintenable, qu’une élucubration technologique improbable et non maîtrisée qui s’écroulera comme un château de carte au premier éternuement du premier bug venu…

Un peu de sérieux…

Il ne s’agit pas d’une invitation à la méfiance, encore moins au passéisme. Non, c’est une invitation au professionnalisme : n’utilisez que ce que vous maîtrisez.

Si vous maîtriser le top de la techno, allez-ci, franchement. Si vous avez un petit doute : faites joujou dans une machine virtuelle et produisez du code que vous comprendrez à 100% et surtout que d’autres pourront comprendre pour le maintenir, le faire vivre dans les années à venir.

Le code Kleenex coûte cher, il dévalorise notre métier et les outils que nous utilisons.

Le code spaghetti peut assassiner une technologie.

Codez bien. Formez-vous bien.

Et faites auditer vos projets avant, pas après.

Et Stay Tuned, j’aurais des trucs plus légers à vous dire la prochaine fois !

Simple MVVM

MVVM, Model-View-ViewModel, est une pattern de développement que je vous ai présentée plusieurs fois (billet et un article récent de 70 pages) en raison de son importance dans le développement d’applications Silverlight principalement.

A ces occasions j’ai présenté plusieurs frameworks permettant de faciliter le développement en suivant la pattern MVVM. Je viens d’en découvrir un autre, encore plus simple que MVVM Light, c’est “Simple MVVM” un projet CodePlex.

La gestion des commandes n’est pas prise en compte mais comme je le montre dans le long article évoqué en introduction en utilisant Silverlight 4 (en bêta pour l’instant mais bientôt releasé) on peut facilement gérer l’interface ICommand dans une vaste majorité de cas sans utiliser de librairie annexe.

Simple MVVM est vraiment simple. C’est un peu “MVVM pour les nuls”. Mais justement, c’est en partant d’exemples simples, de librairies hyper light qu’on peut mieux cerner une technologie et choisir ensuite des frameworks plus lourds et plus complets. Je vous conseille donc d’y jeter un œil.

Dans tous les cas je suis pour les librairies les plus light possible. Les gros “zinzins”, même très bien faits, pose toujours un problème de maintenabilité (si vous, personnellement, vous avez investi du temps pour apprendre telle ou telle grosse librairie, c’est bien, mais que ce passera-t-il s’il faut qu’un autre informaticien maintienne votre code au pied levé ? Combien coûtera sa propre formation sur la dite librairie ? Alors que souvent ces dernières ont pour objectif de simplifier le travail et donc de couter moins cher “à la longue”. C’est faux en réalité, et c’est donc un contre-sens que de les utiliser, aussi puissantes ou savantes soient-elles, sauf cas exceptionnels…).

Ce qui se conçoit bien se programme clairement – Et le code pour le faire vient aisément… (paraphrase libre du célèbre proverbe de Nicolas Boileau).

Stay Tuned !

Lorem Ipsum generator : un autre exemple Silverlight

Et un de plus dans la galerie ! :-)

https://www.e-naxos.com/slsamples/lorem/loremgen.html

“Lorem ipsum…” vous connaissez tous ce texte en pseudo-latin que tous les infographistes utilisent pour tester la mise en page de leurs documents, de la carte de visite aux sites web, de la plaquette commerciale sur papier glacé jusqu’à, bien entendu, des applications Silverlight.

Il existe beaucoup d’extraits de ce fameux texte. Je vous propose “ma” version, en Silverlight.

image

Fonctionnalités

Génération

LIG (Lorem Ipsum Generator) est capable de générer des Paragraphes, des Phrases et des Mots de façon aléatoire.

Une phrase est constituée de mots, il est possible de choisir une fourchette, par exemple entre 3 et 8 mots. Le tirage est aléatoire pour chaque phrase.

Un paragraphe est constitué de phrases, il est ici aussi possible de choisir une fourchette, par entre un paragraphe fait de 5 à 8 phrases.

Qu’il s’agisse de paragraphes, de phrases ou de mots, la “chose” générée le sera en plusieurs exemplaires (tous différents) et ce nombre est fixé par une fourchette.

Les radio boutons en bas à gauche permettent de choisir le mode de fonctionnement.

Une case à cocher sous le texte permet de forcer la première phrase (en mode Paragraphes) à sa valeur la plus connue, le célèbre “Lorem ipsum …”.

Visualisation et statistiques

Le texte généré est immédiatement visualisé à l’écran. De plus, LIG fournit des statistiques simples mais importantes sur ce dernier : nombre de symboles et nombre de mots.

Récupération du texte

Le plus simple est de cliquer sur le bouton “Copy”. Une confirmation est demandée par mesure de sécurité. Il suffit ensuite de coller ce texte où vous en avez besoin…

Bouton Info

Il affiche une page d’information sur l’application

Installation Out-Of-Browser (OOB)

LIG supporte le mode de fonctionne OOB, en cliquant sur le bouton “Install” (en haut à droite, il s’illumine quand on passe la souris dessus) l’application LIG sera téléchargée depuis le Web et installée sur votre machine. Selon votre choix une icône peut être placée sur le bureau ainsi qu’une entrée dans le menu Démarrer (ou l’équivalent sous Mac).

Lors de la première installation l’application sera ouverte immédiatement, le site web sera toujours à l’écran aussi. Fermez ce dernier. Vous disposez maintenant d’une application locale totalement indépendante… Bien entendu LIG n’a alors plus besoin de connexion Internet pour fonctionner et durant son utilisation ni il n’envoie ni il ne reçoit des données depuis Internet. C’est totalement safe et sans espionnage.

Conclusion

Du coup, me privant d’une remontée d’info automatique pour respecter la liberté de chacun de ne pas être pisté par la moindre application, je remercie par avance les utilisateurs de LIG de bien vouloir me laisser un mot pour me dire s’ils apprécient ou non l’application.

Le code source sera mis à disposition d’ici quelques temps, alors… Stay Tuned !