• Extrait de code pour traitement asynchrone en WPF

    Si vous faites un peu de programmation WPF, vous avez certainement dû vous retrouver assez souvent à écrire du code ressemblant à :

    ThreadPool.QueueUserWorkItem
        (new WaitCallback(delegate(object ignored)
        {
            // un appel à un traitement long comme une requête 
            // sql ou un web-service
    
            Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new WaitCallback(delegate(object ignored_too)
                {
                    // le traitement de mise à jour de votre
                    // controle 
                }
                ), null);
        }
        ));
    

    Je vous propose donc un petit extrait de code qui se charge d'écrire cela à votre place (le bonus étant que les namespaces sont importés automatiquement) :

    Une fois installé, vous pourrez utiliser le snippet wpfthreadtask :

    image

    Mots clés Technorati : ,,,,

  • Retour d'expérience sur WPF

    Cela fait maintenant un an que je réalise (principalement) du développement WPF, d'abord pour 2 ou 3 projets "direct-poubelle" - vous savez ces mini-projets que l'on prévoit de ne jamais arriver à finir et qui ne sont là que pour faire toutes les gaffes possibles et imaginables - puis pour deux applications LOB. Bien qu'ils ne soient pas finis, ces deux derniers projets ont aboutis à la création d'un certains nombres de règles d'or que nous utilisons (et utiliseront) pour améliorer le développement...

    N'ayez pas peur du découpage design/développement

    C'est probablement l'un des points les plus difficiles à mettre en place - et un sujet de complainte fréquent sur les blogs parlant de WPF - mais une fois un certain nombres d'idées prises en compte et appliquées, cela va beaucoup mieux... La règle à retenir pour ce point est : "ne faites pas faire au développeur le travail du designer". Dans les règles suivantes, j'utiliserai le terme "fonctionnel" pour désigner les données et la façon de les manipuler, et le terme "présentation" pour tout ce qui est de l'ordre du rendu visuel. Si vous utilisez un modèle de séparation "règles métier" / "affichage", sachez donc que la partie que je nomme "fonctionnelle" correspond au code utilisé dans la "vue" pour appeler les objets métiers et traiter leurs réponses.

    • Le développeur doit exposer des propriétés pour toutes les données qu'il souhaite voir afficher. Ce n'est pas à lui de décider de où, ni de comment seront affichées les informations d'un écran.
    • Les animations et autres effets graphiques sont du ressort du designer. Oui, même celles que l'on ne peut pas facilement exprimer en XAML ! Il n'est pas logique que le développeur "fonctionnel" se charge de déclencher des animations ou de manipuler directement des objets (panels, listview...) de la partie "présentation". Après tout, lorsqu'un designer fait une animation Flash, il écrit du code ActionScript pour de très nombreuses choses. Le principe est ici le même : il devra probablement mettre les mains dans le cambouis. Bon, bien sûr, ça c'est le concept, mais une bonne solution pour éviter que le code soit saccagé - euh, oui je suis développeur dans l'âme, pourquoi ? - est :
      1. Si votre équipe est suffisamment grande, d'affecter un développeur à l'équipe design, ou dans le cas d'une petite équipe de faire travailler le développeur au code de présentation après qu'il ait fini le code fontionnel. Ce développeur-designer sera chargé d'écrire tout le code nécessaire aux animations et autres effets graphiques.
      2. de séparer dans des fichiers sources différents (à l'aide de partial class) la partie fonctionnelle de la partie présentation.
    • Le développement d'une fenêtre ou d'une page, demandera probablement plusieurs aller-retour. C'est l'une des choses qu'il ne faut clairement pas oublier : il n'y a que très peu de chance que le designer ne soit pas bloqué par un oubli du développeur et/ou que le développeur ne soit pas amené à ajouter du code (évenements, nouvelles propriétés) à cause du designer.
    • Vos développeurs vont devoir oublier une bonne partie de ce qu'ils savent sur l'écriture d'une application.Dans chaque développeur Windows Forms se cache un bidouilleur-né : nous avons été habituées depuis longtemps à nous servir des contrôles directement (qui ne s'est jamais servi de la propriété Tag d'un ListViewItem comme seul et unique conteneur pour ses données métier ?) et donc à nous appuyer sur eux, mais ce genre de comportement ne doit plus être de mise avec WPF.
    Prenez le temps de développer un framework applicatif

    L'un des points que l'on omet aussi souvent lorsque l'on parle de WPF c'est de se souvenir qu'il s'agit d'un framework de présentation et en tant que tel, qu'il n'est pas suffisant dans le développement d'une application complexe. Qu'il s'agisse de répercussions de ce fait ou des oublis dans le framework, voici deux exemples de fonctionnalités à développer.

    • Le modèle de gestion des threads choisi pour le framework est... comment dire ? .... extrêmement pénible ! Pour éviter de nombreux soucis de concurrence, les données utilisées sur celui d'affichage ne sont pas accessibles sur les autres. Il vous faudra donc, pour la santé mentale de vos développeurs, faire développer des classes simplifiant la discussion inter-thread( par exemple pour fusionner le résultat d'un appel asynchrone à un web-service avec une ObservableCollection exposée par votre contrôle)
    • Il n'est pas possible d'écrire du code ni même des expressions dans le fichier XAML (il n'y a pas d'équivalent XAML à "IsEnable= (madonne==null)" par exemple), il vous faudra donc créer des classes de conversion de données ou d'extension du markup pour pallier à ce manque.
    Acceptez les limites de WPF

    Avec .net 3.0 (et 3.5), nous avons le droit à une version 1.0 de WPF, ce qui engendre un certain nombre de soucis et de limitations. Pour un développement réussi, il vous faudra donc les prendre en compte.

    • La performance de WPF est loin d'être exceptionnelle... Exit donc les délire d'animations qui demanderont un QuadCore minimum pour fonctionner correctement. Si dans l'ensemble la performance n'est pas mauvaise pour un système vectoriel, certaines fonctionnalités de WPF ne sont clairement utilisable qu'avec parcimonie : les bitmaps effects par exemple sont à réserver à de très petites surfaces
    • XAML n'est pas suffisant ! Il est vain d'essayer de faire croire que XAML est une solution parfaite pour les designers. En effet, et malgré une étendue déjà plus qu'impressionnante, certaines choses ne sont pas réalisables en XAML ou ne le sont qu'avec un niveau de complexité bien plus grand qu'en C# (par exemple). N'hesitez donc pas à écrire du code lorsque cela est nécessaire et/ou plus simple !
    • Le compilateur XAML a encore du chemin à parcourir. C'est peut-être le point le plus gênant lors de l'utilisation de WPF : il existe de nombreuses erreurs dues à des facteurs techniques (mauvais type d'objet, paramètres incorrects, etc...) qui ne sont pas détectées par le compilateur (alors qu'un compilateur comme celui de C# est capable de les repérer) et qui se traduisent donc par des exceptions plus ou moins compréhensibles (plutôt moins d'ailleurs...) lors de l'exécution. Il n'y a malheureusement pas de palliatif à ce problème mis à part un : prenez votre mal en patience.
    Mots clés Technorati : ,,

  • 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 !

  • 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 >