Dévelopement

Xamarin : angoisses du débutant et solutions !

Dot.Blog - mer, 13/06/2018 - 00:32

Lorsqu’on débute avec Xamarin on se retrouve souvent face à des problèmes qui semblent trop nombreux ou insolubles. Le manque de connaissance fait paraître tout cela bien plus grave que ce ne l’est. Heureusement il y a Dot.Blog pour vous sortir de l’impasse !

Problem Solving

L’informatique est un métier qui peut s’exercer de cent façons différentes, mais toutes ont une chose en commun : l’obsession de régler des problèmes. Des problèmes de toutes sortes. L’objectif reste constant,leur trouver une solution. Celui qui n’aime pas les problèmes et préfère la routine ne doit jamais faire d’informatique !

Bien plus que le codage qui n’est qu’apprentissage d’une langue étrangère simplifiée, c’est la résolution des problèmes qui fait le métier et ses difficultés mais aussi son charme… L’informaticien doit être un joueur d’échec passionné qui sait analyser, savourer la victoire mais profiter aussi de ses défaites pour apprendre… sinon sa vie sera un enfer !

Aborder de nouveaux langages, de nouveaux OS, de nouveaux environnement obligent à sortir de sa zone de confort et par force à se confronter à des problèmes nouveaux devant lesquels le plus endurci se retrouvera comme un débutant démuni et stupide… Passer du sommet de la montagne au fond de la vallée en quelques instants est vertigineux. Les clients du parapente ou du saut à l’élastique restent rares malgré tout, l’humain est casanier et prudent, comme les chiens… L’informaticien est plutôt un aventurier même s’il travaille dans un bureau !

Xamarin n’échappe pas à la règle qui touche tous les environnements, au départ on se frotte à des difficultés qui peuvent sembler insurmontables alors que celui qui aura franchi ces premières étapes d’apprentissage donnera l’impression que tout y est simple et facile. C’est le cas des démos ou des conférences et je suis bien placé pour en parler ! Pour une heure de présentation il y a parfois des journées entières de travail, de test, d’échecs, de préparation du discours, le répéter plusieurs fois, etc. Les choses paraissent alors simples et couler de source… Mais quand on rentre chez soi et qu’on lance Visual Studio les choses se compliquent, s’embrouillent. Ce qu’on pensait avoir compris ne fonctionne pas, il manque des choses, les messages d’erreurs s’accumulent, certains abandonnent là et retournent à leurs habitudes. C’est triste. Si vous n’êtes pas de ceux là, alors voici quelques pistes qui vous permettront de dépasser les premiers instants parfois rugueux !

Trop d’erreurs au premier build

Le premier mur qui se dresse devant le débutant est bien celui-là ! Vous lancez Visual Studio, créez un projet Xamarin.Forms qui vous offre un template de base mais malgré cela vous ne pouvez pas faire un build et encore moins exécuter l’App immédiatement malgré sa simplicité déconcertante ! Ne paniquez pas, en général cela est du aux paquets Nuget qui ne sont pas restaurés par VS ou qui nécessitent d’être mis à jour (les templates ont parfois un peu de retard sur les mises à jour désormais très fréquentes de VS).

Solution

Restaurer tous les paquets Nuget, mettez à jour ceux qui le nécessitent.
Pour vous aidez, vous pouvez lire cet article de Microsoft : Restauration des packages

Impossible de compiler ou d’exécuter l’App

Hélas les choses ne sont pas toujours aussi simples qu’une restauration de paquets… Et malgré vos efforts pour tous les vérifier ça ne veut pas aller plus loin.

Ici il s’agit le plus souvent de paquets spécifiques à une plateforme qui manquent à l’appel. Après avoir restauré les paquets vous pouvez fort bien avoir des messages d’erreur vous indiquant qu’il en manque ! Cela est déconcertant je l’accorde. C’est souvent le cas avec les paquet pour Android comme Xamarin.Android.Support.Design ou d’autres paquets de supports. Ou bien c’est VS qui va rapporter des références manquantes à des dépendances ou des paquets qui sont pourtant déjà présents dans votre solution ! Agaçant et frustrant… Et si vous arrivez à lancer l’App, elle se fermera immédiatement. Si près du but…

Solution

je vais vous décevoir mais il n’existe pas de solution simple à ces problèmes, pas de recette miracle… Chaque cas est un cas particulier et seule la connaissance que vous accumulerez ainsi que l’expérience vous permettra de vous en sortir rapidement… Mais je ne vais pas vous planter là comme ça, ce n’est pas mon genre ! Je vous propose plutôt de lire cet article qui a l’avantage d’être écrit par une jeune femme, jolie et qui nous vient d’un coin qui n’est pourtant pas réputé pour sa haute technologie, la République Dominicaine…Common compilation problems in Xamarin and how to solve them (j’ai corrigé la faute du titre original qui se termine par “it” au lieu de them, on lui pardonnera !). J’entends quelques grincheux anglophobes là bas au fond… Problem-Solving… Utilisez l’un des traducteurs en ligne pour lire en français

C’est du XAML “çà” ? !

Si vous êtes un aficionado de XAML, un dur, un tatoué, du genre à faire du WPF dans le bloc-note Windows avec un main attachée dans le dos, forcément le XAML de Xamarin.Forms va vous sembler un peu curieux et parfois déroutant. XAML est un langage à balise, issu de XML et de leur grand-papa SGML. Xamarin.Forms ne fonctionnait pas en XAML au départ, uniquement en C#. Puis est venu XAML, très limité au départ et de plus en plus sophistiqué malgré tout. Mais il faut l’avouer il manque quelque chose à ce XAML là : le graphisme. Pas de templating de contrôles, pas de dessin vectoriel non plus. Au départ Xamarin.Forms ne fait que proposer des “wrappers” fantômes et non des contrôles réels… Un “Button” Xamarin.Forms n’a aucune existence réelle, c’est un décorateur, une façade, une pure vue de l’esprit. A l’exécution Xamarin.Forms instancie directement le contrôle équivalent natif de la plateforme. Les contrôles Xamarin.Forms sont ainsi des synthèses de ce qui existe, le plus petit dénominateur commun entre iOS, UWP, Mac OSX et même Tizen. C’est déjà une prouesse ! Mais on comprend qu’on ne peut pas bricoler le look du Button (ou de toute autre contrôle) comme on le fait en XAML classique, le “Button” n’existe pas ! C’est une abstraction pure. Et même si les XAML de Xamarin.Forms avait des primitives graphiques cela n’aiderait en rien pour ce problème là.

Solution

Là encore pas de miracle, il n’y a pas une option cachée à activer pour se retrouver avec des courbes de Bézier !

En revanche Xamarin.Forms est fourni avec une panoplie complète de primitives pour faire des animations… De même les Xamarin.Forms offrent tout ce qu’il faut pour travailler dans chaque projet natif, toujours en C#, pour y faire tout ce qu’on veut et utiliser toutes les possibilités de la plateforme sans perdre l’avantage d’un code centralisé, c’est le principe même des contrôles cross-plateforme supportés par les XF… Mieux, et comme le dernier article le montre, en utilisant une librairie comme SkiaSharp on retrouve des primitives graphiques 2D permettant de créer des contrôles personnalisés au graphisme léché et cross-plateformes !

Le XAML de Xamarin.Forms n’est peut être pas le plus puissant de la famille, mais il offre tout le nécessaire et sait ouvrir la porte pour ce qu’il ne sait pas faire.

Alors faut-il utiliser le XAML des Xamarin.Forms ou pas ? La réponse est un OUI francs et massif ! XAML est étudié pour créer des UI, il n’y a rien de mieux ni de plus pratique pour cela, ne cherchez pas. Et une fois que serez familiarisé avec ce XAML là, vous ne voudrez plus jamais faire vos UI en code C#…

Les animations : téléchargez un chapitre gratuit de mon livre sur les Xamarin.Forms !

Sur SkiaSharp : Création d’un contrôle personnalisé avec SkiaSharp, SkiaSharp et Xamarin.Forms la 2D cross-plateforme

Les erreurs fantomatiques !

Ca fait peur hein ! Bon, ce ne sont pas non plus des poltergeists qui viendront tourmentez vos nuits, n’exagérons rien. Ces erreurs là arrivent le plus souvent quand il y a une erreur en XAML ou côté plateforme native. Malgré tous les efforts de Xamarin et de Microsoft, remonter des infos complètes sur certaines erreurs de ce type est très complexe. Le plus souvent il faut se contenter d’un message du genre “ça a planté” sous-entendu démerdez-vous ! Même si on sait pourquoi, c’est très frustrant. J’ai parfois passé des heures à tenter de comprendre des bogues de ce genre.

Solution

… Jusqu’au jour où j’ai compris que puisque XAML est natif sous UWP et malgré le peu d’intérêt que cette plateforme suscite c’est par elle que viendrait le salut ! Depuis j’ajoute systématiquement la cible UWP à mes projets même lorsqu’ils ne ciblent qu’Android ou iOS. Pourquoi ? Faut suivre… parce que XAML est natif sous UWP. Il suffit donc de lancer son appli en mode UWP et le curseur viendra s’arrêter là où le code est mauvais le plus souvent et le tout garni d’un message d’erreur complet… La grosse astuce est donc de toujours avec une cible UWP dans ses projets et dès que vous tomberez sur l’une de ces erreurs angoissantes, au lieu de passer des heures à chercher, basculer sous UWP et relancez… Vous gagnerez des heures précieuses, croyez-moi !

Pas de designer visuel ?

la question revient forcément souvent. Non, pour l’heure il n’y a pas de concepteur visuel pour Xamarin.Forms. Si vous avez lu tout ce qui précède vous avez compris pourquoi : les contrôles de Xamarin.Forms n’existent pas ! Ils sont remplacés au runtime par des équivalents natifs qui ne sont pas tous exactement identiques. Comment offrir un concepteur visuel dans un tel cas de figure ?

Solution

Il y a des tentatives de proposer un système “'d’écho”, c’est à dire une fenêtre qui affirme en temps réel les modifications faites en XAML. Mais ce n’est pas encore très au point et cela ne peut pas remplacer un concepteur visuel soyons franc. Mais tout de même cela permet de se faire une idée, de ne pas être totalement en aveugle.

A connaître donc : Le Générateur d’aperçu XAML pour Xamarin.Forms

Il existe aussi un produit commercial, Gorilla Player, je n’ai pas accroché mais certains en sont contents.

Vous noterez qu’il existe en revanche un concepteur visuel pour Android et un autre pour iOS lorsqu’on utilise Xamarin en mode natif (et non pas en mode Forms).

Comprendre la philosophie des Xamarin.Forms et ses astuces

Au final ce qui manque le plus lorsqu’on débute c’est bien d’avoir une vue d’ensemble tout autant que de bénéficier d’un coup de pouce pour résoudre certains problèmes comme le support de MVVM ou l’utilisation d’une base de données SQL par exemple…

Solution

Si vous ne l’avez pas déjà, procurez-vous mon Livre sur les Xamarin.Forms ! Il vous donnera à la fois cette vue d’ensemble indispensable ainsi que ces petits détails qui rendent les choses plus faciles… Je donne aussi des formations ou propose des formules de monitoring ou de starter-kit mi-développement mi-formation pour bien démarrer vos projets… Et pour les grandes occasions, j’assure aussi un service de forfait qui vous évitera de vous prendre la tête ! Vous ne pourrez pas dire que vous n’étiez pas au courant

Conclusion

Xamarin.Forms représente une avancée fantastique. Depuis son rachat il y a déjà un moment par Microsoft c’est en plus une plateforme “officielle” soutenue et garantie. Tout n’est pas rose, mais le problème posé est véritablement diablement complexe à résoudre. Les Xamarin.Forms s’en sortent très bien et offrent l’avantage de langages à la pointe, C# et XAML, le plus puissant des IDE, Visual Studio, la plateforme objet la plus riche, .NET, et le support de tous les OS, jusqu’au Mac sur lequel il existe une version de Visual Studio pour ceux qui ne veulent pas toucher à un PC !

C’est plutôt du lourd. Alors on comprend que les débuts ne soient pas toujours faciles faciles… Mais là encore, Solution : lisez Dot.Blog et…

Stay Tuned !

Catégories: Dévelopement

Les papiers des lecteurs : Créer un contrôle graphique Xamarin avec SkiaSharp

Dot.Blog - dim, 10/06/2018 - 17:19

Xamarin vous semble figé avec son XAML sans primitives graphiques ? C’est oublié SkiaSharp… Alors découvrez comment développer un contrôle graphique personnalisé avec cette librairie !

Les papiers des lecteurs

Cette rubrique est la vôtre, Dot.Blog publie de temps en temps des articles écrits par ses lecteurs, saisissez cette possibilité !

Ce mois-ci c’est Patrick Breil qui s’y colle. Patrick développe des applications mobiles pour l’entreprise où il exerce ses talents par exemple des suivis de dossiers pour des techniciens qui interviennent sur site et qui peuvent ainsi prendre connaissance des tâches à effectuer et saisir le détail de leurs interventions. L’adoption de tels logiciels réclame non seulement un fonctionnel à la hauteur mais aussi un look & feel comparable aux autres Apps que l’utilisateur à l’habitude de voir sur son smartphone. Cela place la barre assez haute !

Les papiers des lecteurs c’est un retour d’expérience qu’on partage, c’est noble et c’est utile ! Vous aussi vous pouvez me proposer vos articles, pensez-y…

SkiaSharp

Pour ceux qui ne connaissent pas : c’est une API 2D complètement portable iOS, Android, UWP et même Mac OSX ou WPF. SkiaSharp ce sont un peu les primitives graphiques qui manquent au XAML de Xamarin.

J’ai déjà présenté cette librairie l’année dernière et je renvoie le lecteur intéressé à ce papier : SkiaSharp et Xamarin.Forms le dessin 2D cross-plateforme.

Créer un contrôle personnalisé avec SkiaSharp

Il est temps de laisser la parole à Patrick pour son papier…

Objectif

Créer un contrôle permettant d’indiquer une valeur de % sur un arc, un peu comme une barre de progression mais qui aurait été courbée (dessin ci-dessous).

Création du projet

Un classique mais il faut bien commencer par là : Nouveau projet > Prism Xamarin.Forms

(Patrick a fait des captures parfois un peu justes niveau résolution mais on comprend l’action !)



Ajout du Package Nuget SkiaSharp


Création du contrôle utilisateur :

Créer un nouveau dossier ‘UserControls’, puis un nouveau ContentView ‘Gauge’

Le xaml du contrôle ne va pas contenir grand-chose si ce n’est un SKCanvasView qui contiendra le dessin de notre contrôle.

Une fois saisi SKCanvasView il est proposé d’ajouté l’espace de nom SkiaSharp.Views.Forms.

Il suffit ensuite d’ajouter un gestionnaire d’événements à PaintSurface.

Nous passons ensuite au code behind, et plus précisément sur la méthode SKCanvasView_PaintSurface qui sera invoqué par l’événement ‘InvalidateSurface’ du CanvasView.

private void SKCanvasView_PaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
{
  SKSurface surface = e.Surface;
  SKCanvas canvas = surface.Canvas;

canvas.Clear();

int width = e.Info.Width;
  int height = e.Info.Height;

canvas.Translate(width / 2, height / 2);

SKRect Rect = new SKRect(-100, -100, 100, 100);
}

Le code ajouté sur la méthode positionne le point 0,0 au milieu de la surface définit en fonction du contenant de notre contrôle.

Le point 0,0 se situe en haut et à gauche.

Notre contrôle sera centré est définit dans un carré de 200 x 200.


Nous définissons un rectangle (qui pour le coup est carré !)

SKRect Rect = new SKRect(-100, -100, 100, 100);


Il faut maintenant adapter la taille de notre contrôle en fonction de la taille du contenant.

L’échelle retenue sera la plus petite calculée pour la largeur et la hauteur.

float scale = Math.Min((width / Rect.Width), (height / Rect.Height));

canvas.Scale(scale);

Passons au dessin du fond de notre gauge. Pour cela nous devons définir un chemin qui sera parcouru par un pinceau.

Le chemin représente le milieu de l’épaisseur du pinceau, soit 75 + (25/2) = 87.5.

Le code suivant, instancie un chemin path.

using (SKPath path = new SKPath())

{
    path.ArcTo(new SKRect(-87.5f,-87.5f,87.5f,87.5f), 135, 270, false);

    canvas.DrawPath(path, new SKPaint
            {
              Color = Color.Gray.ToSKColor(),
              Style = SKPaintStyle.Stroke,
              StrokeWidth = 25
             });
}

Un arc est décrit par la méthode ArcTo.

  • Le rectangle décrit l’espace dans lequel sera tracé notre arc. Il s’agit bien d’un rectangle ce qui signifie qu’il est possible de décrire une section d’une ellipse si le rectangle n’est pas un carré !
  • La première valeur correspond à l’angle de départ dans le sens horaire (ou inverse trigonométrique), soit dans notre cas 45° + 90° = 135 °
  • La seconde l’angle décrit par notre arc, soit 360° -90° =270°
  • Le booléen indique si la suite du chemin part ou non de la fin de notre arc.

Nous traçons ensuite notre arc avec un pinceau d’une épaisseur de 25 correspondant à la différence de nos deux rayons.

Pour tester le résultat (provisoire), il suffit d’ajouter notre contrôle au Xaml

<ContentPage xmlns=http://xamarin.com/schemas/2014/forms
             xmlns:x=http://schemas.microsoft.com/winfx/2009/xaml
             xmlns:usercontrols="clr-namespace:ControleSKiaSharp.UserControls"
             x:Class="ControleSKiaSharp.Views.MainPage"
             Title="{Binding Title}">

<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
   <usercontrols:Gauge HorizontalOptions="FillAndExpand"VerticalOptions="FillAndExpand"/>
</StackLayout>
</ContentPage>



Notre contrôle est centré et occupe l’espace disponible et s’adapte au changement de taille de la fenêtre.

Ajoutons maintenant le second arc correspondant à la valeur à représenter.

La variable valeur contient la valeur à représenter.

L’angle décrit est calculé en fonction de l’angle complet de notre compteur soit 270°.

La fonction pourrait être adapter pour des plages de valeurs différentes, ainsi qu’un angle de représentation plus petit ou plus grand.

float valeur = 55;
float AngleValeur = (270 * valeur) / 100;
using (SKPath path = new SKPath())
{
   path.ArcTo(new SKRect(-87.5f, -87.5f, 87.5f, 87.5f), 135, AngleValeur, false);
   canvas.DrawPath(path, new SKPaint
                             {
                               Color = Color.CornflowerBlue.ToSKColor(),
                               Style = SKPaintStyle.Stroke,
                               StrokeWidth = 25
                              });
}


Ajoutons maintenant l’affichage de valeur au centre de notre contrôle.

canvas.DrawText(valeur.ToString("0.0") + "%", 0, 10, new SKPaint
   {
     Color = Color.CornflowerBlue.ToSKColor(),
     Style = SKPaintStyle.Fill,
     StrokeWidth = 1,
     TextAlign = SKTextAlign.Center,
     TextSize = 40
    });



Notre contrôle à l’apparence souhaité mais pour le moment son utilité est plutôt limitée. Il faut que nous rendions Bindable la propriété valeur pour qu’elle soit dynamique.

// NDE : Patrick a eu un problème pour définir la propriété en float et il a réussi à le faire en utilisant le type string.
// Ce n’est évidemment pas optimal… j’attends qu’il m’envoie le code source du projet pour regarder
// et je vous tiendrai au courant !


float valeur = 0.0f;

public static readonly BindableProperty ValeurProperty =
           BindableProperty.Create(propertyName: "Valeur",
                                   returnType: typeof(string),
                                   declaringType: typeof(Gauge),
                                   defaultValue: "0.0",
                                   defaultBindingMode: BindingMode.Default,
                                   propertyChanged: UpdateValeur);




public string Valeur
         {
             get { return (string)GetValue(ValeurProperty); }
             set { SetValue(ValeurProperty, value); }
         }

private static void UpdateValeur(BindableObject bindable, object oldValue, object newValue)
         {
             var ctrl = (Gauge)bindable;
             var v = (string)newValue;
             ctrl.valeur = float.Parse(v.Replace(".",","));
             ctrl.canvas.InvalidateSurface();
         }


On modifie ensuite le Xaml de la page pour lier la propriété du contrôle avec un Slider

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:usercontrols="clr-namespace:ControleSKiaSharp.UserControls"
              x:Class="ControleSKiaSharp.Views.MainPage"
              Title="{Binding Title}">

     <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
         <Grid.RowDefinitions>
             <RowDefinition Height="25" />
             <RowDefinition Height="1*" />
         </Grid.RowDefinitions> 
         <Slider x:Name="Slider" Minimum="0" Maximum="100"/>
         <usercontrols:Gauge Grid.Row="1" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
                             Valeur="{Binding Source={x:Reference Slider}, Path=Value}"/>
     </Grid>

</ContentPage>





Notre contrôle est maintenant fonctionnel et peut être utilisé en MVVM.

SkiaSharp est une bonne technologie pour créer ses propres contrôles et peux nous dispenser de télécharger des Packages complet pour n’utiliser que quelques composants.

Ce contrôle est relativement simple, mais il est possible d’en créer de plus complexe en ajoutant des propriétés dynamique comme les couleurs, les unités, des listes de valeurs, etc…

Exemple : Une application mobile de contrôle pour Terrarium.


Conclusion

Remercions à nouveau Patrick pour ce bel effort ! Cela vous prouve qu’en prenant un peu le temps on peut écrire des papiers intéressants et les partager avec la communauté… je vous incite à suivre cet exemple !

Cela prouve aussi que Xamarin.Forms a atteint une vraie maturité. Son C# est à la hauteur (c’est le même que pour WPF ou UWP), son XAML s’est complexifié avec le temps et supporte l’essentiel de ce langage, et pour la partie graphique, problématique dans un environnement cross-plateforme, nous disposons de librairies simples d’utilisation qui comblent le fossé avec le XAML classique.

Il est donc possible à la fois de gagner du temps (un code, de multiples cibles natives) sans rien sacrifier au look & feel si essentiel sur les smartphones.

A vos claviers ! (pour coder mais aussi pour écrire un papier des lecteurs !)

Stay Tuned !

Catégories: Dévelopement

Xamarin Forms 3.0 une release qui a du Style !

Dot.Blog - dim, 13/05/2018 - 14:34

Xamarin.Forms est déjà un ensemble mature et complet d’outils, compilateurs, templates et contrôles cross-plateformes basés sur .NET et XAML. Développer pour Android, iOS, UWP avec un seul code unifié est déjà en soi une prouesse. Pour un résultat natif, pas un bricolage hybride ou interprété. La version 3.0 pousse encore plus loin les choses…

Les évolutions

Chaque évolution de Xamarin.Forms 3.0 mérite un article, il sera donc difficile ici de faire beaucoup mieux que de vous donner un aperçu de sujets sur lesquels je reviendrai dans les semaines à venir.

La première chose intéressante à noter c’est l’intégration du travail de la communauté. Xamarin.Forms s’enrichit par le travail de son équipe de développement mais aussi par celui de la riche communauté qui s’est formée autour de ce produit. Et c’est une excellente nouvelle !

Autre point : la convergence avec XAML de WPF s’améliore encore… Avec l’une ces possibilités les plus marquantes pour qui manipule WPF…

Le Visual State Manager

Emblématique fonctionnalité de WPF ? Non pas tout à fait… C’est feu Silverlight qui le premier introduisit cette extraordinaire possibilité doublée d’un pilotage admirable dans Blend. Ce n’est qu’après, constatant le côté incontournable de cette évolution, qu’on vit apparaître le VSM sous WPF.

Aujourd’hui il arrive dans Xamarin.Forms. Silverlight n’est pas mort, son âme plane toujours sur XAML !

Sous WPF et Sliverlight qui sont (était) des systèmes graphiques totalement vectoriel le VSM a toujours été une merveille visuellement. Vous définissez des “états” de votre UI, vous leur donnez des noms, vous fixez éventuellement des transitions, des effets, et via une commande en C# vous faites passer votre UI d’un état à l’autre. Brusquement ou plutôt via des effets de transition du plus bel effet. Cela s’accorde de plus facilement avec MVVM puisque le ViewModel n’a besoin que d’envoyer des noms d’état à l’UI pour que celle-ci s’adapte sans avoir à connaître les contrôles eux-mêmes. Le nom de l’état est une string, le découplage est donc total. En envoyant le nom de l’état dans un message MVVM vers la View les puristes retrouve un découplage total. Car ce nom envoyé ne présume en rien de ce qui en est fait. Un ViewModel peut ainsi prévoir de passer de l’état “Idle” à l’état “Chargement en cours” sans même savoir ce que l’UI en fera. Charge au designer d’utiliser ou non ces changements d’états bien identifiés pour les matérialiser dans l’UI.

Sous Xamarin.Forms le procédé n’est pas graphiquement du même niveau puisque ce XAML là n’est pas vectoriel, pas même graphique. On y manipule des abstractions de contrôles qui seront remplacées par des contrôles natifs. Impossible de déformer lentement l’enveloppe d’un bouton en tirant sur les points de contrôle de la courbe de Bézier de son contour par exemple…

C’est pourquoi WPF est et restera la référence absolue pour XAML, sa plus belle et plus puissante implémentation. Le petit frère Silverlight est tombé au combat, mais ce qu’il a lui-même apporté à WPF vit toujours et se retrouve désormais dans Xamarin.Forms.

Je ne vais pas vous gaver d’images piquées ici ou là, d’extraits de codes sortis de leur contexte, je réserve une accueil un peu plus sérieux au VSM sous Xamarin.Forms, et j’en parlerai plus en détail dans un article entièrement consacré à ce procédé absolument génial et indispensable même.

Le FlexLayout

Encore un contrôle permet de disposer ses enfants selon des modes particuliers. Dans la lignée de tous les xxxLayout. Et à chaque fois nous gagnons en souplesse car chaque layout propose une stratégie de placement de ses enfants que les autres ne savent pas faire.

FlexLayout est inspiré de la FlexBox du Web très en vogue pour les mises en page aujourd’hui. Un outil de plus pour se sortir de tous les cas c’est toujours bon à prendre.

CSS

CSS ? Vade Retro Satana ! Un exorciste vite !

Oui… la Bête immonde fait sont apparition dans Xamarin.Forms… Les âmes sensibles sont prévenues. Cauchemars à prévoir.

Alors disons le tout de suite pour rassurer tout le monde, c’est une option. Une simple option. Totalement démagogique je vous l’accorde. Comme à chaque fois qu’on met des morceaux de web dans de vrais langages. C’est justifié systématiquement par le fait que cela permettra aux développeurs Web de venir plus facilement à la technologie en question. MS a fait ça avec UWP, on voit le résultat, Xamarin, aujourd’hui possession de MS a certainement été poussé à le faire pour les mêmes raisons.

Si vous vomissez CSS comme moi, oubliez cette “possibilité” immédiatement.

car Si XAML est excitant, CSS, satan l’habite…

Mais si vous êtes un pratiquant de messes noires (du genre à coder en JS par exemple), vous serez heureux de pouvoir faire vos mises en page Xamarin.Forms en CSS !

L'utilisation de CSS pour styliser vos mises en page est un moyen optimal (langue de put* inside) d'exprimer des styles en tandem avec XAML. Beaucoup de développeurs adorent la magie noire CSS à cause de leur expérience avec les technologies Web n’ayant pas eu la chance de connaître un vrai cursus professionnel en informatique. Mais les développeurs XAML, ces saints, restent accrochés à la définition des styles en XAML. Xamarin.Forms vous offre ces deux options de productivité! Utilisez ce qui vous rend le plus productif (mais de préférence XAML).

Les StyleSheets peut être ajouté en tant que fichiers CSS distincts dans votre projet, ou en ligne dans vos ressources. Une variété de sélecteurs communs sont disponibles pour composer vos styles.

C’est pas merveilleux ça le mariage de la carpe et du lapin ?

(le texte ci-dessus est en partie une traduction très libre de l’annonce officielle : “Cascading Style Sheets (CSS) is a natural companion to FlexLayout given their shared heritage. Using CSS to style your layouts is an optimal way to express styles in tandem with XAML. We know many of you love CSS from your experience with web technologies and we also recognize XAML developers love to express their styles in XAML. We are so excited to bring both of these productivity options to you! Use what makes you most productive.”)

Bon alors next !

Localisation gauche droite

Il y a des langues qui s’écrivent dans l’autre sens que le  nôtre, l’hébreu, l’arabe par exemple. Pour gérer ces cas de façon simple il suffit désormais d’utiliser FlowDirection…

Support de WPF

Voilà une riche idée, rendre à César ce qui lui appartient, un juste retour des choses… C’est un travail communautaire, ça avance mais ce n’est pas encore terminé à 100%. Ce projet permet d’ajouter WPF aux cibles d’une solution Xamarin.Forms… Vous pouvez accéder ici au statut d’avancement de ce chantier très intéressant !

Conclusion

Xamarin.Forms ne déçoit pas, chaque release apporte son lot d’améliorations. On peut certes regretter que des satanistes venus du Web aient noyauté l’équipe de développement pour souiller la pureté mariale de XAML, mais bon, ayons l’esprit large en sachant que cette mode d’ajouter du JS ou du CSS un peu partout pour faire venir des développeurs Web n’a jamais fonctionné qu’à la marge alors restons zen…

Voire la communauté travailler au support de WPF est une excellente chose en revanche. Voire des inventions géniales de Silverlight faire leur comeback par la grande porte est aussi un plaisir.

Il y a vraiment du très bon dans cette version 3.0.

Je vais y revenir forcément.

J’ai eu 4 / 5 mois de charge un peu plus dense et j’ai moins écrit dans Dot.Blog, mais ne vous inquiétez pas, cela va revenir ! Alors…

Stay Tuned !

Catégories: Dévelopement

Dessiner c’est programmer avec Ink To Code

Dot.Blog - dim, 22/04/2018 - 12:51

Un simple croquis qui devient code Xamarin ou UWP ? Oui c’est possible ! Comment ? …

De l’idée à la réalisation

Imaginez que vous venez d'être illuminé par l'inspiration pour votre prochaine application. Vous allez certainement commencer par prendre des notes et esquisser des écrans sur un bout de papier. Mais au lieu de vous saisir d’un carnet de croquis, d’aller noircir un tableau blanc ou même d’écrire sur le le dos d'une serviette de table, pensez plutôt à prendre votre ordinateur et à installer Ink to Code !

Ink to Code est un projet de Microsoft Garage. En quelque sorte c’est la version numérique du dos d'une serviette pour vos idées d'applications. C’est une application Windows 10 qui se lance en quelques secondes et fournit un canevas pour que vous puissiez dessiner des écrans avec votre stylet. Voyons cela de plus près !

Mise en place

La première chose à faire est d'installer Ink to Code à partir du Microsoft Store. Lors de son lancement vous serez accueilli par son interface utilisateur minimaliste et sa grande surface de dessin. Pour savoir quoi dessiner, cliquez sur le bouton Guide dans la barre d'outils. Ceci vous montre les différents éléments de conception que Ink to Code reconnaît :

Dessiner votre application


Imaginons que votre géniale inspiration vous guide vers la prochaine calculatrice de pourboire pour la marché américain. Voici comment vous pourriez la dessiner en quelques secondes avec Ink To Code :

***

Au fur et à mesure que vous dessinez chaque élément de conception, vous remarquerez que Ink to Code convertit les traits d'encre en composants reconnus. En tapotant sur chacun de ces éléments, un menu vous permet de les supprimer ou, s'ils ont été mal reconnus, de les convertir en différents composants. Vous remarquerez également qu'en appuyant sur le deuxième bouton de la barre d'outils de gauche, vous passez en mode contrainte, où vous pouvez dessiner des lignes de contrainte qui peuvent être utilisées pour aligner d'autres éléments.

Comment fonctionne cette magie ? En interne, Ink to Code utilise la puissance de la plate-forme Windows Ink et certaines de ses propres heuristiques pour reconnaître les éléments de conception communs dans vos esquisses d'application et les convertir ensuite en UWP XAML ou en XML de mise en page Android qui peuvent être chargés directement dans Visual Studio.

Exportation vers Visual Studio


Ink to Code est idéal pour esquisser rapidement les grandes lignes de votre idée d'application, mais pour commencer à l'affiner en prototype, vous voudrez exporter vos dessins vers Visual Studio. Pour cet exercice, nous allons construire une application Xamarin.Android :

Cliquez sur le bouton Exporter dans la barre d'outils Encre vers code et choisissez Android dans le menu. Ensuite, choisissez un répertoire temporaire dans lequel exporter le fichier XML de mise en page.

Ouvrez ensuite Visual Studio 2017 et créez un nouveau projet d'application Android vierge.

Si vous ne trouvez pas le modèle de projet, assurez-vous d'avoir Xamarin installé. Vous pouvez également utiliser Visual Studio pour Mac et vous assurer qu'Android est sélectionné pendant l'installation.

Trouvez “Main.axml“ dans votre projet Android sous le chemin “Ressources\layout” et remplacez-le par le fichier que vous avez exporté lors de la première étape.

Ink to Code utilise “ConstraintLayout” dans son XML Android exporté, vous devrez donc ajouter le paquet Nuget Android.Support.Constraint.Constraint.Layout à votre projet.

Cliquez sur Exécuter, et vous devriez voir l'écran que vous avez esquissé lancer dans un émulateur Android comme de véritables widgets Android interactifs !

Vous voudrez probablement continuer à affiner votre mise en page dans Visual Studio après l'exportation vers ce dernier. Pour ce faire, double-cliquez simplement sur le fichier Main.axml et le Xamarin Android Designer s'ouvrira, ce qui est la manœuvre usuelle qui n’a rien de particulier.

Libérez votre créativité !

Maintenant que vous avez une idée de la façon dont vous pouvez utiliser Ink to Code pour esquisser rapidement votre prochaines applications, il est temps de laisser libre cours à votre imagination ! Ink to Code est un projet de Microsoft Garage, ce qui signifie qu'il s'agit d'une expérience et que vos retours seront essentiels pour façonner son avenir. N’hésitez pas à faire des remontées à l’équipe.

Installez et Essayez Ink to Code, esquisser votre prochaine grande idée d'application et dites à l’équipe MS Garage ce que vous en pensez !

Conclusion

Ink to Code dévoile ce que sera la programmation dans 10 ou 20 ans. Adossée sur de l’IA, des systèmes de reconnaissance de formes, le développeur donnera ses idées à l’ordinateur sous la forme de croquis, de phrases. Le plus gros et le plus fastidieux sera alors fait par l’ordinateur lui-même et le développeur pourra alors se concentrer sur son vrai métier, affiner le code, le personnaliser, le rendre fonctionnel.

Je doute que M. Tout-le-monde devienne informaticien grâce à de telles avancées. Car l’informatique se complexifie tous les jours un peu plus et qu’il faut une tournure d’esprit, des connaissances et une expérience typiquement humaine pour faire un bon soft. Mais il est possible que pour les petites Apps sans grandes prétentions l’utilisateur soit capable de les fabriquer. Est-ce-que le fait que votre femme (ou mec) sache faire une omelette au champignon a signé la mort des grands chefs étoilés ? Non bien sûr.

Bon sketching et …

Stay Tuned !

Catégories: Dévelopement

Faut-il bouder les warnings ?

Dot.Blog - sam, 31/03/2018 - 22:59

Les warnings semblent moins intéresser que les erreurs, on le comprend, les unes sont bloquantes, les autres non. Mais certains warnings du compilateurs matérialisent des erreurs potentielles aux conséquences qu’il serait dangereux d’ignorer… Les warnings

Posons le tout de suite, les warnings sont des erreurs comme les autres. ils peuvent même être pires encore car une erreur empêche de compiler donc de délivrer le produit. Un warning n’interdit rien et c’est plus tard qu’une erreur potentiellement grave se matérialisera, une fois le logiciel livré et en production…

Tous les warnings ne véhiculent pas le même niveau de danger potentiel c’est vrai. Mais ils devraient être traités avec attention, non par rigidité psychologique mais tout simplement car ce sont des erreurs mais d’une nature un peu différente.

La programmation asynchrone est source de grandes joies mais aussi de surprises étonnantes. Sa maîtrise n’est pas aisée, raison de plus pour ne pas laisser traîner des warnings la concernant ! Prenons deux exemples de warnings concernant la programmation asynchrone pour illustrer le propos.Code CS1998

A part suggérer qu’il y a 1997 autres erreurs possibles – ce qui donne le vertige (mais la prochaine fera encore plus peur !) – ce warning ne vous dit-il rien ?

Le code CS1998 affiche le message suivant (en version US) :

warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

La cause est assez simple : vous avez spécifié le modificateur “async” dans l’entête de la méthode mais vous ne faites aucun usage de “await” dans cette dernière.

C’est un avertissement à prendre au sérieux. Soit vous avez placé cet “async” pour rien, et il faut le retirer… soit vous aviez prévu d’utiliser “await” et vous avez oublié de le faire ce qui risque de fausser “légèrement” le fonctionnement de la méthode. Situation à corriger immédiatement donc.Code CS4014

Le vertige devient digne de celui d’un Baumgartner avant de sauter de son ballon sonde à 39000 m d’altitude… imaginez 4012(*) autres warnings à découvrir ! :-)

(*) les plus futés auront noté l’erreur… j’aurai du dire 4013 n’est-ce pas ? Ceux qui suivent vraiment auront compris qu’il n’y a pas d’erreur, puisque je vous ai déjà fait découvrir la 1998, il en reste bien 4012 à connaître !

Le message de celui-ci est tout aussi important à reconnaître :

warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

La cause : Vous appelez une méthode qui possède le modificateur “async” et qui retourne une Task<> et votre code ne fait rien pour attendre le résultat de cette dernière.

Peut-être est-ce volontaire. Le cas peut se produire (si le code qui suit l’appel ne se sert pas du résultat par exemple).

Si ce n’est pas intentionnel il est urgent de vérifier de le code appelant et le code appelé !

Si la situation est voulue, on peut supprimer le warning avec un #pragma. Cela est tout à fait acceptable car l’intention du développeur devient visible : il assume la situation. C’est mille fois préférable à un warning laissé en l’état. Exemple d’utilisation de pragma issu la doc Microsoft pour un autre warning :

On note que la suppression du warning doit s’accompagner d’une “remise en route” par un “restore” en spécifiant à nouveau le code du warning concerné. Cette double écriture est fastidieuse et surtout l’obligation de répéter le code du warning peut s’accompagner d’une faute de frappe masquant le warning pour tout le reste du code sans le savoir.

Une autre astuce consiste à tout simplement déclarer un variable qui ne sert à rien et lui affecter le résultat de l’appel, de type “var dummy = appelDeCodeAsync;”

Ne vous inquiétez pas pour cette variable supplémentaire, le compilateur est assez malin pour la supprimer totalement du code compilé. Son avantage est d’éviter un #pragma (et éviter de se tromper en répétant le code du warning). Comme cette solution ne coûte rien dans le code compilé elle est intéressante. Bien entendu elle sous-entend une parfaite compréhension du warning en question et ne doit pas servir à masquer une situation non maîtrisée ! Les puristes détesteront cette solution d’ailleurs…

Je préfère moi aussi le #pragma qui marque l’intention plus clairement d’une façon standardisée par le langage. Mais le risque de supprimer le warning dans tout le code (en cas d’erreur sur le second code) et de masquer des erreurs potentielles me chagrine tout autant.

A vous de voir…Treat Warnings as Errors

La solution pour éviter la paresse naturelle qui consiste à considérer les Warnings comme des citoyens de seconde zone c’est justement de les élever au niveau des erreurs !

Les warnings de compilation peuvent être traités comme des erreurs bloquantes, il suffit de le paramétrer dans la page build du projet :


Conclusion

La programmation asynchrone est pleine de surprise. Mais il n’y a pas qu’elle qui peut donner lieu à des warnings, ce n’était qu’un exemple parmi des centaines d’autres ! Certains développeurs laissent parfois des tartines de warnings dans leurs projets. Pourtant il faut les traiter avec la même vigilance que des erreurs de compilation car derrière chaque warning peut se cacher de potentiels bugs très sournois.

Le cas des warnings 1998 et 4014 est intéressant à ce titre.

Les warnings sont des erreurs comme les autres, mais de nature plus vicieuse, c’est un peu la leçon à retenir…


Stay Tuned !

Catégories: Dévelopement

Hériter d’une classe Sealed ?

Dot.Blog - dim, 25/03/2018 - 19:08

C’est un peu un piège, bien entendu, une classe “sealed” on ne peut en hériter... Pourtant le besoin existe. Par exemple une String différenciée. Comment contourner l’interdiction du Framework ? Est-ce possible ? Pourquoi ?

C’est la première question, et la plus importante peut-être. Pourquoi vouloir créer des types descendant de classes sealed ? En quoi cela peut-il être utile ?

Si je parle d’utilité c’est bien parce que le code doit répondre à cet impératif, tout code sans exception. On code pour faire quelque chose d’utile. Sinon coder n’a pas de sens.

Prenons la classe String. Le Framework ne permet pas de la création de classes en héritant et pour bloquer toute velléité en ce sens, la classe String est celée (sealed). Les concepteurs du Framework ont définitivement fermé cette porte. Mais ils en ont ouvert une autre : les extensions de classe. Cela permet d’étendre les possibilités de toute classe, même sealed, donc de string aussi.

Cela serait parfait si le besoin d’hériter d’une classe se limitait à vouloir lui ajouter des méthodes... Or ce n’est pas la seule motivation envisageable !

Prenons un cas concret : vous créez un logiciel qui pour autoriser la saisie de nombreux paramètres de classes différentes utilise une PropertyGrid (comme celle de Windows Forms qui peut s’utiliser sans problème sous WPF). Au sein d’un tel mécanisme vous pouvez généralement définir vos propres éditeurs personnalisés, qui dépendent du type de la valeur. Par exemple, pour une propriété de type Color vous pourrez écrire un éditeur offrant un nuancier Pantone et une “pipette”. Cela sera plus agréable à vos utilisateur que de taper à l’aveugle un code hexadécimal pour définir une couleur.

Imaginons une seconde que parmi ces paramètres qui seront saisis dans une PropertyGrid (ou son équivalent WPF ou UWP) il se trouve certaines chaines de caractères définissant par exemple le nom d’un fichier externe.

Dans un tel cas vous souhaitez qu’en plus d’un simple éditeur de string s’affiche aussi un petit bouton ellipse “...” qui permettra à l’utilisateur de browser les disques pour directement sélectionner un nom de fichier existant. Peut-être même la zone gèrera-t-elle le drag’n drop depuis l’explorateur.

Hélas... Soit vous enregistrez le nouvel éditeur pour le nom d’une propriété précise (ce qui est très contraignant et source de bogues), soit vous l’enregistrez pour son type, String, et dès lors ce seront toutes les strings qui bénéficieront du browser de fichiers, ce qui n’a aucun sens !

Que ne serait-il pas plus facile de définir juste “public class NomDeFichier : string {} “ et Hop ! l’affaire serait jouée !

L’éditeur serait enregistré pour le type “NomDeFichier”, les noms de fichiers dans les paramètres ne seraient plus de type “string” mais de type “NomDeFichier” et tout irait pour le mieux dans le meilleur des mondes.

Donc voici concrètement un cas qui montre l’utilité évidente de créer des classes héritant de string (ou d’autres classes sealed), même totalement vides, juste pour créer une CLASSification, à la base même de la programmation objet malgré tout...

Je ne doute pas qu’éclairez par cet exemple vous en trouviez d’autres, même totalement différents.

En tout cas nous avons répondu à la première question. C’est utile, et puis la programmation objet se base sur l’héritage pour régler de nombreux problèmes, il y a donc une légitimité naturelle à vouloir hériter d’une classe. “sealed” est un peu frustrant. C’est presque un contre-sens dans un monde objet. La justification du code plus efficace produit par une classe sealed me semble assez artificielle et ne se défendant que difficilement. Mais C# est ainsi fait, la perfection n’existe pas. Heureusement la grande souplesse du langage permet de contourner assez facilement ce genre de problème !Comment ?

Je vous l’ai déjà dit : ce n’est pas possible, n’insistez pas ! ...

Mais comme ce billet n’existerait pas si je n’avais pas une solution à vous proposer, vous vous dites qu’il doit y avoir un “truc”.

La classe string est sealed. Donc il n’y a pas de “truc” magique. Pas de moyen de bricoler le Framework non plus. Je vous ai dit que ce n’est pas possible !

Mais il y a une solution, un peu moins directe mais tout à fait raisonnable et “propre”.

Elle consiste tout simplement à développer une autre classe qui n’hérite de rien.

Hou là ! Réinventer le type string juste pour une raison de classification semble carrément overkilling !

C’est vrai, et nous ne nous lancerons pas sur une voie aussi complexe. En revanche on peut être rusé et tenter d’en écrire le moins possible tout en se faisant passer par une string...

En fait c’est assez facile mais cela utilise des éléments syntaxiques peu utilisés comme les opérateurs implicites.

L’astuce consiste à créer une classe “normale” n’héritant de rien, et possédant une seule propriété, Value, de type string (ou d’un autre type sealed dont on souhaiterait hériter).

C’est sûr que ce n’est pas compliqué à écrire mais cela ne règle pas la question. Il n’est pas possible de faire passer notre classe pour string. Partout il faudra changer ‘x = “toto”’ par ‘x.Value = “toto”’ et ce n’est pas du tout ce qu’on cherche !

C’est oublier les opérateurs “implicit” qui permettent de convertir une instance d’une classe en d’autres types (et réciproquement). Implicitement. C’est à dire sans avoir à écrire quoi que ce soit dans le code qui utilise la dite classe à convertir.

Pour commencer nous aurons ainsi un code qui ressemble à cela :public class MyString : IEquatable<MyString>, IConvertible
{
private string value;

public MyString() { }

public MyString(string value)
{
this.value = value;
}

public string Value
{
get { return value; }
set { this.value = value; }
}

public override string ToString() { return value; }

public static implicit operator MyString(string str)
{ return new MyString(str); }

public static implicit operator string(MyString myString)
{ return myString.value; } ...

Le type MyString déclare une propriété Value de type string, mais surtout elle déclare deux opérateurs implicites : l’un permettant de convertir une string en MyString, et l’autre s’occupant du sens inverse.

C’est presque tout. Ca marche. Je peux écrire ‘MyString x = “toto”’ et l’inverse aussi (affecter à une variable de type string directement une variable de type MyString).

Dans la réalité il faudra s’occuper d’autres détail, comme les opérateurs d’égalité par exemple, ou bien les conversions de type (interface IConvertible), etc.

Mais la majorité de ce code peut  être directement vampirisé de la classe string puisque la valeur Value est de ce type et que notre classe ne contient rien d’autre à convertir.

On en arrive à un code final de ce type (en supposant comme dans l’exposé plus haut qu’on souhaite avoir une string personnalisée pour saisir le nom d’un dictionnaire, Donc une DictionaryNameString) :public class DictionaryNameString : IEquatable<DictionaryNameString>, IConvertible
{
private string value;

public DictionaryNameString() { }

public DictionaryNameString(string value)
{
this.value = value;
}

public string Value
{
get { return value; }
set { this.value = value; }
}

public override string ToString() { return value; }

public static implicit operator DictionaryNameString(string str)
{
return new DictionaryNameString(str);
}

public static implicit operator string(DictionaryNameString dictionary)
{ return dictionary.value; }

public bool Equals(DictionaryNameString other)
{
if (ReferenceEquals(null, other)) return false;
return ReferenceEquals(this, other) || Equals(other.value, value);
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == typeof(DictionaryNameString) &&
Equals((DictionaryNameString)obj);
}

public override int GetHashCode()
{
return (value != null ? value.GetHashCode() : 0);
}

public static bool operator ==(DictionaryNameString left, DictionaryNameString right)
{ return Equals(left, right); }

public static bool operator !=(DictionaryNameString left, DictionaryNameString right)
{ return !Equals(left, right); }

#region IConvertible Members

public TypeCode GetTypeCode() { return TypeCode.String; }

public bool ToBoolean(IFormatProvider provider)
{ return Convert.ToBoolean(value, provider); }

public byte ToByte(IFormatProvider provider)
{ return Convert.ToByte(value, provider); }

public char ToChar(IFormatProvider provider)
{ return Convert.ToChar(value, provider); }

public DateTime ToDateTime(IFormatProvider provider)
{ return Convert.ToDateTime(value, provider); }

public decimal ToDecimal(IFormatProvider provider)
{ return Convert.ToDecimal(value, provider); }

public double ToDouble(IFormatProvider provider)
{ return Convert.ToDouble(value, provider); }

public short ToInt16(IFormatProvider provider)
{ return Convert.ToInt16(value, provider); }

public int ToInt32(IFormatProvider provider)
{ return Convert.ToInt32(value, provider); }

public long ToInt64(IFormatProvider provider)
{ return Convert.ToInt64(value, provider); }

public sbyte ToSByte(IFormatProvider provider)
{ return Convert.ToSByte(value, provider); }

public float ToSingle(IFormatProvider provider)
{ return Convert.ToSingle(value, provider); }

public string ToString(IFormatProvider provider)
{ return value; }

public object ToType(Type conversionType, IFormatProvider provider)
{ return Convert.ChangeType(value, conversionType, provider); }

public ushort ToUInt16(IFormatProvider provider)
{ return Convert.ToUInt16(value, provider); }

public uint ToUInt32(IFormatProvider provider)
{ return Convert.ToUInt32(value, provider); }

public ulong ToUInt64(IFormatProvider provider)
{ return Convert.ToUInt64(value, provider); }

#endregion
}

Et voici une classe “string” personnalisée, utilisable comme string et offrant globalement les mêmes services dans 99% des cas (affectations dans un sens ou dans l’autre, conversions).

Petit plus : notre classe n’est pas “sealed”... Il suffit de l’appeler “MyStringBase” et d’hériter ensuite de cette classe pour se créer des tas de types “string” personnalisés.

En dehors de l’exemple que je donnais, on peut imaginer de nombreux cas où faire un “if (variable is MySpecialString)...” pourra simplifier beaucoup les choses. Tout en conservant une écriture simple et limpide, un code propre et maintenable.Conclusion

C# est un langage fort subtil, il sait créer des blocages qui évitent les grosses boulettes (comme le fait qu’il n’autorise pas l’héritage multiple ou l’existence des classes sealed) mais en contrepartie il offre de nombreuses portes de sortie permettant de façon élégante de faire ce qu’on veut. Reste à bien le maîtriser pour y arriver, et ça c’est un sacré challenge, plus on travaille avec ce langage, plus on comprend qu’on est loin de tout en savoir !

Stay Tuned !

Catégories: Dévelopement

Générer convenablement des nombres aléatoires

Dot.Blog - sam, 24/03/2018 - 22:31

Les ordinateurs sont des machines déterministes, même si on peut parfois en douter ! Ils sont ainsi très mauvais au jeu des valeurs aléatoires, comme nous, mais pour d’autres raisons (trop de rigidité pour les premiers, trop de poids de l’inconscient pour les seconds). Peut-on se fier à Random ? Comment l’utiliser ? Peut-on mieux faire ? Nous allons voir cela …L’aléatoire

C’est une notion … complexe.

Il ne faut d’ailleurs pas confondre l’aléatoire et le chaos qui est un autre concept. En informatique ce qu’on cherche le plus souvent c’est la génération de nombres aléatoires sans trop se poser de questions… ce qui n’est pas malin !

Mais supposons que nous ayons un tel groupe de nombres aléatoires. Une fois cela en poche on peut tout aussi bien générer des couleurs aléatoires ou des chaînes de caractères ayant cette propriété et c’est ce qui intéresse l’informaticien quand il pense “nombre aléatoire”. En matière de programmation on pourrait ainsi affirmer “ici tout est nombre !” car on sait transformer les nombres en n’importe quoi, musique, image…l’inverse étant vrai (c’est même à cela qu’on doit l’essor du “digital”).

Beaucoup de définitions savantes ont été données au fil du temps à cette notion d’aléatoire.

Mais au final nous en sommes revenus…

Ce que nous appelons “aléatoire” est en réalité une forme de “complexité” puisque la prédiction n’est pas possible. Kolmogorov nous a expliqué ce qu’il entendait par la “complexité aléatoire” qu’il a réduit a sa plus simple expression, la taille du programme nécessaire pour reproduire un contenu, une information. Plus le programme est court et moins l’objet est complexe. Lorsque le programme le plus court consiste à simplement recopier la donnée cela indique que sa complexité est maximale et que rien ne permet de simplifier, de compresser l’information véhiculée.

Complexité et nombre aléatoires sont deux choses différentes, mais un nombre réellement aléatoire est par définition complexe car rien ne permet d’en prédire ou d’en calculer les chiffres. Il n’y a pas de programme plus court que celui qui consiste à le recopier.

D’ailleurs un “nombre aléatoire” qu’est-ce que cela veut dire ? Un nombre peut-il être “aléatoire” comme il peut être pair ou premier ? Non bien sûr, un “nombre aléatoire” est une forme d’abus de langage. C’est une série de nombres qui peut l’être. Un nombre tout seul ne peut être qualifié d’aléatoire. Il faut donc entendre par “nombre aléatoire” un élément d’une série aléatoire de nombres. La complexité se jugera ainsi sur la série et non sur un seul nombre. Mais on peut aussi considérer un nombre comme une série de chiffres et tenter de juger de sa complexité sous cet angle.

Les nombres peuvent parfois être très grands, voire infini, et même ne présenter aucune régularité tout en étant peu complexe ! Prenez le cas de Pi par exemple. Il présente une infinité de décimales, aucun schéma répétitif n’a pu être détecté bref ça semble être du lourd niveau complexité, et bien non.. Car face à l’infini de sa taille, on peut opposer un très court programme qui calcule Pi. Cela peut demander du temps, mais cela prouve que Pi n’est pas complexe puisqu’on peut le “compresser” en quelques lignes de programmation… Certes calculer les décimales va être long, mais cela ne change rien à la faible complexité de Pi. Oui l’infini c’est long, surtout vers la fin, mais ce n’est pas un critère de complexité.

En théorie de l’information on est même allé encore plus loin. Il ne s’agit même plus d’écrire un programme pour reproduire une donnée et d’en jauger la taille par rapport à celle-ci, il suffit d’utiliser … Zip !

La complexité aléatoire d’une information peut se mesurer ainsi à la taille du fichier produit par Zip (entendez un programme de compression réputé efficace peu importe son nom)…

Prenez deux cents pages A4 remplies d’espaces (ASCII 32), prenez 7Zip… le fichier final sera largement plus petit que le document original c’est une évidence (si vous en doutez, faites le test !). En revanche prenez le même document de 200 pages contenant toutes les décimales de Pi que vous pourrez y placer… le fichier compressé final ne sera pas plus petit que votre document original (un simple fichier texte sans artifice).

La nature aléatoire d’une donnée peut se mesurer à sa complexité interne, et cette complexité peut se calculer soit en écrivant un programme qui reproduit la donnée, soit tout simplement en essayant de zipper la donnée ! Pour être totalement précis la complexité de Komogorov est parfaitement définie mais elle n’est pas calculable (au sens mathématique, pas par ignorance). Mais ne nous écartons pas trop du fil aléatoire de cet article…

La nature aléatoire d’un nombre et sa complexité restent des notions différentes mais elles peuvent donc se rejoindre au moins pour juger de la qualité des données.

Il faut aussi savoir séparer la complexité aléatoire de la complexité organisée. Une information peut très bien avoir une complexité organisée élevée mais un complexité aléatoire faible ou l’inverse ou les deux ou aucune… L’exemple de Pi plus haut montre qu’il n’est pas désordonné, sa structure nous est cachée et nous apparait aléatoire mais il existe une organisation forte puisqu’on peut résoudre son infinité à un calcul de quelques lignes. Pi a une haute complexité organisée mais une faible complexité aléatoire (puisqu’on peut prédire avec précision la valeur de n’importe quelle décimale). C’est un fait troublant car en même temps la suite de chiffres de Pi est selon toute évidence aléatoire ! Mais rassurez-vous même pour les spécialistes la séparation entre complexité organisée et complexité aléatoire n’est pas forcément évidente à faire tout le temps.

Les générateurs de nombres aléatoires se “mesurent” d’ailleurs plutôt à l’aide d’outils statistiques. Car tout dépend de ce qu’on cherche, la complexité ou une certaine distribution ce qui n’a rien à voir bien entendu. Le générateur aléatoire le plus sobre qu’on puisse vouloir est celui où tous les nombres ont une équiprobabilité d’apparaître. La moyenne est idéalement de Max/2 avec une grande variance. Mais on peut aussi vouloir utiliser des “nombres aléatoires” suivant une distribution particulière comme celle d’une courbe de Gauss ou de n’importe quelle fonction.

La non prédictibilité et la complexité ne font pas bon ménage… Car plus on veut suivre une loi de distribution plus on introduit un biais fort dans le tirage qui est de moins en moins aléatoire… Par exemple des tirages aléatoires suivant une loi de répartition normale permettent avec certitude d’écarter de nombreuses valeurs (celles qui n’appartiennent pas à l’aire de la fonction).

Il y a de fait une confusion à éviter entre la complexité d’un tirage au sort et sa loi de distribution qui trahit certaines caractéristiques de l’ensemble ce qui rend les données prévisibles. Prévisible != Aléatoire…Ordinateurs et nombres aléatoires

Un ordinateur étant une machine déterministe (même si certains bogues peuvent parfois nous en faire douter !) l’aléatoire est totalement hors du champ de ses possibilités, quelle que soit sa taille, sa puissance, sa mémoire ou la présence d’un coprocesseur mathématique (dont on ne parle plus depuis quelques années puisque systématiquement intégré aux CPU ce qui ne fut pas le cas pendant longtemps).

Au mieux, il produira une suite de nombres “pseudo aléatoires” répondant à certains critères (c’est à dire simulant au plus proche certaines courbes ou lois comme celle de Poisson ou d’autres formes de distribution).

Les nombres aléatoires ne peuvent ainsi être générés qu’en s’appuyant sur des phénomènes physiques eux-mêmes réputés aléatoires comme les phénomènes quantiques. C’est pour cela que pour générer des vraies séries de nombres aléatoires sur un ordinateur il faut absolument utiliser un hardware spécifique. Il en existe de différentes nature selon le degré de précision dans l’aléatoire qu’on désire (s’il est possible de parler de précision ici). Ces boitiers qui peuvent se brancher sur un port USB par exemple, utilisent des phénomènes quantiques qu’ils amplifient et numérisent pour obtenir des nombres : bruit thermique d’une résistance par exemple.

Donc hors de ces hardwares, parler de nombres aléatoires avec un ordinateur est un abus de langage dans le meilleur des cas et une hérésie mathématique dans le pire….NET et Random

Tout le monde la connaît cette classe du framework, c’est le moyen le plus simple d’obtenir des nombres pseudo aléatoires sous .NET.

Mais de ce que j’ai toujours pu constater lorsque j’audite du code, cette classe est mal utilisée dans la grande majorité des cas. Cela était vrai il y a des années, cela reste vrai aujourd’hui. Tout comme la compréhension des bases de données rationnelles (Formes Normales, loi de Codd…) semble restée bloquée à l’ère de DBase II, la compréhension de Random n’a pas évoluée en 10 ans.

Il y a par exemples des erreurs classiques qui montrent cette méconnaissance des principes fondamentaux de Random.

La première consiste à vouloir initialiser l’instance avec l’heure courante comme pour augmenter la nature aléatoire des données qui seront générées. C’est une bévue. La classe Random utilise déjà l’horloge comme graine si on n’utilise son constructeur par défaut…

Inutile donc d’écrirevar r = new Random(DateTime.Now.Millisecond);

Cela ne sert à rien.

Autre erreur classique, multiplier les instances pour des tirages différents en espérant gagner “en aléatoire”…

Le pire que j’ai pu voir dans ce style est une série de Random instanciées l’une derrière l’autre. Si les créations sont réellement à la suite ou peu espacées il y a toutes les chances qu’elles soient initialisées avec la même heure qui a une résolution de 20 ms environ ! Et toutes les instances de Random produiront alors …. exactement la même série de nombres !Une seule instance suffit

Il ne sert donc à rien de multiplier les instances de Random dans une application en espérant avoir “plus” d’aléatoire au final. Bien au contraire on risque d’obtenir, comme l’exemple ci-dessus le démontre, une uniformité qui n’a vraiment plus rien d’aléatoire, même “pseudo” !

Si les exigences mathématiques sont assez faibles on peut parfaitement se contenter de Random. Mais alors, le plus malin consiste à créer une seule instance pour toute l’application. On s’assure bien ainsi que chaque run de l’application se basera sur une série différence et surtout qu’au sein de l’application tous les nombres sembleront bien être aléatoires...

Je vous passe l’exemple d’une classe statique déclarant une instance de Random et exposant des méthodes statiques calquant les méthodes principales de cette dernière. C’est enfantin.

Bref, la façon la plus simple d’avoir réellement des nombres pseudo aléatoires dans une application est de n’utiliser qu’une seule instance centralisée de Random. La classe n’est pas garantie “thread safe” donc si vous l’utiliser dans un contexte multithread le mieux est soit d’avoir une instance par thread. L’idée de créer un wrapper statique utilisant des locks pourrait être séduisante pour certains mais franchement je vous le déconseille, cela créera un goulet d’étranglement écroulant en partie l’avantage du multithreading.Complexité, aléatoire et Random

C’est ici que les deux histoires se rejoignent…

Random génère toujours la même séquence pour la même graine, ce qui n’est déjà pas très aléatoire comme fonctionnement. De plus les séries produites - puisque dépendantes de la graine - sont forcément issues d’un processus calculatoire tout à fait prédictible pour un spécialiste. Utiliser Random pour des opérations sensibles notamment touchant à la sécurité serait ainsi une grave erreur…

Pour cela il existe le namespace System.Security.Cryptography.

Ce namespace, comme son nom le laisse deviner, contient de nombreuses classes fort utiles en cryptographie. Et qui dit cryptographie dit nombres (pseudo) aléatoires. Mais comme il s’agit ici de sécurité les exigences mathématiques placent la barre un peu plus haut. Même si la qualité ne peut rivaliser avec du hardware spécialisé, elle dépasse de loin celle de Random.

Loin de moi l’idée d’aborder le contenu de ce namespace en quelques lignes. Je veux juste attirer votre attention sur son existence et qu’on y trouve un générateur qui remplace Random de façon plus efficace en évitant le risque de répétition des valeurs si plusieurs instances doivent être créées de façon proche dans le temps. Entre autres choses.

Il suffit d’utiliser RNGCryptoServiceProvider du namespace indiqué.

Cette classe permet de créer des instances de RandomNumberGenerator offrant un comportement plus fiable que Random. Il faut utiliser une instance par thread comme Random.

Quelle complexité aléatoire attendre ? Si on utilise la méthode de la compression, Random et RNG ont une complexité assez proche. Faites tourner le petit programme ci-dessous pour vous en convaincre (mode Programme C# dans LinqPad ou faites le tourner dans VS en mode console).

En revanche on obtient des séries ayant une meilleure distribution des valeurs produites et une prédictibilité bien moins grande qu’avec Random.void Main() { var r = new Random(); var t = new double[10000]; for (var i = 0; i < 10000; i++) t[i] = r.NextDouble(); //for (var i = 0; i < 10000; i++) t[i] = NextRandomDouble(); //for (var i = 0; i < 10000; i++) t[i] = 1; Console.WriteLine($"Taille tableau: {sizeof(double) * 1000}"); var tc = SerializeAndCompress(t); Console.WriteLine($"Taille tableau compressé: {sizeof(Byte) * tc.Length}"); var tdc = DecompressAndDeserialize<double[]>(tc); Console.WriteLine($"Taille tableau décompressé: {sizeof(double) * tdc.Length}"); } // Define other methods and classes here public static byte[] SerializeAndCompress(object obj) { using (MemoryStream ms = new MemoryStream()) { using (GZipStream zs = new GZipStream(ms, CompressionMode.Compress)) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(zs, obj); } return ms.ToArray(); } } public static T DecompressAndDeserialize<T>(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { using (GZipStream zs = new GZipStream(ms, CompressionMode.Decompress, true)) { BinaryFormatter bf = new BinaryFormatter(); return (T)bf.Deserialize(zs); } } } public static double NextRandomDouble() { var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); var bytes = new byte[8]; rng.GetBytes(bytes); var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11); return ul / (Double)(1UL << 53); }

On trouvera dans ce code quelques idées annexes qui pourront vous intéresser comme la sérialisation + compression (et son inverse) en mémoire des données utilisant GZip (du framework .NET).Quand utiliser RandomNumberGenerator plutôt que Random ?

Les réponses seront assez simples :L’intention

Si votre code n’a rien à voir avec la sécurité et que la qualité des nombres aléatoires n’a pas grande influence utilisez Random, sinon utilisez RandomNumberGenerator on comprendra alors que vous tenez à utiliser un générateur plus pointu.La rapidité

Si les tirages au sort sont nombreux et peuvent gêner l’exécution de votre programme préférez Random car il est beaucoup plus rapide que RandomNumberGenerator, sauf bien sûr si la sécurité est un point important.La distribution des nombres

La classe Random ne produit pas un spectre très homogène. Si vous désirez des nombres aléatoires mieux répartis utilisez RandomNumberGenerator.La répétabilité

C’est un comble ! Mais oui la répétabilité peut être une bonne raison (mais une bien mauvaise publicité !) de préférer Random… Pour produire des tests identiques il peut s’avérer très intéressant d’utiliser les défauts de Random en prenant soin d’utiliser toujours la même graine lors de la création de l’instance. Ainsi new Random(5); produira à coup sûr la même séquence à chaque exécution. Elle sera différente de celle produite en utilisant une autre graine et la série aura toujours l’apparence de l’aléatoire. Mais cette répétition des mêmes séquences pour les mêmes graines peut s’avérer très utile !Conclusion

Il y aurait bien d’autres choses à dire sur les nombres aléatoires, pseudo ou non. C’est un sujet passionnant. Mais le but était principalement d’attirer votre attention sur les bonnes et les mauvaises utilisations de Random et vous signaler l’existence dans le Framework d’autres classes plus “pointues” pour produire des séries pseudo aléatoires. La mauvaise maîtrise des outils du framework n’est pas un tort en soi, on a tous quelque chose à apprendre, tous les jours. Mais quand je vois les mêmes erreurs se reproduire années après années je me dis que l’information a du mal à passer et que c’est dommage.

Stay Tuned !

Catégories: Dévelopement

Les deux règles pour comprendre XAML

Dot.Blog - mar, 06/03/2018 - 10:00

Des règles et des bonnes pratiques pour développer des applications XAML 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 ou Blend). Il faut accumuler de l’expérience et mémoriser de nombreux patterns pour architecturer et designer correctement une application. Mais il y a deux règles à comprendre pour bien programmer en XAML. XAML qui es-tu ?

XAML, le vrai, le dur, le tatoué, il n’y en a qu’un seul. Celui de WPF. Ces deux technologies sont nées ensemble et sont à jamais liées. Le XAML de WPF est la référence absolue. Il y a eu sa variante “everywhere” (WPF/E appelé plus tard Silverlight) qui n’a hélas pas survécu malgré ses grandes qualités, il y a eu Silverlight pour Windows Phone, passé à la trappe aussi, puis le XAML de WinRT version peu aimée du framework associée à Windows 8 mais qui n’est pas morte et qui a donné naissance à UWP, un XAML très honorable mais pas du niveau de celui de WPF, et enfin le XAML des Xamarin.Forms. Ce dernier ne peut rivaliser graphiquement avec WPF car le principe même est d’instancier des contrôles natifs ce qui supprime la possibilité de modifier leur aspect visuel via XAML et ses primitives graphiques, absentes des XF.

Donc aujourd’hui XAML c’est avant tout celui de WPF, et celui de UWP. Malgré mon support inconditionnel depuis des années à Xamarin et aux Xamarin.Forms, côté graphique et pour les raisons évoquées on ne peut pas comparer ce XAML là aux autres.WPF fait de la résistance

Ca fait des années que je le dis, WPF est loin d’être devenu inutile. Je le disais quand Windows 8 et Metro sont sortis, je l’ai dit pour Windows 10 et UWP, et je continue à le dire car rien n’a changé dans la justification : WPF est toujours le seul et unique moyen de créer des applications PC hyper performantes et lookées dont la distribution reste sous le contrôle de son auteur ! UWP oblige à passer par le Store de MS, c’est restrictif.

Pour le Mac, pour les smartphones, Xamarin.Forms est la solution la plus aboutie, d’autant qu’elle couvre simultanément UWP ce qui n’oblige pas à cibler cette plateforme de façon spécifique. Mais comme je le disais, privée de sa facette graphique le XAML des Xamarin.Forms ne peut pas être comparé à celui de WPF et d’ailleurs il n’y a pour l’instant aucune vraie concurrence puisque WPF ne peut pas tourner en cross-plateforme et Xamarin.Forms ne peut pas tourner sur un PC sauf par le biais de UWP.

Le XAML de WPF et celui de UWP qui reste très complet malgré tout, sont ainsi les seules émanations de ce langage fantastique jamais égalé ni de près ni de loin par aucun éditeur de langage de programmation dans le monde.

La philosophie de XAML est totalement reprise dans UWP et dans les Xamarin.Forms ce qui est déjà extraordinaire, mais les deux règles que je vais aborder ici ne concernent que WPF en premier chef et UWP en second lieu.

WPF fait donc bien de la résistance, tellement en avance pour son époque qu’il reste la référence absolue pour XAML aujourd’hui encore.

Les éditeurs de logiciels qui ont fait le dos rond durant les époques troubles de Windows 8 jusqu’à 10 doivent bien mettre à jour leurs logiciels qui ont vieilli… Et quel éditeur de logiciel peut se permettre de perdre 20 à 30% de son CA juste pour le fun en créant des Apps UWP pour le Store ? Cela peut être une démarche complémentaire, mais je ne connais aucun actionnaire qui accepterais de perdre autant sur ses dividendes ! Donc pouf pouf … ben non, pas de pouf pouf car il n’y a qu’un seul choix possible pour rester maître de sa création et la distribuer comme on veut au prix qu’on veut : WPF.

Il existe aussi d’autres raisons, comme le fait qu’UWP nécessite un Windows 10 alors qu’il existe encore beaucoup de Windows anciens dans certains parcs informatiques.

Bref, faire du XAML autonome et complet c’est choisir WPF. Si on ne se base que sur l’aspect graphique UWP peut faire l’affaire avec les restrictions techniques et financières déjà évoquées plus haut.Programmation par Templates

Sous WPF/UWP lâchez les vieilles habitudes. L’une de celles 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, WPF et UWP 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

J’adore cet exemple car c’est certainement l’un des plus vieux qu’on puisse trouver ! Il prouve à la fois la fantastique avance de XAML dès sa sortie et sa spécificité technique. Sticky notes pour WPF est un cas d’école (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. Certes le temps à passé et côté graphisme aujourd’hui la mode est au flat et le skeuomorphisme est un peu dépassé, mais ce n’est pas grave c’est un exemple qui à l’avantage d’être très visuel pour illustrrer mon propos.

Ce 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.Le bouton de sélection de langue

Dans une application UWP 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 (l’exemple est très réducteur quant au support international mais on s’en contentera ici). 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 Visual State Manager) 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 !Les règles

On en arrive aux fameuses deux règles illustrées par ces exemples.

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

La programmation Xaml (WPF/UWP) 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. Ce changement n’est pas récent, il remonte à la création de WPF mais c’est le changement de paradigme le moins bien compris aujourd’hui encore. 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 le 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 sous WPF historiquement) 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 sélection 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 ne reçoit pas un événement (ou une ICommand) mais voit l’une de ces propriétés (IsFrench) 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 (INPC), il avertit en retour la Vue que toutes les propriétés de type texte ont été modifiées. La vue (et ses bindings) y réagit en rafraichissant les affichages… sans programmation ou presque.

Toute cette mécanique s’est déroulée sans aucun gestionnaire d’événement, sans aucune programmation de Commande, 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 une toolbox MVVM).Règle 2 :  de l’événementiel au binding

La seconde règle d’or à bien comprendre pour tirer totalement partie de XAML et de ses enfants (WPF et UWP mais aussi les Xamarin.Forms ici) 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 XAML !

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 à lever le voile qui vous empêchait de les voir ainsi. Vous ne ferez qu’entrer plus vite dans la catégorie précédente !

Stay Tuned !

Catégories: Dévelopement

XML, arbres, LINQ et parallélisme

Dot.Blog - lun, 05/03/2018 - 21:06

Traiter des données arborescentes est toujours un peu délicat car cela implique l’usage de code récursif, sorte d’épouvantail à informaticien… Pire si tout cela doit être parallélisé c’est un cauchemar pour certains ! Mais c’est oublier que ces problèmes complexes peuvent être résolus par quelques lignes de C# avec l’aide de LINQ !

Arborescences

Un arbre c’est beau. Il y a un tronc et des ramifications, les branches. Elles-mêmes se ramifient en sous-branches jusqu’à atteindre les feuilles. On oublie souvent que la partie la plus importante d’un arbre ce sont ses racines, ce qu’on en voit n’est là que pour les faire vivre Mais de notre point de vue c’est idem car si on veut on peut tout aussi bien étudier l’arborescence de son système racinaire, c’est tout aussi complexe et ramifié. A l’endroit ou à l’envers, un arbre est ramifié !

De nombreux problèmes font intervenir en informatique des structures de données arborescentes. Les créer, mais surtout les traiter (donc les parcourir) posent néanmoins des problèmes à beaucoup d’informaticiens car cela fait intervenir l’épouvantail que j’évoquais plus haut : la récursivité. Les algorithmes récursifs sont eux aussi très beaux, comme les arbres. En peu de code ils peuvent traiter des ramifications complexes. La clé étant qu’une structure arborescente est une structure “self replicating”, auto récplicative.

Vouloir la traiter et l’aborder comme une structure de données classique est une erreur qui rend le problème très difficile à résoudre. La réponse est dans l’utilisation d’un code lui-même auto réplicatif. Non pas qu’il va se démultiplié en mémoire par enchantement, mais dans le sens où son exécution va pouvoir s’auto répliquer grâce à l’utilisation de la pile qui lui servira de fil d’Ariane pour ne pas se perdre dans le labyrinthe du Minotaure !

Code et arbre

Mais voilà le problème, écrire un code capable de se comporter ainsi, de savoir balayer tous les chemins d’un arbre et de retrouver son chemin vers la sortie n’est pas un exercice mental avec lequel tout le monde est à l’aise.

Créer une arborescence est assez simple, mais la traiter l’est beaucoup moins.

Surtout de nos jours où tout traitement digne de ce nom ce doit en plus d’utiliser tous les cœurs de la machine, donc d’être multitâche, et même encore plus complexe ici : parallélisé.

Création d’arbre parallélisée

Pour faire simple car les données de test ne sont jamais très simples à trouver dans ce genre de cas nous allons choisir comme source l’arborescence d’un disque dur. C’est assez gros et diversifié pour créer un arbre bien costaud.

Nous allons créer cette structure sous la forme d’un fichier XML qui pourra ensuite être exploité soit directement en mémoire soit après été stocké sur disque.

public static XElement BuildTree(string dirName) { var di = new DirectoryInfo(dirName); FileInfo[] files; try { files = di.GetFiles(); } catch { files = null; } var dirsize = 0L; if (files != null) Parallel.ForEach(files, current => dirsize += current.Length); DirectoryInfo[] subdirs; try { subdirs = di.GetDirectories(); } catch { subdirs = null; } // each item is a "directory" having 5 attributes // name is the name of the folder // fullpath is the full path including the name of the folder // size is the size of all files in the folder (in bytes) // files is the number of files in the folder // subdirs is the count of possible sub directories in the folder var elem = new XElement("directory", new XAttribute("name", di.Name), new XAttribute("fullpath", dirName), new XAttribute("size", dirsize), new XAttribute("files", files?.Count()??0), new XAttribute("subdirs", subdirs?.Count()??0)); if (subdirs != null) Parallel.ForEach (subdirs, dinf => { var elemDir = BuildTree(dirName + "\\" + dinf.Name); elem.Add(elemDir); }); return elem; }

La méthode ci-dessus créée un document XML en mémoire en balayant toute l’arborescence d’un disque à partir de l’emplacement spécifié qui peut être autre chose que la racine. Je vous le conseille même pour vos tests ! Sur mon portable dont le disque de 3To est loin d’être plein, le fichier qui est créé en sortie fait plusieurs Go si je pars de la racine, si votre machine n’est pas équipée généreusement de RAM vous allez tout faire sauter !

Mais armé d’un tel fichier votre disque dur (ou toute autre structure de données) n’aura plus de secret pour vous ! Grâce à LINQ il va être possible d’interroger la structure sans trop se perdre dans les méandres de la pensée récursive…

On notera au passage que la méthode de création de l’arbre exploite les extensions parallèles pour traiter l’arborescence le plus efficacement possible. Vous verrez qu’à l’exécution tous les cœurs de votre machine seront à 100%.

Le vidage sur disque de la structure en mémoire peut prendre ainsi bien plus de temps que sa création, car l’écriture du fichier XML par le Save du XDocument n’est pas parallèle…et si le disque n’est pas très rapide ça peut ramer un bon moment ne vous inquiétez pas… D’où l’intérêt de choisir un sous arbre pas trop gros pour vos tests.

La taille du fichier de sortie peut être énorme car XML est verbeux mais en plus ici nous mémorisons pour chaque fichier son path complet ce qui fait une quantité de texte phénoménale.

Voici un exemple de sortie juste pour le sous répertoire NDepend (très bel outil au passage) placé à la racine de C: :

<?xml version="1.0" encoding="UTF-8" standalone="true"?> <!--Structure au 05/03/2018 20:43:36--> <directories> <directory subdirs="4" files="5" size="322725" fullpath="c:\ndepend" name="ndepend"> … (je fais court car on va retrouver ce code plus bas !) </directory> </directory> </directories>

Il n’y a pas beaucoup d’imbrications dans cet exemple mais à vous de choisir quelque chose de plus gros, pour cet article cela suffira largement.

Traiter l’arbre

Un arbre se traite avec délicatesse… Et qu’y a t il de plus doux que LINQ pour ça…

Par exemple imaginons qu’on veuille connaître la liste exacte de tous les répertoires vides…

Voici un petit bout de programme qui va exploiter la séquence de création de l’arbre pour le parcourir :

var d = DirToXml.BuildTreeDocument(@"c:\ndepend"); //d.Save(@"C:\Temp\Test.xml"); Console.WriteLine(d.ToString()); Console.WriteLine(new string('-', 60)); var q = from e in d.Descendants("directory") where (int)e.Attribute("files") == 0 && (int)e.Attribute("subdirs") == 0 orderby (string)e.Attribute("fullpath") select e.Attribute("fullpath"); Console.WriteLine("Répertoires vides"); foreach (var element in q) { Console.WriteLine(element); } Console.WriteLine(string.Format("{0} répertoires vides", q.Count()));

Sur l’exemple d’arbre plus haut la sortie sera :

<!--Structure au 05/03/2018 20:53:07--> <directories> <directory name="ndepend" fullpath="c:\ndepend" size="322725" files="5" subdirs="4"> <directory name="ENaxos" fullpath="c:\ndepend\ENaxos" size="0" files="0" subdirs="0" /> <directory name="BuildProcessResources" fullpath="c:\ndepend\BuildProcessResources" size="1442" files="11" subdirs="3"> <directory name="MSBuild" fullpath="c:\ndepend\BuildProcessResources\MSBuild" size="12166" files="2" subdirs="0" /> <directory name="NAnt" fullpath="c:\ndepend\BuildProcessResources\NAnt" size="11227" files="2" subdirs="0" /> <directory name="ReportXsl" fullpath="c:\ndepend\BuildProcessResources\ReportXsl" size="39285" files="2" subdirs="0" /> </directory> <directory name="Lib" fullpath="c:\ndepend\Lib" size="27203256" files="38" subdirs="0" /> <directory name="NDepend.PowerTools.SourceCode" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode" size="59793" files="7" subdirs="21"> <directory name="Base" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\Base" size="164" files="1" subdirs="0" /> <directory name="AnalyzeAssemblyInFolder" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\AnalyzeAssemblyInFolder" size="4745" files="1" subdirs="0" /> <directory name="APIChanges" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\APIChanges" size="10198" files="2" subdirs="0" /> <directory name="DeadCode" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\DeadCode" size="10221" files="2" subdirs="0" /> <directory name="DotNetFrameworkAnalysis" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\DotNetFrameworkAnalysis" size="2265" files="1" subdirs="0" /> <directory name="CodeQueryConsole" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\CodeQueryConsole" size="75956" files="17" subdirs="0" /> <directory name="AnalyzeCodeOnDisk" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\AnalyzeCodeOnDisk" size="3755" files="1" subdirs="0" /> <directory name="Evolution" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\Evolution" size="4062" files="1" subdirs="0" /> <directory name="AppWords" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\AppWords" size="16503" files="4" subdirs="0" /> <directory name="DetectAssemblyIssues" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\DetectAssemblyIssues" size="13340" files="3" subdirs="0" /> <directory name="bin" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\bin" size="0" files="0" subdirs="1"> <directory name="Release" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\bin\Release" size="0" files="0" subdirs="0" /> </directory> <directory name="DotNetFrameworkCoreAPIChanges" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\DotNetFrameworkCoreAPIChanges" size="4203" files="1" subdirs="0" /> <directory name="CQL2CQLinq" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\CQL2CQLinq" size="4564" files="1" subdirs="0" /> <directory name="Properties" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\Properties" size="1408" files="1" subdirs="0" /> <directory name="Trend" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\Trend" size="2366" files="1" subdirs="0" /> <directory name="SearchForDuplicateCode" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\SearchForDuplicateCode" size="29396" files="6" subdirs="0" /> <directory name="SharedUtils" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\SharedUtils" size="29549" files="5" subdirs="0" /> <directory name="ReviewMethodChanges" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\ReviewMethodChanges" size="2968" files="2" subdirs="0" /> <directory name="SearchTypesByName" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\SearchTypesByName" size="15712" files="5" subdirs="0" /> <directory name="TestDebuggerDisplay" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\TestDebuggerDisplay" size="1881" files="1" subdirs="0" /> <directory name="obj" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\obj" size="0" files="0" subdirs="1"> <directory name="Debug" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\obj\Debug" size="578489" files="5" subdirs="1"> <directory name="TempPE" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\obj\Debug\TempPE" size="0" files="0" subdirs="0" /> </directory> </directory> </directory> </directory> </directories> ------------------------------------------------------------ Répertoires vides fullpath="c:\ndepend\ENaxos" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\bin\Release" fullpath="c:\ndepend\NDepend.PowerTools.SourceCode\obj\Debug\TempPE" 3 répertoires vides

Mais pour aller au bout de la démarche parallèlisée je vous propose de réécrire la fin du code de cette façon :

var q = from e in d.Descendants("directory").AsParallel() where (int)e.Attribute("files") == 0 && (int)e.Attribute("subdirs") == 0 orderby (string)e.Attribute("fullpath") select e.Attribute("fullpath"); Console.WriteLine("Répertoires vides"); Parallel.ForEach (q, element => Console.WriteLine(element) );

Vous noterez ici l’utilisation de Parallel.ForEach déjà utilisé et de PLINQ, l’extension parallèle de LINQ qui se manifeste dans la requête par l’utilisation de AsParalel() sur la source de données…

Conclusion

Il n’y a pas de des grandes nouvelles explosives en informatique, il y a aussi les petites choses bien utiles qu’on doit faire et devant lesquelles on reste parfois sans idées… Créer un arbre, le sauvegarder en XML, le traiter par des requêtes LINQ, le tout parallélisé, ce n’est pas de la bombe, certes, mais c’est ce qui fait le bon code, celui qui va vite, qui est lisible et dont on peut raisonnablement être fier !

Stay Tuned !

Catégories: Dévelopement

Do you speak RegEx ?

Dot.Blog - dim, 04/03/2018 - 19:46

Les expressions régulières sont d’une puissance incroyables, .NET nous offre des outils simples et performants pour les utiliser, mais voilà, quand on s’en sert une fois de temps en temps c’est à chaque fois la galère d’en écrire ! Mais on peut se faire aider..

Régulières mais pas simples

Les expressions régulières semblent être ignorées par bon nombre de développeurs malgré leur puissance. Une raison à cela : c'est aux antipodes de la programmation moderne qui se veut claire et lisible...

Les expressions régulières c'est un peu comme XSLT, balèze mais incompréhensible à moins de ne faire que ça tous les jours. Le problème avec ces "langages" c'est que justement on n'en a pas besoin tous les jours ! Et ce n'est pas en pratiquant une fois de temps en temps qu'on acquiert l'habilité nécessaire. Au final, les plus courageux qui ont essayé plusieurs fois finissent par laisser tomber...

Dommage. D'autant que les expressions régulières sont parfaitement intégrées aux frameworks modernes comme .NET, et que leur bonne utilisation permet des choses quasi magiques en une ou deux lignes d'instructions : tester la conformité d'une donnée complexe et même, ce que beaucoup ignorent, découper une données en groupes ou extraire une information dans un flot (par exemple repérer automatiquement les adresses web dans tout un texte).

Mais j'ai une solution pour vous !

Il existe un petit soft qui n’est pas très récent mais qui permet de saisir des expressions régulières, de les tester, et de les analyser (pour le debug c'est parfait, mais aussi pour.. apprendre!). Ce soft est vraiment bien fait et vaut le coup d'œil dans tous les cas.

Je vous conseille donc de le télécharger et de le tester, vous pourrez insérer des expressions régulières complexes dans votre code et passer pour un héro à la machine à café, c'est pas cool ça ?

Ca s'appelle "Expresso" et ça se trouve là http://www.ultrapico.com/Expresso.htm.Conclusion

L’informatique a été conçue pour simplifier le travail de l’humain, pas pour le complexifier. Ceux qui aiment les trucs tordus à faire de tête ou à la main sont des traitres à la cause informatique ! Un bon informaticien est avant tout une faignasse, il ne se jette jamais sur son clavier avant d’avoir longuement réfléchi, et surtout il ne fait pas de tête ou à la main ce qu’un logiciel peut faire à sa place ! Les expressions régulières aussi puissantes soient-elles sont une torture créant une friction cognitive forte. Mais on peut se faire aider d’un logiciel. Et de fait il serait bête de s’en passer…

Stay Tuned !

Catégories: Dévelopement
S'abonner à Sleepy SME agrégateur - Dévelopement