jeudi 20 novembre 2014

Comment créer un contrôle graphique WPF avec Blend.

Mon incompétence en terme d’IHM étant assez importante, j’ai suivi récemment une formation sur les outils de modélisation WPF, et je dois dire que cela m’a assez bien réconcilié avec le monde de l’interface graphique.

Cela a surtout démystifié pas mal d'aprioris que j’avais sur le sujet, et il est vrai que Microsoft a fait de très gros efforts pour permettre à tout un chacun de créer de jolies interfaces, même pour les amoureux des programmes en mode console comme moi.

Avec cet article, je vais tenter de vous montrer comment on peut facilement faire un contrôle graphique en partant d’une page blanche, et ce sans aucune compétence particulière en graphisme.

Introduction

Pour commencer, voici l’environnement de base que nous allons utiliser :
  • Visual Studio 2012
  • Microsoft .NET 4.0
  • Langage C#
  • Blend for Visual studio (normalement installé en même temps que VS 2012)
Le composant graphique que nous allons réaliser sera une horloge à aiguille avec les spécificités suivantes :
  • Les aiguilles se placent aux bons endroits en fonction d’un TimeSpan passé en propriété
  • Un bouton permet de changer le cadran de l’horloge via un panel de choix qui s’ouvrira avec une petite animation.
Voici à quoi cela ressemblera lorsque ce sera terminé :



Ceux d’entre vous qui font du Winform et qui ne connaissent pas WPF peuvent trouver ça irréalisable, surtout en partant de zéro !

Ne vous inquiétez pas, une fois que vous aurez lu cet article, vous ne voudrez plus faire vos IHM autrement qu’avec du WPF tant la manipulation graphique est simple, et donne en peu de temps des résultats bluffants !

Au travail

Nous allons commencer par ouvrir Visual studio et créer un nouveau projet en .NET 4.0 de type « WPF User Control Library » que nous allons appeler « MaLibrairie »


Une fois le projet créé, vous arrivez dans Visual studio à quelque chose qui doit ressembler à ceci :



Cliquez sur « UserControl1.xaml » et renommez le fichier en « MonHorloge.xaml »

Si Visual ne vous a pas fait de refactor automatique, n’oubliez pas de renommer aussi le « UserControl1 » dans « MonHorloge.xaml » ainsi que dans « MonHorloge.xaml.cs »





Voilà, une fois ces détails réalisés et enregistrés, nous allons pouvoir commencer le « design » de notre contrôle d’horloge.

Pour cela, ouvrez l’application « Blend » qui se trouve dans Démarrer à Tous les programmes à Microsoft Visual Studio 2012 à Blend for Visual Studio 2012



Dans la fenêtre d’accueil, cliquez sur « Open Project »


Allez chercher votre solution puis cliquez sur « Ouvrir »



Vous allez vous retrouver dans un logiciel qui ressemble plus à photoshop qu’a un IDE.
Bienvenue dans la partie visible de l’IceBerg  :)

Vue d’ensemble de Blend

Ok, faisons un peu le tour du propriétaire :
Pour que nous soyons dans la même configuration, je vous conseille de cliquer dans le menu « Window » à « Workspace » à « Animation »

A votre droite vous avez 4 onglets : « Properties, Projets, Ressources et Data ». Les onglets qui vont le plus nous intéresser sont les deux premiers. L’onglet « properties » va permettre de manipuler les propriétés graphiques des éléments que nous allons ajouter dans notre composant, et l’onglet « Project » est l’équivalent de l’explorateur de solution sous visual studio


Au centre, vous serez face au designer de votre contrôle graphique. Celui-ci est vide et vous regarde d’un air menaçant, mais vous verrez qu’il est très sympa pour peu qu’on apprenne à le connaitre.

C’est ici que nous allons ajouter, manipuler et agencer  des formes et des images pour que notre contrôle ressemble à quelque chose.



En bas, plusieurs onglets avec des noms obscures tels que « Assets, Triggers, States, Parts et Objects and Timeline » sont présents.

Tous ces outils permettent de créer des animations. Nous verrons comment les utiliser lorsque nous traiterons de la palette de configuration de notre horloge.


Sur la gauche, vous avez une palette d’outils un peu comme ce qui vous est proposée dans visual studio pour le design.


Nous utiliserons cette palette pour ajouter des composants à notre layout de design au centre.

Commencement

Alors, par quoi commencer pour faire notre horloge ?
Dans un premier temps, nous allons mettre en place le cadran. Pour cela rien de plus simple, allez sur « google image » puis faites quelques recherches pour trouver votre bonheur.


Choisissons un cadran avec une taille qui soit la même aussi bien en largeur qu’en hauteur. Celui en gris me parait très bien.

Tout d’abord, téléchargez l’image sur votre disque


Une fois l’image téléchargée, allez dans l’onglet « Projects » de blend, puis créez un dossier appelé « Cadrans » à votre projet.




Renommez l’image que vous avez téléchargée en « cadran1.jpg » puis glissez-la de votre disque au dossier que vous venez de créer dans blend.


Allez dans la barre d’outils située à gauche, puis cliquez sur le bouton « Assets » représenté par deux petites flèches sur la droite, puis tapez « Image » dans le panel de recherche qui apparait.



Faite glisser le composant Image dans le designer situé au centre de l’application.


Pour que le composant image prenne toute la place, cliquez sur l’onglet « Properties » situé à droite.
Cliquez ensuite sur le carré blanc des propriétés « Width », « height » et « Margin » puis sélectionnez « Reset »

Sélectionnez aussi l’option « Stretch » pour « HorizontalAlignement » et « VerticalAlignement »



Nous allons maintenant assigner notre image de cadran au composant, pour ce faire, allez dans la propriété « Source » puis sélectionnez « Cadran1.jpg »



Voila ! Notre cadran d’horloge est opérationnel !

Passons maintenant à la deuxième partie, celle de la disposition des aiguilles.
Pour faire simple, nous allons utiliser des rectangles que nous allons paramétrer pour être en mesure de tourner autour du point central. Let’s do it :

Cliquez une nouvelle fois sur le bouton « Assets » de votre barre d’outils puis tapez le mot « Rectangle » dans le champ de recherche.


Prenez le composant « Rectangle » et glissez-le dans la zone de dessin.


Ajustez-le à l’aide de votre souris pour que celui-ci soit disposé comme une aiguille sur le cadran pointant vers midi.


Dans l’onglet « properties », assignez une couleur rouge à ce rectangle





Maintenant, voici la partie magique. Nous allons paramétrer le rectangle pour que celui-ci puisse tourner autour de l’axe central du cadran.

Pour que vous vous rendiez bien compte du délire, ouvrez la partie « Transform » dans l’onglet des propriétés de votre rectangle, puis choisissez le sous onglet « Rotate » représenté par une flèche qui fait un demi-cercle sur la gauche.


Dans cet onglet, vous allez trouver une propriété « angle ».

Si vous vous amusez à faire varier sa valeur, vous remarquerez que le rectangle tourne sur lui-même, l’axe de rotation étant en son centre.






Notre but est donc de changer le point de rotation pour que notre rectangle ne tourne plus en fonction de son centre, mais en fonction de sa base.

Remettez la propriété « Angle » à 0, puis cliquez sur l’onglet « Center Point »



Les propriétés X et Y représentent les coordonnées du point de rotation. Celles-ci sont sous forme de ratio (valeur décimale entre 0 et 1) le point 0,0 étant en haut à gauche de la forme et le point 1,1 en bas à droite.


Ainsi, si je veux que mon point de rotation se fasse au centre de la base de mon rectangle, X doit avoir la valeur 0.5 et Y la valeur 1.



Pour le vérifier, remettez-vous sur l’onglet « Rotate » et jouez une nouvelle fois avec la propriété « Angle ». Normalement vous verrez votre rectangle tourner autour du centre du cadran comme une aiguille.



Cool n’est-ce pas ?

En considérant que nous venons de faire l’aiguille des minutes, faite de même pour l’aiguille des heures et des secondes. Nous dirons que l’aiguille des heures sera de couleur verte, et l’aiguille des secondes de couleur bleu.

Allez-y, je vous attends…

Voilà, au bout de quelques minutes, vous devriez avoir ce résultat (en partant du principe que tous vos angles ont des valeurs différentes :



Maintenant que toutes nos aiguilles sont placées sur notre cadran, et qu’elles sont capables de tourner correctement, nous allons créer une propriété à notre contrôle afin de placer les aiguilles par code en fonction d’un TimeSpan.

Pour ce faire, nous allons devoir jouer sur les propriétés « Angle » de chacune de nos aiguilles, il est donc important de les nommer pour pouvoir les manipuler plus tard.

Sélectionnez par exemple l’aiguille rouge, puis retournez dans l’onglet « Rotate » de la fenêtre des propriétés.


Cliquez sur le petit carré blanc situé à droite de « Angle », et choisissez le menu « Go to source »


Le code Xaml lié à notre rectangle s’affiche dans Blend avec la propriété « Angle » sélectionnée.


Pour manipuler celle-ci depuis l’extérieur il existe plusieurs possibilités.

L’une d’entre elle consiste à faire du « data binding », c’est-à-dire de la liaison de données. C’est un mécanisme très puissant qui permet de découpler les données de l’IHM afin de pouvoir faire du code testable.

Dans notre cas, le code que nous voulons ajouter n’est pas du code métier qu’il est nécessaire de tester, mais purement et simplement du code de manipulation graphique, c’est pourquoi nous opterons pour du code « Behind », c’est-à-dire embarqué à notre contrôle.

Pour faciliter la manipulation de la propriété « Angle » par le code behind, nous allons la nommer afin de pouvoir y accéder plus facilement.

Rajoutez dans le code xaml un attribut nommé « x :name » auquel nous donnerons le nom « AngleMinutes »


Nous ferons exactement la même chose avec « AngleSecondes » et « AngleHeures »




Nous allons maintenant passer à la partie programmation, celle que vous préférez.

Pour cela, retournons dans Visual Studio que nous avons laissé ouvert en arrière-plan. (N’oubliez pas au préalable de bien tout sauvegarder dans blend)

Normalement, une boite de dialogue d’avertissement doit apparaître dans VS.



Cliquez sur le bouton « Reload All »

Le designer de Visual Studio vous montre le contrôle que nous venons de créer dans « Blend », il s’agit maintenant de créer la propriété qui va bien pour que notre horloge soit contrôlable depuis l’extérieur.

Pour cela, WPF nous impose de créer une « DependencyProperty ».

Ouvrez « MonHorloge.xaml.cs » puis tapez « propdp » + tabulation.

Un snippet de visual studio vous génère un bout de code un peu obscure tel que :



Première étape : on modifie le code pour proposer un TimeSpan du nom « Time »



Deuxième étape : lorsque le programmeur assignera cette propriété, nous voulons pouvoir définir la valeur des angles de nos aiguilles.

Pour cela, le 4ème paramètre de notre fonction « Register » permet d’ajouter une CallBack à son constructeur qui sera appelée lorsque la DépendencyProperty sera définie. C’est exactement ce que nous voulons :)

Voici comment faire :

public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof (TimeSpan), typeof (MonHorloge), new PropertyMetadata(default(TimeSpan), PropertyChangedCallback));

private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
    throw new NotImplementedException();
}

Il ne reste plus qu’à taper le code qui va bien dans la Callback.


private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
    var component = dependencyObject as MonHorloge;
    var timeSpan = (TimeSpan)dependencyPropertyChangedEventArgs.NewValue;
    if (component != null && timeSpan != null) {
          component.AngleHeures.Angle = (timeSpan.TotalMinutes/60/12)*360;
          component.AngleMinutes.Angle = (timeSpan.TotalMinutes%60)/60*360;
          component.AngleSecondes.Angle = (360/60)*timeSpan.Seconds;
    }
}

A partir de là, nous avons déjà une première version d’horloge fonctionnelle !

Teste de la première version de notre contrôle

Pour la tester, créer un nouveau projet d’application WPF.


Ajouter « MaLibrairie » dans ses références :


Compilez le projet, puis allez dans la « Toolbox ».

Le contrôle « MonHorloge » Apparait en haut de la barre d’outils !


Prenez le contrôle et glissez le dans le designer de votre fenêtre principale


Notre horloge apparait !

Par contre, un petit détail risque de vous chiffonner.
Si vous redimensionnez la taille de l’horloge, les aguilles ne se trouvent plus au centre du cadran.


Ceci est dû au fait que nous avons dessiné notre composant dans une grille qui travaille avec des positions relatives. Pour régler ce problème, il suffit de remplacer notre grille par un « Canvas » afin que nos aiguilles travaillent avec des positions absolues.

Pour cela, ouvrez l’horloge dans VisualStudio, puis remplacez les tags <Grid></Grid> par <Canvas></Canvas> dans le code xaml



Enregistrez et recompilez.
Lorsque vous retournerez dans le designer de votre projet de test, tout rentrera dans l’ordre.



Maintenant que ce petit détail est réglé, vous aimeriez voir les aiguilles tourner, non ?

Rien de plus simple !

Commencez par donner un nom à cette horloge, par exemple « HorlogeFr » en utilisant la fenêtre de propriétés de Visual studio.



Ouvrez ensuite « MainWindows.cs », puis créez un DispatcherTimer pour définir l’heure toute les secondes.


public partial class MainWindow : Window
    {
        readonly DispatcherTimer _timer = new DispatcherTimer();
        public MainWindow()
        {
            InitializeComponent();
            SetHorlogeFr();
            _timer.Interval = new TimeSpan(0,0,1);
            _timer.IsEnabled = true;
            _timer.Tick += timer_Tick;
        }

        void timer_Tick(object sender, EventArgs e)
        {
            SetHorlogeFr();
        }

        private void SetHorlogeFr()
        {
            HorlogeFr.Time = DateTime.Now.TimeOfDay;
        }
    }

Comme vous pouvez le voir, à chaque seconde, nous assignons à la DependencyProperty « Timer » de notre contrôle horloge l’heure qu’il est afin que la position des aiguilles se recalcule.

Il ne vous reste plus qu’à démarrer votre projet de test pour constater le résultat.

Alors ? Pas mal pour des mecs qui ne font que du mode console :)

A ce stade, on a déjà un truc bien, mais pour explorer encore un peu plus WPF, nous allons rajouter la possibilité de changer le cadran d’horloge à la volée, le tout en passant par un petit panel de configuration animé.

Panneau de configuration du cadran

Donc, pour commencer, nous allons retourner sous Blend, qui soit dit en passant tournait en fond de tâche.


Blend a détecté que le projet a été modifié depuis la dernière fois. Normal, car nous avons changé la grille en canvas.

Cliquez sur le bouton « Yes to all » pour accepter tous les changements.

Je rappelle que notre but est de pouvoir changer le cadran de l’horloge à la volée. La première chose à faire consiste donc à trouver d’autres cadrans, et à les dimensionner selon la taille du cadran originale.


Partons sur google Image à la recherches de quelques cadrans supplémentaires :


Je vais prendre par exemple le cadran avec un cheval ailé.


Je vois que ses dimensions sont en 1616x1616 alors que le cadran original était en 500x500.

Le problème c’est que je ne sais pas à quoi correspondent ces dimensions.

Je vais vous montrer comment nous allons régler le problème très simplement avec Blend et MSPaint.

En premier lieu, j’enregistre cette image sur mon disque dur, puis je la glisse dans le répertoire « Cadrans » de ma solution


Je renomme l’image en cadran2.jpg, puis je fais un clique-droit à « Edit externally » sur cadran1.jpg

Celui-ci s’ouvre dans Paint


Cliquez sur « Image » puis « Redimentionner », une fenêtre s’affiche.
Choisissez le bouton radio « Pixels »


Nous voyons que notre image de référence fait 225x225 pixels. Voici donc la taille dans laquelle la nouvelle image doit être redéfinie.


Pour cela, on referme Paint, puis on retourne sous Blend pour faire un clic droit à « Edit externally » sur cadran2.jpg


On refait « Image » puis « Redimentionner » à bouton radio « Pixels », et on entre la valeur 225 dans « Horizontal ». La valeur « Vertical » se met automatiquement à 225 elle aussi.


Cliquez sur OK


Il ne reste plus qu’a enregistrer l’image redimentionnée, et à refermer Paint.

Touvez deux ou trois autres cadrans sur google, et mettez les à la bonne taille en utilisant cette méthode.


Ajout de la liste

Une fois ceci fait, nous allons passer à l’incrustation d’une liste et d’un bouton dans notre contrôle, autant vous dire qu’on va devoir manipuler un peu le designer et le code xaml généré pour arriver à nos fins.


Donc, voici comment se présente votre composant dans Blend


Nous allons créer une grille avec deux colonnes.

La première contiendra le canvas qui contient notre horloge, et la deuxième notre liste de choix.


Dans l’onglet « Object and Timeline », faites un clic droit sur « canevas » puis choisissez « Group into à Grid »


Sélectionnez ensuite « UserControl » pour activer les outils de manipulation dans le designer.


Dans le designer, sélectionnez le carré sur le côté droit, puis étirez jusqu’à avoir un espace suffisant pour ajouter une grille.



Dans l’onglet « Object and Timeline », sélectionnez « Grid » pour activer les outils de manipulations dans le designer, puis ajouter une nouvelle colonne à votre grille en cliquant sur le bord supérieur droit de l’horloge.


Allez ensuite dans la barre d’outils, et choisissez un composant de type « ListBox »


Faite glisser la « ListBox » dans la deuxième colonne de votre grille


Nous allons maintenant aller changer quelques petites propriétés directement en xaml.


Cliquez sur le petit bouton « code » situé dans la barre du coin supérieur droit du designer.


Un éditeur xaml va apparaitre, le code doit ressembler à peu près à ceci :


Nous allons changer ce code pour que :
-          Le canvas soit sur la première colonne, et ne déborde pas sur la deuxième
-          Le ListeBox soit sur la deuxième colonne, et ne déborde pas sur la première
-          La deuxième colonne de la grille soit en taille automatique pour s’adapter à la taille du listBox

Tout cela pourrait se faire avec les propriétés de design, mais c’est beaucoup plus rapide de passer directement par le xaml.

D’ailleurs, une fois que vous serez familiarisé avec Blend, vous remarquerez que beaucoup de choses peuvent directement être faite en xaml, ce qui fait gagner pas mal de temps.


En premier lieu, voici les modifications à apporter au niveau des définitions de colonnes :



Ensuite, nous indiquons au canevas qu’il est dans la colonne 0 de la grille


Descendez tout en bas, et supprimez tous les attributs du nœud « ListBox » à l’exception de « Grid.Column= »1 » »


Voilà pour cette partie.

En retournant dans le designer, votre composant doit ressembler à ceci.


Template et Animation

Alors, maintenant nous allons commencer à entrer dans le dur, là ou WPF apporte pas mal d’avantages par rapport à du Winform standard.

Si je vous dis que dans notre ListBox, je veux afficher chaque candran sous forme d’un libellé, que me répondez-vous ?

Facile !

Ok, maintenant si je vous dis que le libellé doit avoir une police un peu stylé ?

Jouable !

Très bien, et si je vous dis que je veux un aperçu de l’image à gauche de chaque libellé qui soit assez gros pour voir quelque chose ?

Ça se complique !

Et si en plus je veux qu’au survol de la souris, l’image s’agrandisse pour avoir un meilleur aperçu ?

Oula ! On n’est pas chez Apple ici !

Grace à WPF, vous pouvez prendre n’importe quel contrôle de base et le redessiner comme bon vous semble grâce aux templates ! C’est ça qui est magique !

Mais comment faire ?


Faites un clic droit sur l’objet « ListBox » dans l’onglet « Object and Timeline », et choisissez le menu « Edit Additional Templates  à Edit generated Items (ItemTemplate ) » à «Create Empty»



Une boite de dialogue s’ouvre.


Choisissez dans la ComboBox de « This document » le choix « ListBox » car seul ce contrôle se verra appliquer le template (pas besoin de partager le template à tous les composants du contrôle)


En cliquant sur « OK », le designer ne montre plus le contrôle de l’horloge, mais affiche a quoi va ressembler graphiquement le Template



Donc là, pour ceux qui n’auraient pas compris, Blend nous donne la possibilité de dessiner à quoi va ressembler chaque item de notre ListBox !

Il suffit donc de  concaténer une image avec un TextBox, et le tour est joué !


Dans « Object and Timeline », faites un clic droit sur « Grid » et choisissez « Change Layout Type à Stackpanel». Cela permettra d’avoir quelque chose de plus rigide pour construire notre item 


Augmentez le zoom de votre designer pour pouvoir insérer des composants au stackpanel


Allez chercher dans la barre d’outils une image et glissez la dans le stackpanel.

Glissez-y ensuite un TextBlock.


Votre template va ressembler à quelque chose comme ça :


Nous voulons que le texte soit à droite de l’image, nous sélectionnerons donc le stackpanel dans « Objects and Timeline » puis nous irons dans la fenêtre des propriétés dans la partie « Layout » changer la propriété « Orientation » de « Vertical » à « Horizontal »


Voici l’impact sur le designer


Comme vous pouvez le constater, le texte n’est pas centré. Nous allons donc nous en occuper.


Cliquez sur le TextBlock puis ouvrez l’onglet « Properties ». Dans la partie « Layout » choisissez « Left » pour « HorizontalAlignement » et « Center » pour « VerticalAlignement »


Le TextBlock dans le designer devient tout de suite beaucoup mieux :


Pour avoir une meilleure vision de à quoi cela va ressembler, cliquez sur le composant « Image » puis dans les « Properties », choisissez un cadran dans la combobox proposée par la propriété « Source » de la partie « Common »



Ceci permet de bien prendre conscience du rendu final.


Attention, cette affectation est temporaire, vous devrez la remplacer par un data binding lorsque les manipulations seront terminées.


Bien. Jusque-là les choses se sont assez bien passées.

Mais maintenant, comment faire pour que l’image s’agrandisse lors d’un survol de la souris ?

Une fois de plus, le moteur WPF va nous sauver la vie en nous permettant de mettre en place ce genre d’effet sans taper une ligne de code.

Tout d’abord, commençons par mettre l’image du cadran à une taille raisonnable, disons 50x50.

Cliquez sur l’image et changez les propriétés With et Height



Très bien.

Maintenant, nous voulons que lorsque l’utilisateur passe sa souris au-dessus de l’image, celle-ci s’agrandit, et lorsqu’il sort le curseur de l’image, celle-ci reprend son état initial. Facile :)

Pour faire cela avec WPF, nous allons mapper des « StoryBoard » aux événements « OnMouseEnter » et « OnMouseLeave » de notre image.

Mais c’est quoi un storyboard ?

Pour faire simple, c’est une ressource éditable qui se présente sous la forme d’une ligne de temps, un peu comme dans les logiciels de montage vidéo.
Cette ligne de temps permet de spécifier à WPF des propriétés qui doivent changer dans le temps. Et là où c’est bien fait, c’est que WPF s’occupe de la transition graphique à votre place.

Ici par exemple, nous allons créer un storyboard pour que la taille de l’image passe de 50x50 à 150x150 en 200ms. Il nous suffira simplement de créer une image clé dans laquelle notre composant possède les nouvelles dimensions, et WPF s’occupe de tout pour nous !!

Allez zou, je vous montre comment faire.


Dans les onglets situés en bas de Blend, cliquez sur l’onglet Triggers. Et sélectionnez dans l’onglet « Objects and Timeline » le composant « image »


Cliquez sur le bouton « + Event » pour créer un nouvel évènement à mapper à un storyboard.


Dans les listes déroulantes situées à côté de « When », changez « target-element » par « image » et changez « Loaded » par « MouseEnter »


Cliquez sur le bouton « + » situé à droit des listes déroulantes.


Normalement ce message apparait :


Cliquez sur « OK » pour créer un storyboard qui sera lié avec cet évènement


Nous venons ici de créer un storyboard du nom de « OnMouseEnter1 », celui-ci possède un petit rond rouge sur sa gauche ce qui signifie qu’il est en cours d’enregistrement. D’ailleurs, vous remarquez aussi que le designer est entouré d’un cadre rouge avec une petite annotation en haut.


A ce stade, nous allons définir le comportement graphique qui va-t-être joué lorsque la souris va passer au-dessus de notre image.


Cliquez sur le composant « image » dans « Objects and timeline » puis déplacez le curseur de votre ligne de temps à 0:00.200


Allez dans les propriétés de votre image, et changez le « Width » et le « height » pour qu’ils soient tous les deux à 150


Vous remarquez dans la ligne de temps qu’une image clé est apparue.


Et aussi que l’image dans le designer est plus grosse.


Pour voir l’effet que cela va donner, cliquez sur le bouton « Play » de la ligne de temps de votre storyboard.



Normalement, vous voyez dans le designer l’image passez de 50x50 à 150x150.

Cool non ?

Faisons maintenant l’inverse, c’est-à-dire que lorsque je vais retirer ma souris de l’image, celle-ci va se rétracter.


Ajoutez un nouvel évènement en cliquant sur le bouton « +Event »




Dans les listes déroulantes situées à côté de « When », changez « target-element » par « image » et changez « Loaded » par « MouseLeave »


Si vous ne voyez pas « Image » dans les choix de la liste de « target-element », prenez garde à bien avoir cliqué sur « image » dans « Objects and Timeline »


Je vais vous montrer comment gagner un peu de temps en évitant de récréer complétement un storyboard.

Il suffit de dire à blend de dupliquer notre premier storyboard, puis de l’inverser :)


Dans « Objects and Timeline », cliquez sur le petit menu déroulant à la droite du « + » situé au niveau du nom du storyboard puis cliquez sur « Duplicate »


Renommez le nouveau storyboard de « OnMouseEnter_Copy1 » en « OnMouseLeave 1»





Déroulez une nouvelle fois le petit menu et choisissez « Reverse »


Il ne vous reste plus qu’à assigner ce nouveau storyboard à votre évènement « MouseLeave » :)

Cliquez sur le « + » situé à droite de votre événement.



Puis choisissez-le storyboard « OnMouseLeave1 »


Et voilà ! Vous venez en quelques clics de créer une petite animation sympa sans effort !

Bon, pourquoi ne pas continuer sur notre lancée et maintenant faire l’ouverture de la liste de paramétrage de cadran ?


Sortez du designer de template en cliquant sur « MaListe »




Bouton de paramétrage

Nous allons ajouter un bouton dans le coin supérieur droit de l’horloge qui nous permettra d’ouvrir la liste à droite afin de choisir un cadran approprié.

Dans votre barre d’outils, recherchez un contrôle de type « Button »



Glissez le bouton dans le coin supérieur droit de l’horloge


Vous conviendrez que ce bouton n’est pas très sexy.

Ce que nous voulons, c’est un petit bouton assez discret contenant une image représentative.


Pour trouver notre image, allons sur www.iconfinder.com à la recherche d’une icône gratuite pour notre bouton.


Celle du milieu semble bien. Je clique dessus afin de la télécharger au format 16x16


Une fois celle-ci enregistrée sur mon disque, je la renomme « param_button.png » puis je la glisse directement dans mon projet Blend.


Passons au paramétrage du bouton.

Cliquez dessus dans le designer et allez dans la fenêtre des propriétés.

Effacez en premier lieu le contenu de la propriété « Content », puis définissez le « Width » et le « Height » à 16


Pour définir l’image du bouton, allez dans la partie « Brush » puis cliquez sur « Background ».

Choisissez le bouton onglet « Tile brush » puis mettez en « imageSource » notre « param_button.png » téléchargée précédemment.


Définissez aussi le « Stretch » à « None »


Il ne vous reste plus qu’à placer le bouton en haut à droite du composant horloge


Maintenant, nous allons définir ce qui doit se passer lors d’un clique sur ce bouton.

Pour rappel, nous voulons que la liste située à droite de l’horloge s’étende vers la droite pour pouvoir sélectionner un cadran. Nous allons donc mapper un storyboard qui fait cette action à l’évènement « Click » de notre bouton.

C’est parti !

Cliquez sur le bouton « + Event » de l’onglet « Triggers »



Après avoir bien vérifié que l’élément « Button » est sélectionné dans « Object and Timeline », choisissez dans les listes déroulantes à côté de When « Button » et « Click », puis cliquez sur le bouton «+ »


Un message proposant de créer un nouveau storyboard apparait. Cliquez sur OK.


Un storyboard « OnClick1 » a été créé.



Sélectionnez le composant « MaListe » dans « Object and Timeline », puis déplacez le curseur de l’image clé sur 0 :00.300


Dans la fenêtre « Properties » de « MaListe », essayez de changer la valeur de la propriété « Width »


Si la valeur est paramétrée en « Auto » comme dans la capture d’écran, un message d’erreur apparait vous expliquant qu’il n’est pas possible d’animer une propriété qui possède une valeur auto-calculée.


Dans ce cas, il faut fermer le storyboard et appliquer une valeur fixe à la propriété « Width » de votre liste.



Une fois la propriété défini, re-sélectionnez le storyboard « OnClick1 » dans « Object and Timeline »


Sélectionnez le composant « MaListe » dans « Object and Timeline », puis déplacez le curseur de l’image clé sur 0 :00.300


Dans la fenêtre « Properties » de « MaListe », changez la valeur de la propriété « Width ».


Normalement, vous n’avez plus le message d’erreur vu précédemment.

Une image clé vient d’apparaitre dans le storyboard.


Vous pouvez maintenant cliquer sur le bouton « play » pour visualiser l’animation.



Voilà, nous venons de faire en sorte que la liste se déploie sur la droite lorsque l’on clique sur le bouton.

Nous allons maintenant mettre en œuvre le mécanisme de rétractation de la liste qui se produira lorsque la souris sortira de celle-ci.


Nous allons ajouter un évènement dans les triggers en cliquant sur le bouton « +Event »



Après avoir pris soins de sélectionner « MaListe » dans « Objects and Timeline », on déroule les listes de choix à coté de « When » pour définir l’événement « MouseLeave » de « Maliste »


Ensuite nous dupliquons le storyboard « OnClick1 »


Puis nous le renommons « OnListMouseLeave »



Nous inversons le storyboard pour donner l’effet de rétractation.


Puis nous assignons ce storyboard à l’évenement « MouseLeave » défini précédemment.




Voila ! Tous nos comportements graphiques sont définis !

Il ne nous reste plus qu’à passer à la partie programmation pour ajouter des items à la liste de choix, puis à définir quelques options de « binding », et le tour est joué !

Databinding

Fermez le programme « Blend » et retournez dans VisualStudio.

Double cliquez sur « MonHorloge.xaml » pour visualiser le code Xaml de votre horloge


Si vous regardez ce code, vous trouverez tous les éléments que nous avons définis dans blend. Les storyboard, les évènements de clique, les boutons, les templates, tout y est.

Nous allons jeter un œil à ce code pour variabiliser les parties liées aux images de cadans, qui pour l’instants sont toutes définies en dur.


Par exemple le cadran principal est défini pour être le fichier cadran1.jpg


Nous avions aussi défini ce même fichier dans le « item template » de notre liste afin d’avoir une meilleure visualisation.


Nous voyons aussi dans ce même bloque de code que le texte est défini en dur.


Donc, quel est le plan d’action ?

Dans un premier temps, nous allons variabiliser le texte et l’image de nos items de liste, puis ensuite nous allons lier l’image du cadran principal avec l’image de la sélection courante dans notre liste.

Pour variabiliser les items, nous allons utiliser le mécanisme de « Data Binding » utilisé par WPF.

Ce système très puissant permet de dire à un composant graphique qu’il doit aller puiser une valeur dans une propriété dont le nom lui est indiqué.

Mais attention, le data binding est une arme à double tranchant car celui-ci est sous forme de couplage faible.

C’est-à-dire que nous allons indiquer à WPF : « Pour la propriété graphique X, tu appliqueras la valeur Y contenue dans la propriété de classe de nom Z »

Et WPF nous fait confiance. Il n’ira pas vérifier à la compilation que le membre est bien lié à une classe dont l’une des propriétés possède bien le nom Z. C’est à nous de nous débrouiller pour lui fournir les bonnes informations.


Voyez comment nous allons définir le binding pour la source de l’image ainsi que pour le texte de notre TextBlock :


Nous venons d’indiquer à WPF : « Tu trouveras le chemin de l’image dans la propriété ‘Image’ et le texte à afficher de la propriété ‘Texte’ de l’item que nous te passerons »

Grâce à ce procédé, nous pouvons ajouter des items sous forme de classes anonymes à notre liste. Tant que celles-ci possèdent nos deux propriétés, WPF n’y vois aucun inconvénient !

public MonHorloge()
{
    InitializeComponent();
    MaListe.Items.Add(new { Image = "Cadrans/cadran1.jpg", Texte = "Standard"});
    MaListe.Items.Add(new { Image = "Cadrans/cadran2.jpg", Texte = "Romain" });
    MaListe.Items.Add(new { Image = "Cadrans/cadran3.jpg", Texte = "Jaguar" });
    MaListe.Items.Add(new { Image = "Cadrans/cadran4.jpg", Texte = "Emeraude" });
    MaListe.Items.Add(new { Image = "Cadrans/cadran5.jpg", Texte = "Classique" });
}

L’avantage avec le databinding, c’est que celui-ci permet aussi d’aller chercher des informations dans les propriétés d’un sous composant de notre arborescence. C’est exactement ce dont nous avons besoins pour définir notre cadran principal !


Allez dans le code WPF de l’image, et définissez le binding de la façon suivante :


La syntaxe indique la chose suivante : « tu iras chercher l’image à afficher dans la propriété ‘SelectedItem.Image’ de l’élément ‘MaListe’ »

Une fois ceci fait, on a presque terminé.

Effectivement, il faut définir dans le code au moins un « SelectedItem » par défaut, sinon notre horloge n’aura pas de cadran au démarrage.

Pour cela, il suffit de rajouter la ligne de code suivante à la fin du constructeur de « MonHorloge.cs »


MaListe.SelectedItem = MaListe.Items[0]

Et voilà, c’est enfin terminé !

Maintenant que vous disposez d’un contrôle d’horloge, Vous pouvez l’utiliser dans toutes vos applications qui en ont besoins comme bon vous semble !

Conclusion

Nous venons de voir comment WPF facilite grandement la conception d'IHM pour votre application, mais ce n'est pas tout !

Car grâce au langage Xaml et aux mécanismes de "Data binding", WPF permet enfin un vrai découplage entre votre code et votre IHM, ce qui présente encore plein d'autres avantages :
  • Vous pouvez intégrer un graphiste/designer à votre projet sans que celui-ci possède nécessairement de grosses compétences en développement.
  • Le découplage total permet de pousser la testabilité de votre logiciel sur la partie interface, notamment grâce au pattern MVVM.
  • Les interfaces que vous réalisez en Xaml sont compatibles avec toutes les technologies de développement Microsoft.
Bref, si un allergique à la programmation graphique comme moi est convaincu par cette technologie, autant vous dire que vous pouvez y aller tranquille :)

3 commentaires:

  1. Sympa ton article ! Boudoux qui fait de l'UI : ça me redonne espoir. Le monde n'est plus ce qu'il est, il change. Hollande aura peut-être un truc intéressant à dire bientôt...

    RépondreSupprimer
  2. Merci bien, le changement est au cœur de la vie d'un développeur :) Malheureusement pour Hollande, comme sa méthode de gestion est basée sur des estimations de croissance, je doute qu'il ait bientôt quelque chose d'intéressant à dire. Il faudrait peut être que ses conseillers lui parlent du #NoEstimates ;)
    @+

    RépondreSupprimer
  3. Super ton article ! Ravi de voir que tu commences à passer dans le monde obscur du design/graphisme des applications WPF ! L'exemple de l'horloge possède des problématiques intéressantes.

    Je voudrais juste ajouter que même si c'est possible (théoriquement), il est très difficile de faire du XAML sans connaître un minimum comment coder / avoir des notions de programmation. Cet avantage avancé par Microsoft, de mon point de vue, n'ai pas du tout une réalité. Dans le monde réel, ce sont les développeurs WPF, Windows Phone, et Windows Store Application qui font aussi la partie graphique et donc les composants UI. Et d'ailleurs c'est mieux, au moins ils correspondent vraiment aux besoins des fonctionnalités.

    Par contre, cet avantage est beaucoup plus visible dans le monde HTML, CSS.

    Hollande ? NoEstimates ? Plutôt dur à appliquer en politique !

    ++

    RépondreSupprimer