• de.collage par Thirteen23

    C’est assez ancien, mais puisque Benoît fait dans le screen saver, moi aussi ! :)

    13-23-decollage

    Ce que vous voyez ci-dessus est la version “application” de decollage, mais cela s’installe très facilement en économiseur d’écran.

    Pour la petite histoire, il s’agit de l’une des premières applications WPF que j’ai pu voir, elle n’est peut être pas bluffante, mais quand on sait que WPF n’est pas plus compliqué à programmer que WinForms (enfin… presque…), cela m’avait impressionné.

    Ca se télécharge par ici, mais c’est surtout leur blog qui est intéressant.

  • The infamous case of IQueryable.First()

    Préambule : Ceci n’est absolument pas un billet destiné à râler sur un bug ou sur un défaut de conception honteux dans LINQ / C# 3.0 : le comportement décrit ci-dessous est une erreur de programmation de notre coté (voir Préambule N°2) et non quelque chose d’induit par LINQ/IQueryable. Cela nous apprendra à ne pas lire la documentation d’une méthode et à ne pas faire attention à la méthode juste en dessous dans la boite de dialogue Intellisense.

    Préambule N°2: Nous connaissons IQueryable.FirstOrDefault() qui constitue la réponse aux mots maux (raaaah des fois c’est bien de se relire, ca évite de passer pour un c…ne sachant même pas faire la différence entre “mots” et “maux”) décrits ci dessous.

    Cette fin de matinée, un samedi qui plus est, a été source de grande joie pour moi : je viens de passer un peu plus de deux heures sur un bug d’une incroyable stupidité…

    L’un des nos clients, en ces derniers jours de préparation de colis pour Noël, a eu besoin de l’écriture en urgence d’un outil destiné à remplacer des terminaux défectueux. Aussi tôt dit, presque (il fallait quand arriver à trouver le temps de programmer…) aussitôt fait : nous avons réalisé une mini-application à partir de WinForms et LINQ. Celle-ci, bien qu’elle n’est pas près de figurer dans la catégorie des applications les mieux conçues, remplissait son métier… enfin… pendant quelques heures : ce matin, catastrophe, il y a beaucoup de messages d’erreurs et cela ralenti considérablement une logistique déjà sous pression.

    Les symptômes sont assez curieux : lors du traitement, les gens obtiennent souvent une erreur correspondant à des règles de gestion internes alors qu’ils sont sûr que celles-ci sont bien respectées. Chose encore plus étrange, parfois un simple nouvel essai permet de corriger le problème, d’autre fois il suffit d’attendre un peu et de re-essayer et cela fonctionne. Après examen du code, nous réduisons le code “fautif” à ces quelques lignes :

    var produits = from ... in linqContext. ...
          where .....
         select ....;
     
    try
    {
       var produit = produits.First();
       if(produit == null)
       {
           //... affiche l'erreur ...
       }
       else
       {
          // ...fait le traitement nécessaire...
       }
    }
    catch // plusieurs blocs catch pour traiter les différents cas
    {
        //... affiche l'erreur ...
    }

     

    Code qui me semble parfaitement valide, j’entreprends donc de monter Visual studio sur l’environnement de production et de commencer à tester en production. Après quelques ratés, nous trouvons des cas où, effectivement, l’erreur apparaît sans raison. Celle ci est affichée par une InvalidOperationException lors de l’appel à IQueryable.First() et dont le message est “le résultat ne contient aucune ligne” (ou quelque chose du genre, je n’ai plus le message en tête). Et la, donc, drame : IQueryable.First() renvoie sous forme d’erreur quelque chose qui n’est, pour moi, pas de l’ordre de l’exception mais bien d’un traitement “normal”.

    Hormis le fait que la documentation MSDN ne liste pas cette exception dans celles possibles pour cette méthode (pour la première fois de ma vie, l’obligation de déclaration des exceptions propre à java m’a semblé utile… comparé aux milliers de fois où il m’a énervé lorsque j’en faisait ^^), le retour sous forme d’exception m’a quelque peu choqué. Combien de fois, en effet, avez vous écrit en (T-) SQL : select top 1 … from …. ? Dans ce genre de cas, car c’est bien cela que semble exprimer IQueryable.First(), on peux raisonnablement s’attendre à ce que le résultat “il n’y a pas de première ligne”, ne soit pas de type “exceptionnel”.

    Tout cela pour dire que, si vous mettez à disposition d’une équipe de développeurs un composant, il est primordial de bien comprendre et de bien définir le contrat que votre composant propose, et en particulier d’évaluer le plus possible ce qui tient des postulats “implicites” de vos fonctions (dans le cas présent, le postulat étant qu’un IQueryable ne peut pas contenir 0 éléments) et de les documenter au fur et à mesure que ceux-ci apparaissent (certain n’apparaîtront en effet que longtemps après l’implémentation, lorsqu’un développeur aura la mauvaise idée de ne pas le(s) respecter).

    Comme je le disais en préambule, l’équipe chargée de cette partie de LINQ/C# 3.0 a parfaitement géré le problème : il existe deux méthodes : First() et FirstOrDefault() (conservant même la notion de …OrDefault propre aux objets “nullables”) qui permettent de choisir le type de comportement que l’on souhaite.

    Pour la petite histoire, l’erreur ne provenait absolument pas de cette erreur. Nous n’affichions pas le message d’erreur exact, peut-être, mais le message d’erreur était proche de ce qu’il devait être. Le fautif a fini par être trouvé : les outils de saisie, parfois, transmettaient des informations incohérentes (quand je vous disais que le bug était d’un incroyable stupidité…).

    Post-Scriptum : Ce billet ne doit son existence qu’à “de saines lectures”, en l’occurrence Eric Lippert. L’un de ses billets, lu par hasard il y a quelques jours, a très fortement résonné dans mon esprit ce matin pendant cette phase de “debug en production” et, sans celui-ci, j’aurais probablement haussé les épaules et continué. Bien que le sujet ne soit pas exactement le même, il y parle du même type de problématique : l’implémentation d’une méthode, ainsi que les postulats implicites de l’équipe développant un composant, font partie intégrante du contrat.

  • Applications WPF

    Il y a quelques temps, j'ai évoqué des projets WPF/Silverlight sur lesquels je travaillait... En voici quelques screenshots, histoire que vous puissiez avoir une idée du rendu :

    • d'abord un outil de visualisation de statistiques, récupérant ses données soit depuis des rapports sous SqlServer Reporting Service soit - et c'est le but principal recherché - depuis les vues statistiques de la gestion commerciale d'un client/partenaire.

    Concept
    Le concept-art réalisé sous CorelDraw - parce que je suis un fan de ce produit - avec le tag "beta" histoire de faire un peu Desktop 2.0 :)...

    Affichage demonstration
    ...et sa réalisation ou plutôt l'état actuel de sa réalisation puisqu'il reste encore quelques modifications à apporter.

    • Je travaille aussi actuellement à la ré-écriture de certains modules de la Gestion Commerciale précédemment évoquée (les deux screenshots sont réalisés sur la même partie de l'application, le suivi des clients) :

    Suivi Client 
    Accroch'info ouvert

    L'un des grands challenge lorsque l'on écrit ce type d'application est d'arriver à innover - et à rendre une application visuellement plus agréable - sans pour autant changer les habitudes de travail ni même ralentir celui-ci. Vous remarquerez probablement dans ces screenshots un petit emprunt à Zune 2.0 (le système de tri de la liste), mais il est plus que probable que celui-ci soit remplacé par les beaucoup-plus-classiques entêtes de colonnes pour la version finale.

    • Je ne reviens pas vraiment dessus, puisque j'en ai déjà parlé, mais, pour le plaisir (en tout cas pour le mien), voici un petit screenshots de l'application kiosque réalisée en silverlight pour un client :

    ecomusee

    PS : oui, je sais, ca fait beaucoup de orange & noir, mais ce ne sont que des coïncidences : ce n'est pas moi qui ai choisi les thèmes de couleurs...

    Mots clés Technorati : ,

  • Créer un conteneur WPF « draggable » (part 2)

    Où l'on commence l'implémentation du contrôle

    Pour commencer l'implémentation de ce contrôle, nous allons nous concentrer sur la détection du « gesture » : c'est assez simple, il suffit que l'utilisateur clique sur le contrôle et bouge d'au moins quelques pixels en maintenant le bouton gauche de la souris pour démarrer le drag/drop.

    Commençons par créer notre classe :

    using System;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows;
    
    namespace Carbenay.Michael.Controls
    {
        public class DraggableBorder : Border
        {
        }
    }

    Il faut ensuite détecter le mouvement de déclenchement :

    • Lorsque l'on clique sur l'élément (événement MouseDown) nous allons conserver la position de la souris
    • Lorsque l'on bouge la souris sur l'élément (évenement MouseMove) après ce clic, il faudra comparer la position de la souris actuelle par rapport a celle au moment de l'appui et si celle-ci est suffisamment différents – ce « suffisamment » étant défini par un paramètre de Windows – déclencher le drag/drop
    • Lorsque le bouton de la souris est relâché au dessus de l'élément (événement MouseUp), le drag/drop est annulé

    Pour être sûr de traiter les événements le plus tôt possible, nous n'allons pas vraiment nous connecter aux événements MouseDown, MouseMove, MouseUp mais à leur version « Preview » (pour plus de renseignement sur les événements et leur versions Preview, je vous conseille Application = Code + Markup de Charles Petzold) :

     

        public DraggableBorder()
        {
            PreviewMouseDown += new MouseButtonEventHandler(MyPreviewMouseDown);
            PreviewMouseMove += new MouseEventHandler(MyPreviewMouseMove);
            PreviewMouseUp += new MouseButtonEventHandler(MyPreviewMouseUp);
        }
        
        // position de la souris sur le MouseDown
        private Point _mouseDownPoint = new Point();
        // a true si le MouseDown a bien été détecté 
        // dans le controle
        private bool _wasMouseDownInControl = false;
    
        void MyPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            // on "annule" le drag drop
            _wasMouseDownInControl = false;
        }
    
    
        void MyPreviewMouseMove(object sender, MouseEventArgs e)
        {
            // si le bouton gauche de la souris est toujours 
            // appuyé on regarde si la position actuelle
            // est suffisament eloignée de la position de
            // départ et si oui, on lance le drag/drop
            if (e.LeftButton == MouseButtonState.Pressed 
                && _wasMouseDownInControl)
            {
                Point pos = e.GetPosition(null);
    
                if (SystemParameters.MinimumHorizontalDragDistance <=
    Math.Abs(position.X - _mouseDownPoint.X) || SystemParameters.MinimumVerticalDragDistance <=
    Math.Abs(position.Y - _mouseDownPoint.Y)) { BeginDrag(); _wasMouseDownInControl = false; e.Handled = true; return; } } } void MyPreviewMouseDown(object sender, MouseButtonEventArgs e) { // on enregistre la position de la souris // pour détecter le mouvement de drag/drop _mouseDownPoint = e.GetPosition(null); _wasMouseDownInControl = true; }

    Reste la méthode BeginDrag dont le rôle est de préparer les données et de déclencher le drag/drop. Pour cette première version nous allons faire très simple et créer un objet de donnée avec une ligne de texte simple :

        private void BeginDrag()
        {
            DataObject data = new DataObject();
            data.SetData(DataFormats.Text, "Test");
            DragDropEffects de = DragDrop.DoDragDrop(this, 
                                    data, 
                                    DragDropEffects.Copy);
        }
    Au programme du prochain numéro : qu'est-ce que le DataObject et qu'est-il possible d'y mettre ?

  • Créer un conteneur WPF « draggable » (part 1)

    Où l'on choisit le type de contrôle

    Première question intéressante : quel type de contrôle allons-nous bien pouvoir réaliser ?

    Si vous avez déjà un peu fait de développement de contrôles en .net (que ce soit pour asp.net, WinForms ou WPF), vous savez que, chez Microsoft (et dans la plupart des autres solutions graphiques aussi…), l'on vous propose toujours 2 types d'extensibilités sur les contrôles.

    Les user-controls

    Que nous ne traduiront pas par « contrôle utilisateur » mais par « contrôle composite » du fait de leur utilisation principale : permettre de créer des « groupes » de contrôles réutilisables comme par exemple un contrôle de saisie d'adresse. Construire ce genre de contrôle est extrêmement facile : les fenêtres ou pages que vous avez déjà créées avec WPF/ASP.net/WinForms n'étaient, en concept, que des User Controls un peu particulier.

    Ce type de contrôle est très utile aussi bien pour ne pas ré-écrire 50 fois le même groupe que pour fragmenter votre interface en quelque chose de plus facile à gérer. Prenons un exemple que vous avez peut-être déjà été amené à rencontrer : lorsque l'on crée une boite de dialogue pour définir les options d'un logiciel, celle-ci à toujours une tendance à intégrer de plus en plus d'onglets au fur et à mesure des différentes versions et par faire 5 000 ou 10 000 lignes de codes pour gérer tous les boutons et autres options avancées présents sur les pages. En scindant chaque page en un UserControl différent, le code devient beaucoup plus lisible et maintenable.

     OptionDialog218

     

    Les custom controls

    L'autre solution d'extension proposée est de dériver votre contrôle de l'un de ceux existants. Le but est dans ce cas très différent : il s'agit d'apporter de nouvelles fonctionnalités à un contrôle, voire de créer un contrôle complètement nouveau en se basant sur l'un de ceux préexistants. Besoin d'une checkbox qui disparait lorsque l'on met la souris dessus ? (je n'ai jamais dit que les fonctionnalités devaient être intelligentes...) Créez une nouvelle classe, dérivez la de System.Windows.Controls.Checkbox et ajoutez ce qu'il faut dans les gestionnaires d'événements de la souris et le tour est joué !

    Et le vainqueur est :

    Dans notre cas, la solution était toute trouvée : nous ne souhaitions pas créer un contrôle composite mais bien étendre les fonctionnalités de l'un des nombreux conteneurs que propose WPF. La seconde partie de la question – à savoir quel contrôle nous allions étendre – a été très vite traitée : nous utilisons dans l'application des « Border » dans tous nos templates de données, un simple rechercher/remplacer dans nos différentes feuilles de styles différents dictionnaires de ressources nous permettra d'ajouter la fonctionnalité sans trop de manipulations.

  • Créer un conteneur WPF « draggable » (part 0)

    Il y a quelques semaines, pour le projet d'un partenaire, j'ai été amené à créer un contrôle réutilisable permettant le drag de son contenu. Ce n'est pas bien compliqué, et je vais donc vous en faire part.

    Le problème posé était assez simple : permettre à l'utilisateur de faire du drag-drop d'éléments pour recopier des informations (par exemple, permettre de glisser le dossier d'un client vers une zone « à synchroniser » pour l'avoir en mode déconnecté du serveur). Comme les développeurs n'avaient pas vraiment envie de repasser sur tous les écrans et d'y inclure la notion de drag/drop, la solution d'un contrôle personnalisé autorisant le drag a bien vite été choisie.

    Les posts correspondants :

    Ca c'est pour plus tard :

    • part 5 : Dans laquelle l'aspect graphique de la chose est renforcé
    • part 6 : Où l'on se rend compte que l'on est pas tout seul

  • Quel type d'applications développe-t-on avec WPF et Silverlight ?

    Il y a de cela quelques jours, je suis tombé sur un billet - de Thomas Lebrun - appelé [WPF / Silverlight] Quel type d'application développe-t-on avec WPF et Silverlight ?.

    WPF est une plateforme très complète, fournissant grâce à toutes les classes de .net - et à l'interop si nécessaire -, une solution robuste pour construire des applications complexes. Traditionnellement, les applications "clients lourds/clients intélligents" faisait pale figure face à la qualité visuelle de la moindre application web, mais cela est maintenant terminé.

    Silverlight, surtout parce que très jeune (et assez limité, il faut bien le dire dans cette version 1.0), va se trouver pendant lontemps utilisé à simplement "rendre un peu plus interactif" un site web. Comme pour Flash, il est peu probable de voir arriver avant quelques années des applications vraiment intelligentes réalisée avec cette techno : les limitations du modèle "web-embedded" étant bien trop nombreuses (Adobe l'a d'ailleurs bien compris en déclinant sa plateforme avec AIR).

    Voila, ca c'était les généralités... Je suis très "client intelligent", donc - surtout depuis que WPF est sorti - lorsque je doit choisir une technologie c'est rarement le web qui gagne, mais voici les deux projets (enfin, les deux qui entre dans le cadre de la question WPF ou asp.net+Silverlight ?) sur lesquels je travaille en ce moment ou que je viens de terminer :

    • un afficheur de rapports/statistiques : en WPF, le choix ayant été assez compliqué... Il s'agissait au début de fournir des aspects graphiques un peu plus sympa aux Reporting Services de SQL Server 2005, et puis, au fur et à mesure de la réflexion de nombreuses fonctionnalités supplémentaires ont été imaginées, dont la plupart demande l'interaction avec le système de fichier de l'utilisateur - par exemple la possibilité d'envoyer un graphique dans un slide de présentation PowerPoint.
    • une visite virtuelle et un complément d'exposition pour un musée. Le choix a été assez simple (après les 15 premieres secondes pendant lesquelles je me suis dit : "chouette une appli WPF à faire !") puisque l'application est plus destinée à être fournie aux visiteurs avec le catalogue de l'exposition qu'à être utilisé sur place. Dans ce cadre, les 50Mo et le quart d'heure d'installation de .net 3.0 sont inenvisageables donc : Silverlight

  • Ah bah ca va tout de même être plus simple !

    Je n'ai jamais fait un mystère de mon amour pour CorelDraw pour tout ce qui touche au design et je viens enfin de trouver un solution pour exporter des éléments sans passer par toute un série d'outils.

    N'ayant effectivement pas encore trouvé de plug-in d'exportation ou de convertisseur direct entre Corel et XAML, je suis obligé pour l'instant de passer par un fichier temporaire (soit en format Illustrator, soit en SVG) puis d'utiliser un convertisseur. Autant dire qu'entre les fonctionnalités non supportées de l'export de Corel (tout ce qui est "fonds degradés" supporte très mal l'export par exemple) et celles non supportées par le convertisseur XAML, le resultat était souvent très approximatif.

    Bien qu'encore loin d'être l'extase, Paste2Xaml permet d'eviter l'une des deux étapes en autorisant le Copier/Coller depuis CorelDraw (malheureusement au format WMF, ce qui ne retire pas tous les problème, loin s'en faut). Il ne reste plus qu'à cliquer sur Enregistrer et c'est dans la boîte !

  • Tiens, un générateur de code qui sait parler anglais...

    Bon, c'est pas hyper passionnant, mais je viens de voir une petite chose amusante dans LINQ. J'ai deux tables Categories et MetaCategories dans mon schéma de base de données (bon, je vous fait pas l'affront de vous dire qu'il y a une relation parent-enfant entre les deux tables... ah bah si tiens, je l'ai dit...) et LINQ To SQL m'a automatiquement crée les liens entre les tables dans les classes qu'il a créé.

    Jusque là, rien de bien exceptionnel me direz vous... Oui, mais ce qui m'a amusé c'est que les objets de données ont été appelés "Category" et "MetaCategory", avec un "y" : LINQ a detecté une forme plurielle et a fait de lui même la conversion... Comme il est sympa !

    Pour naviguer dans mes tables je peut donc par exemple :

    • faire un from ... MetaCategories ... select ...
    • ce select me renvoi un MetaCategory[] (enfin pour les puristes, pas tout à fait, mais on peut simplifier en disant cela)
    • sur un objet MetaCategory, la propriété Categories permet d'obtenir obtenir ses enfants

    J'espère que MS à prévu l'internationalisation de ce truc, ca serait vraiment bien !

  • Même les erreurs peuvent "avoir de la gueule"

    [Ooops, en écrivant un autre article, je me suis rendu compte que ce post était resté dans mon Live Writer et n'avait pas été publié]

    Je suis de plus en plus amoureux de WPF... avec un tout petit peu de temps et quelques connaissances en design (je suis pas encore merveilleux dans ce domaine, je l'avoue, mais je m'améliore), on peut facilement transformer des applications ternes en quelques choses d'un peu plus sympatique.

    Je viens par exemple de réaliser un outil, pour un client/partenaire, permettant de visualiser les erreurs survenues lors des processus batchs d'une gestion commerciale. Comme toute l'application est en train de migrer vers WPF, c'était l'occasion de faire un peu de présentation. Voila le résultat :

    MemeLesErreurs

    C'est plutôt joli, pour une application de suivi d'erreurs, non ?

  Next >