Pratique de Silverlight


précédentsommairesuivant

1. Introduction

Dans ce chapitre, vous découvrirez la nouvelle plate-forme de diffusion plurimédias interactive Silverlight et les raisons qui ont poussé Microsoft à sa conception. Vous apprendrez en quoi Silverlight hérite de la bibliothèque .Net et propose un nouveau modèle de conception avantageux. Nous évoquerons également la nouvelle suite d'outils Expression Studio, dédiée aux graphistes et aux intégrateurs, et comment ceux-ci se positionnent dans la chaîne de production.

Silverlight laisse aussi bien la place aux langages .Net C# qu'aux langages libres et dynamiques comme JavaScript. Prendre partie pour l'un de ces langages est l'une des étapes incontournables de sa prise en main. Nous listerons donc les critères qui orienteront votre choix parmi ceux-ci. De manière générale, Silverlight est fait pour améliorer notre quotidien de designer ou de développeur, ainsi que l'expérience utilisateur sur Internet. Nous examinerons les moyens mis en œuvre pour atteindre ces différents objectifs.

1-1. Qu'est-ce que Silverlight ?

Silverlight est un lecteur Internet existant sous forme de plug-in, anciennement connu sous le nom de WPF/E, initiales de Windows Presentation Foundation Everywhere. Les applications lisibles par ce lecteur sont des fichiers au format xap. Basé sur un moteur vectoriel puissant, il permet d'afficher du contenu dynamique et interactif pour le Web. Il facilite la conception et la diffusion d'applications riches sur Internet. Il fait partie de la plate-forme de développement .Net (prononcer "dotte net") proposée par Microsoft, mais ne nécessite qu'une fraction héritée de celui-ci pour être exécuté, autrement dit, l'installation du lecteur seul suffit. Cette particularité lui permet d'être diffusé au sein de nombreux navigateurs sur Mac OS, Windows ou Linux (via le projet Moonlight pour Linux, voir Tableau 1.1).

Tableau 1.1 - Navigateurs et systèmes supportés.

Système
d'exploitation

Internet Explorer 8

Internet Explorer 7

Internet Explorer 6

Firefox 2 / 3

Safari 3 / 4

Windows 7

Oui

-

-

Oui

-

Windows Vista

Oui

Oui

     

Windows XP SP2

Oui

Oui

Oui

Oui

-

Windows 2000 SP4

 

-

Oui

-

-

Windows Server 
2003 / 2008

Oui

Oui

Oui

Oui

-

Mac OS 10.4.8+ (PowerPC)

 

-

-

Oui

Oui

Mac OS 10.4.8+ (Intel)

 

-

-

Oui

Oui

Linux, FreeBSD, SolarisOS

 

Supporté par le projet Moonlight développé par Novell

Les applications .Net traditionnelles reposent sur la notion de client lourd, c'est-à-dire qu'elles nécessitent l'installation de la bibliothèque .Net. Celle-ci peut peser jusqu'à 195 Mo pour la version 3.5. Le lecteur Silverlight échappe à ce type de fonctionnement peu souple, car c'est un environnement d'exécution pour les applications de type client léger. Cela signifie que son poids est suffisamment négligeable (de 4 Mo à 7 Mo, selon les systèmes d'exploitation) lors du téléchargement, pour que son installation soit rapide et les applications exécutées très légères (souvent inférieure à 1 Mo). Sous Vista et Windows 7, les droits nécessaires à son installation sont moins contraignants pour l'utilisateur de la machine cliente. Bien que l'on puisse avoir l'impression qu'il s'agit d'un détail, les réseaux d'entreprises actuels sont souvent constitués de centaines, voire de milliers d'ordinateurs, et les déploiements qui en découlent nécessitent d'importants moyens humains et techniques. Dans ces conditions, le déploiement de clients légers est une stratégie efficace à court et moyen termes puisque les applications développées pour ces plates-formes sont de plus en plus performantes et puissantes. Malgré un poids négligeable, Silverlight apporte un environnement de développement complet.

1-2. De .Net 1 à Silverlight

Comme nous l'avons vu précédemment, Silverlight est à la fois une évolution parallèle et un héritage de la plate-forme de développement .Net (prononcé "dotte nette"). Qu'est-ce que .Net ? Quel est son but et comment est-elle née ? Répondre à ces questions nous permettra de mieux comprendre les orientations prises par les équipes d'ingénieurs ayant participé à sa conception et nous permettra plus facilement d'envisager les améliorations possibles dans le futur.

Il faut remonter le temps jusqu'à février 2002 pour voir la sortie de .Net 1.0, livré avec le Service pack 1 de Windows XP. Visual Studio.Net est à cette époque, l'outil de prédilection pour développer ce type d'applications. .Net est né des besoins d'interopérabilité (entre ancienne et nouvelle technologie) et de portabilité (valable pour les langages et les systèmes). Les objectifs sont simples :

  • proposer une meilleure plate-forme de développement ;
  • apporter un environnement indépendant du réseau ou du langage choisi quels qu'ils soient Visual Basic, C# ou C++, etc. 
  • délivrer un unique modèle de conception quelle que soit la version de Windows : XP, 2000, Windows CE, etc.
  • concurrencer Java ;
  • faciliter la création de services Web ;
  • accompagner les développeurs et assouplir la méthodologie de développement avec en point de mire l'industrialisation des processus.
    Microsoft propose de nombreux systèmes d'exploitation et .Net uniformise le développement d'applications. Les applications développées en .Net ciblent essentiellement l'OS Windows. Par ailleurs Microsoft réalise une première percée significative pour scinder le design des interfaces et le code logique, grâce aux formulaires Windows, surnommés Winforms. Avec ce type d'architecture, apparaît la notion de code "behind". Un fichier va contenir le code nécessaire pour fabriquer l'interface visuelle. Celle-ci sera simplement créée par de simples glisser-déposer de composants dans la fenêtre de prévisualisation sous Visual Studio. Un autre fichier contient désormais le code logique, ou code "behind", sans lequel l'interface ne répondrait pas aux interactions utilisateurs et ne posséderait aucune logique. Sorti fin 2005, .Net 2 est une véritable amélioration mais n'apporte que peu de nouveautés. Il faut attendre janvier 2007 pour découvrir .Net 3 qui révolutionne le développement d'applications sous Windows.
    .Net 3 n'est pas une refonte, mais une extension de .Net 2. Cette version apporte, en plus de C# 3 ou de technologies comme Linq, de nombreux bénéfices répartis en quatre modules :
  • WCF (Windows Communication Foundation) facilite la communication entre les applications, les domaines ainsi que le partage de bas de données ;
  • WCS (Windows CardSpace) pour l'authentification des utilisateurs ;
  • WWF (Windows Workflow Foundation) pour l'amélioration des flux de production ;
  • WPF (Windows Presentation Foundation) comme couche de présentation.

WPF est la technologie qui nous concerne : Silverlight en est un développement à la fois hérité et parallèle. On peut considérer que WPF représente le futur de la technologie Winforms. Tout en conservant les capacités précédentes, il intègre les profils du designer et de l'ergonome qui, dans 90 % des cas, étaient soit mis à l'écart, soit relégués au second plan. Microsoft comprend en effet que la fonctionnalité n'est plus la seule garantie d'une réussite commerciale ou technique. De plus en plus d'applications voient le jour et beaucoup sont fonctionnelles. Toutefois, seules très peu d'entre elles y ajoutent l'ergonomie et l'esthétisme, deux facteurs que l'on retrouve pourtant dans l'industrie automobile, les téléphones portables et de nombreux autres secteurs. Grâce à l'environnement WPF, vous pourrez changer le visuel d'une application dans sa globalité. Modifier le design d'une barre de défilement ou d'une liste d'éléments se révèlera très simple. Tout ceci est réalisable grâce à WPF et au langage XAML (eXtensible Application Mark-up Language). Le XAML permet de formaliser n'importe quel dessin vectoriel ou composant visuel sous forme de balises XML. Un client peut dorénavant configurer le visuel d'une application en modifiant des feuilles de styles XAML et y apposer ainsi sa charte graphique sans faire appel à l'éditeur. Certes, ces opérations sont réalisables depuis longtemps en important des bibliothèques entières de classes, mais autant dire que les graphistes n'étaient pas vraiment présents dans le processus de création. Exit donc les interfaces grises, rectangulaires, héritées de Windows 3.1.

WPF aborde donc avec succès l'un des grands chantiers de l'informatique moderne : la séparation complète du fond et de la forme, elle dépasse de loin les Winforms sur ce point. D'autres technologies ont également essayé de répondre à cette problématique : XUL, de la fondation Mozilla, ou encore le langage FXG basé sur SVG du côté d'Adobe. WPF a pris de l'avance, mais concerne les développements sur système Windows pour les langages de hauts niveaux. À partir de ce constat, une nouvelle problématique apparaît. De manière générale Microsoft est avant tout éditeur de solutions, quel que soit le système d'exploitation, c'est là son cœur de métier. Il se doit donc proposer des solutions indépendantes du système. Afin de supprimer le couplage existant entre l'environnement de développement et les systèmes d'exploitation Windows ciblés, WPF/Everywhere voit le jour. C'est l'ancien nom de code de Silverlight. L'objectif est clair : puisque l'on crée des solutions autant faire en sorte qu'elles ciblent le plus de systèmes d'exploitation possibles. Le moyen le plus efficace consiste alors à proposer un lecteur Internet sous forme de plug-in. Les navigateurs les plus courants sont ciblés en premier, et Silverlight voit finalement le jour en novembre 2007. Cette initiative de Microsoft répond avant tout aux attentes du marché actuel qui évolue rapidement vers les applications en ligne et répond au quasi monopole de la plate-forme Flash.

1-3. Les avantages de Silverlight

Développer avec Silverlight apporte beaucoup d'avantages, dont certains ne lui sont pas forcément propres, mais sont plutôt relatifs à l'ensemble des technologies asynchrones présentes sur le Web comme Shockwave, Flash, Silverlight et Ajax. Silverlight est avant tout orienté applications interactives riches et, dans ce cadre, il bénéficie également de toute l'expérience de Microsoft en matière de développement. Ce serait une erreur de penser que les graphistes sont délaissés, car des efforts importants ont été réalisés pour fournir des outils de design et d'animations performants. Lors du rachat de Macromedia par Adobe, de nombreux acteurs de talent furent recrutés par Microsoft pour atteindre ces objectifs. Voici les avantages les plus significatifs de Silverlight :

  • Comportement asynchrone. Ajax est la première technologie asynchrone au sein du navigateur, mais c'est aussi la plus ancienne. Apparu avec Internet Explorer 5 en 2001, cette technologie, rapidement adoptée, provient d'un objet, en particulier (Xml-HttpRequest), permettant d'effectuer des requêtes client / serveur. La spécificité des technologies asynchrones réside dans le fait que ces technologies ne nécessitent pas le rechargement complet de la page du navigateur à la réception des données du serveur. L'utilisateur peut même continuer à utiliser le reste de l'interface sans provoquer d'erreur. Silverlight possède de nombreuses méthodes asynchrones, plus modernes qu'Ajax et permettant non seulement les échanges dans un format texte, mais également dans un format binaire. Ainsi, l'échange d'images, de flux vidéo, de données typées est possible ; le mode peer-to-peer est également supporté.
  • Il ne dépend pas d'une technologie serveur spécifique. Il vous suffit d'un simple client FTP et le tour est joué. Vous n'avez pas besoin d'une configuration serveur spécifique ou propriétaire. Un serveur Apache traditionnel fera très bien l'affaire pour diffuser votre site ou votre application Silverlight.
  • Ouvert aux technologies du Web. Les langages dynamiques, comme JavaScript, ont la capacité de communiquer avec une instance de plug-in Silverlight de manière transparente. La communication est en réalité possible dans les deux sens. De plus, la lecture, l'écriture ou l'envoi de données sont réalisables dans des formats courants, comme XML, ou JSON, de même que l'accès à n'importe quel type de technologie côté serveur. On peut ainsi interfacer sans problème une application Silverlight avec des services, côté serveur, codés en PHP, ASP, etc.
  • Il bénéficie d'une compatibilité multienvironnement. Comme nous l'avons dit au début de l'introduction, Silverlight est accessible en tant que lecteur sur Mac OS, Windows et Linux (Moonlight) et sur les navigateurs Chrome, Firefox, Internet Explorer 6, 7 et 8, Safari. Votre application, bien que développée avec Visual Studio, Expression Blend ou même Eclipse, sera donc visible et accessible au plus grand nombre.
  • Les applications ne nécessitent pas de déploiement. Autrement dit, une fois accessible à une adresse donnée, l'application est lisible et exécutable au sein des navigateurs par les internautes. Cela est peut être anodin ou normal si vous travaillez actuellement sur le marché du Web, mais la philosophie actuelle (en mutation) consiste encore à déployer des clients lourds et des applications locales poste par poste. Dans des réseaux comprenant des milliers de postes clients, le déploiement de telles applications est extrêmement coûteux en ressources humaines et techniques. Les applications Silverlight apportent une réponse efficace à ces problématiques de diffusion.
  • Les langages logiques .Net supportés sont gérés. Le code géré (dit aussi managé) est l'opposé du code interprété. C# ou VB sont des codes compilés, JavaScript, PHP, HTML sont interprétés. Cela signifie qu'ils sont lus par le logiciel d'une machine cliente ou serveur dans le cas de PHP. Pour un langage comme JavaScript, cela se traduit par des différences de performances flagrantes selon le navigateur, mais aussi par des différences d'interprétations. Dans un site Web traditionnel, l'aspect visuel, ainsi que la version du langage, peuvent donc varier fortement selon les navigateurs (IE, Firefox ou Safari par exemple). Au sein de Silverlight, toutes ces problématiques sont résolues, l'affichage sera le même quelle que soit la plate-forme, la version du langage reste constante. On obtient également beaucoup de performances, car le langage est géré. À la compilation, il est transformé et optimisé en langage intermédiaire. Par la suite, selon la machine cliente, le système d'exploitation et le navigateur, le lecteur s'adaptera pour l'optimiser en un langage machine adapté à chaque navigateur (voir Chapitre 3 Hello World).
  • Il contient un moteur d'affichage vectoriel puissant. L'ensemble des composants ou objets visuels sont vectoriels. Ils sont donc affichés grâce au moteur vectoriel Silverlight. De nombreuses problématiques, comme le poids des fichiers déployés ou encore la génération d'objets de manière dynamique, sont ainsi résolues. Les animations vectorielles sont non seulement possibles, mais également simples à réaliser. L'affichage de texte avec des polices de caractère embarquées, des vidéos ou des images est aisé à mettre en œuvre. Les visuels ne sont pas figés sous forme de bitmap ce qui facilite grandement la mise à jour des applications et le travail collaboratif.
  • Il améliore la collaboration entre designers et développeurs. Héritant de WPF, Silverlight bénéficie de la même architecture de production. Cela signifie que le langage XAML est présent pour formaliser l'ensemble des éléments visuels d'une application. Tout ce que le graphiste va produire génère du XAML, de la mise en forme jusqu'au moindre pictogramme visuel. La conséquence directe est que la création d'un composant (ou contrôle) personnalisé, comme une mire de connexion par exemple, s'effectue en deux étapes distinctes complètement autonomes. Les designers interactifs ou les intégrateurs créent les éléments visuels et les animations. Le développeur ajoute le code logique. C'est une vision un peu simplifiée de la réalité, mais cependant assez proche. L'avantage réside dans le fait que les graphistes participent pleinement à la conception. De plus, ce qu'ils produiront sera fidèlement réutilisé par les développeurs et non recréé ou retraduit par ceux-ci. À tout moment, les designers pourront mettre à jour le visuel sans mettre en danger l'aspect fonctionnel ou modifier le code logique.

1-4. La suite Expression Studio

La gamme Expression Studio fait partie de la stratégie WPF. Cette suite de logiciels apporte un confort jamais atteint pour le développement d'applications Microsoft. Les objectifs de la gamme Expression Studio sont :

  • permettre la création d'applications riches connectées (Rich Desktop Application) ou d'applications riches pour navigateur (Rich Internet Application) via Expression Web ou Blend intégrant Silverlight ;
  • améliorer la qualité graphique et l'ergonomie des applications en proposant de nouveaux outils aux profils de type graphiste, intégrateur ou designer dans l'environnement .Net et Silverlight, tout cela en scindant le fond et la forme. Ces outils facilitent la mise en place d'un flux de production reposant sur la collaboration intermétiers ;
  • proposer de nouvelles solutions aux besoins de la "vidéo à la demande" très présents sur le Web grâce à Silverlight, Expression Encoder et au format wmv ; répondre aux problématiques traditionnelles propres à la création de site Internet via Expression Web.

    Pour rappel, Visual Studio est l'outil de développement phare de Microsoft. Du point de vue d'un développeur et concernant des projets standard, Visual Studio peut être utilisé seul. En effet, il est possible de bâtir un projet Silverlight sans besoin de logiciels de la gamme Expression Studio (Blend ou Design). Toutefois, cela ne serait pas pertinent, on pourrait en effet se demander quelle serait la valeur ajoutée de telles applications ? La gamme Expression facilite justement l'accès au développement à de nouveaux acteurs. Ces nouveaux profils que sont, l'intégrateur, l'ergonome et le graphiste, par leur apport, enrichissent de manière considérable l'approche et le ressenti d'une application pour l'utilisateur. La gamme Expression Studio apporte une ouverture exceptionnelle au monde du graphisme qui, jusque-là, était laissée de côté. Blend et Design ont donc pour mission de fournir du contenu graphique et interactif, ainsi qu'une ergonomie accrue en comparaison des applications bureautiques ou Internet standard.

    Voici une liste des outils compris dans la suite :

  • Expression Web pour la création de sites Internet ;

  • Expression Blend pour la conception d'interfaces riches ;

  • Expression Design pour la conception graphique vectorielle ;

  • Expression Encoder pour l'encodage des fichiers au format wmv en haute définition ;

  • Expression Media pour l'organisation et la lecture des médias ;

  • Visual Studio pour la conception, le développement et l'architecture d'applications.

Pour notre part, nous couvrirons la conception d'applications interactives riches via l'utilisation d'Expression Blend et de Visual Studio.

1-5. Positionnement métier

Comme nous le voyons, WPF et les outils de la gamme Expression sont sur le point de bouleverser les modes opératoires existants. La tendance actuelle repose sur un besoin devenu essentiel qui consiste à formaliser le graphisme grâce à des langages déclaratifs de type XML. Il est intéressant de noter que ce mouvement, initié par Mozilla avec le XUL et par Adobe avec le SVG il y a quelques années, est aujourd'hui concrétisé par Microsoft. Cependant, nous parlons bien de tendance, ce qui signifie que d'autres acteurs comme Adobe reprennent la même direction. Le FXG est la version Adobe du XAML, les deux langages se ressemblent fortement. L'enseignement que l'on peut tirer de cette situation est que les modes et flux de production s'uniformisent et sont en pleine mutation. Les prochaines années seront riches à tous points de vue. Les capacités de Silverlight, Flash et Ajax (et nouvellement XHTML 5) à proposer des langages performants et à diffuser du contenu innovant seront décisives. Ce qui est abordé dans cette section est donc valable pour tous les acteurs du marché quelle que soit leur provenance.

1-5-1. Designer interactif

Au sein de l'environnement .Net, trois logiciels de la gamme sont le théâtre de cette évolution : Expression Blend, Expression Design et Visual Studio.

  1. Expression Design concerne essentiellement les graphistes. Il contient l'ensemble des outils nécessaires à la création vectorielle. Cependant il joue aussi le rôle de passerelle et permet d'importer nativement de nombreux types de fichiers dont le format Adobe Illustrator ou le format Photoshop psd.
  2. Expression Blend cible avant tout les designers interactifs, les intégrateurs et les designers d'eXpérience Utilisateur. Les ergonomes peuvent aussi dans une certaine mesure créer des maquettes et prototype en partie réutilisables (voir Chapitre 9 Prototypage dynamique avec SketchFlow).
  3. Visual Studio comme outil de développement principal est conçue pour les développeurs et possède un puissant outil de débogage que Blend ne fournit pas. À ce jour, il est considéré comme l'un des meilleurs produits délivrés par Microsoft.

Le tableau 1.2 indique la localisation de Blend / Design dans la chaîne de production, ainsi que l'investissement à prévoir en fonction du rôle de chacun. On remarque que chacun peut l'utiliser, Blend étant un outil polyvalent et multiforme.

Tableau 1.2 - Positionnement de Blend au sein de la chaîne de production.

Rôle

Outils et niveau d'utilisation

Expression Design

Expression Blend

Visual Studio

Graphiste

Fort

Moyen

Aucun

Designer interactif

Moyen

Fort

Faible à moyen

Développeur

Aucun

Faible à moyen

Fort

Comme on peut s'en rendre compte, un nouveau profil émerge : le designer interactif. On peut considérer ce profil comme un nouveau genre de designer Web. Dans la majorité des cas, c'est un graphiste ayant évolué et acquis une réelle culture de développeur, ainsi qu'une connaissance aiguë de l'ergonomie. L'intégrateur HTML représente en partie ce type de profil. Il est à la fois confronté à des langages comme JavaScript, PHP (réalisation de modèles) et CSS, tout en conjuguant cette technique à une sensibilité de graphiste. La différence entre intégrateur et designer interactif repose essentiellement sur la partie animations et transitions. On peut voir le flasher comme designer interactif. Son objectif principal est de coupler le visuel, l'ergonomie, l'animation et les transitions. Il considère la logique applicative et la fonctionnalité comme importantes, mais coulant de source et plutôt dévolues aux développeurs.

Au sein des environnements WPF et Silverlight, le designer interactif tient un rôle crucial, puisqu'il fait le lien entre les profils situés à chaque extrémité de la chaîne de production. C'est un élément fédérateur. Silverlight et WPF proposent un modèle qui facilite la collaboration, mais c'est le profil de designer interactif qui formalise et permet cette communication. On pourrait y voir un mélange des genres douteux, mais sans lui, on se retrouverait dans une situation délicate où les graphistes n'auraient finalement que peu d'influence sur la production.

1-5-2. La chaîne de production

Tout projet conçu avec Blend s'inscrit dans le cadre WPF, les applications Silverlight sont un sous-ensemble de WPF. Cependant, au contraire des applications WPF bureautiques, les applications Silverlight sont multiplate-formes et multinavigateurs. Les projets Silverlight suivent la logique classique de tout développement applicatif avec un renforcement singulier de la collaboration entre chaque pôle métier. Voici le détail de chaque étape et son impact métier.

  • Cahier des charges. Profils concernés : client, D.A. / D.T., chef de projet, graphistes, développeurs, ergonomes et utilisateur final…
  • Maquette fonctionnelle et story-boards. Profils concernés : 50 % développeurs et 50 % graphistes ou ergonomes. Réflexion papier commune sur la disposition des objets de l'interface et sur l'ergonomie de l'application. La navigation et la cinématique doivent être pensées à ce stade.
  • Squelette technique et maquettage. Les collaborateurs du projet sont dans le même bureau et travaillent ensemble à :
  • - l'élaboration du squelette technique, profils concernés : 95 % développeurs et D.T., 5 % graphistes ou ergonomes,
    - la création de plusieurs maquettes sous Expression Design ou Illustrator "Look & Feel", profils concernés : 5 % développeurs et 95 % graphistes ou D.A. 
    Cette phase se termine lorsque l'une des maquettes est validée par le client.
  • Production. Cycle de "va et vient" dit itératif entre chaque pôle métier qui est une interaction continue entre les profils. Durant cette phase chaque pôle métier fournit à l'autre les éléments dont il a besoin afin d'avancer dans le développement du projet.
Image non disponible
Figure 1.1- Le cycle itératif de production

Un designer interactif est toujours nécessaire. Toutefois, si le projet nécessite peu de ressources et qu'il est de faible envergure, ce rôle est dévolu à un graphiste ou à un développeur, ou encore aux deux à tour de rôle selon les besoins.

1-6. Langages de développement et choix

Choisir un langage de développement n'est pas seulement un choix pragmatique, cette décision est étroitement liée à la sensibilité et à l'histoire personnelles de chacun. Nous allons toutefois lister les langages, et essayer de nous décider pour l'un d'eux de manière impartiale.

1-6-1. Langages accessibles

Plusieurs langages sont accessibles aux développeurs dans la gamme Expression Studio, ils sont catégorisés en trois types distincts. La première catégorie concerne les langages managés propres à la CLR (Common Language Runtime). On y compte :

  • le langage déclaratif XAML : XAML signifie eXtensible Application Mark-up Language ; ce langage permet de formaliser le graphisme : les courbes vectorielles, les dégradés de couleurs ou les couleurs unies, les styles, les composants visuels, les animations, et les modèles de composants qui correspondent à la forme et au graphisme de ceux-ci ;
  • C# : le langage logique que nous utiliserons tout au long du livre (voir Chapitre 2 Le couple XAML / C#) ;
  • Visual Basic : langage de haut niveau, comme C#, mais plus spécifique dans son écriture, nous ne l'utiliserons pas dans ce livre.
    La deuxième catégorie concerne les langages managés dynamiques gérés par la DLR (Dynamic Language Runtime). Ils ne seront pas couverts ici. Parmi eux on trouve :
  • IronPython: c'est le portage du python pour la plate-forme .Net, vous trouverez plus d'informations à l'adresse suivante : http://www.codeplex.com/IronPython;
  • IronRuby: c'est la version du langage Ruby propre à .Net, vous trouverez plus d'informations à l'adresse suivante : http://www.ironruby.net/;
  • JScript : c'est un langage créé par Microsoft qui jouit de la norme ECMA ;
  • JavaScript : version spécifique du langage du même nom, mais compilée par la DLR.

La troisième catégorie fait référence au langage non compilé (donc interprété) JavaScript, cela engendre moins de possibilités et d'accès au lecteur Silverlight, mais l'accès est transparent.

Le XAML est le seul langage dont vous aurez besoin quel que soit le code logique que vous choisirez. Celui-ci est orienté présentation, c'est un langage déclaratif de type XML (voir Chapitre 2 Le couple XAML / C#).

1-6-2. CLR, DLR et langages non compilés

Nous aborderons en détails les notions de CLR et de DLR au Chapitre 3 Hello World. Sachez simplement que la CLR est le compilateur gérant les langages natifs de la plate-forme .Net, donc de l'environnement Silverlight. Au sein de Silverlight, deux langages sont gérés par défaut dans les projets : Visual Basic et C#, mais la CLR donne accès à d'autres langages moins connus comme F# par exemple. D'une toute autre manière, la DLR ouvre et enrichit Silverlight et d'autres langages dynamiques qui ne sont pas forcément hérités de ou propres à .Net. Un langage dynamique est un langage capable de faire évoluer sa structure à l'exécution, autrement dit un langage dont il est facile d'étendre les capacités des classes ou objets natifs dynamiquement. Ce type de langage n'est pas réellement dans la culture originelle de Microsoft mais les anciens dogmes provenant des années 80 sont en cours de mutation. L'évolution de C# en est un flagrant exemple. La DLR est en réalité une surcouche à la CLR, elle permet à des langages dynamiques d'accéder au lecteur Silverlight en communiquant avec la CLR. JavaScript est un cas particulier, il peut être géré de deux manières, soit par la DLR, soit de manière transparente. Lorsqu'il est géré par la DLR, sa mise en production est, au départ, plus complexe mais dans ce cas, il accède entièrement à la CLR et bénéficie des avantages liés à cette dernière. Dans le cas d'une utilisation transparente, il donne directement accès à une petite partie des capacités du lecteur Silverlight, mais les performances à l'exécution sont beaucoup moins élevées puisqu'il n'y a pas de compilation.

Le choix du langage dépendra essentiellement de votre culture de développeur. Cependant, décider d'utiliser ou non directement JavaScript comme langage non compilé est un peu moins évident, vous devrez vous poser deux questions :

  • Quel type de projet souhaitez-vous construire ?
  • Quel est votre corps de métier ?

Pour notre part, nous utiliserons de façon significative le XAML et opterons pour le langage C#, assez proche de la norme ECMA (les origines de l'auteur…) sur de nombreux points. JavaScript ne nous servira qu'à l'intégration de Silverlight au sein d'une page HTML. Ce livre s'adresse en partie aux designers Web, dans ce cadre et pour diverses raisons, nous n'aborderons pas Visual Basic, IronRuby, IronPython ou JScript.

Au Chapitre 2 Le couple XAML / C#, afin de commencer notre apprentissage de Silverlight, en douceur, nous ferons un rapide retour sur les bases du langage XML et XAML, ainsi que sur C#. De cette manière, vous les assimilerez plus facilement par la suite.

2. Le couple XAML / C#

L'apparition du XAML constitue l'une des grandes nouveautés de .Net 3. Ce langage repose sur XML. Dans ce chapitre, nous reviendrons donc brièvement sur l'écriture et la grammaire XML. Puis nous verrons en quoi certains processus de création sont facilités grâce à XAML, et comment afficher un document XAML valide au sein d'un navigateur ou d'un éditeur de code simple.

Au même niveau que XAML, le langage C# permet d'ajouter de la logique à nos applications Silverlight. Nous dresserons donc un récapitulatif succinct de l'écriture afin de vous familiariser avec C# et la programmation orientée objet. Si vous développez avec un autre langage, comme JavaScript ou PHP, ou si vous êtes intégrateur, ce chapitre vous concerne. Ainsi, inutile de consacrer des heures à la lecture d'un ouvrage sur C#, dans un premier temps. Si vous êtes vous-même développeur .Net, vous pouvez passer votre chemin ou revoir les fondamentaux du langage.

2-1. XML

Le  XAML repose sur XML. Connaître les règles d'écriture du XML revient donc à respecter celles du XAML. XML est l'acronyme de eXtensible Mark-up Language, c'est un langage déclaratif à balises.

2-1-1. À quoi sert le XML ?

Le XML n'a qu'un seul objectif : présenter d'une manière structurée et universelle des données. Il n'y a pas de vocabulaire lié à ce langage, seule la grammaire est importante. Celle-ci est très simple et ne connaît que quelques règles importantes. XML se veut donc le plus générique possible. Au contraire du XAML qui est conçu pour afficher un visuel et qui possède un vocabulaire spécifique, le XML ne possède pas de vocabulaire spécifique. Écrire ou comprendre la structure d'un fichier XML est à la portée de tous, développeurs ou non. Les noms des balises ne sont donc pas prédéfinis ou figés, mais arbitrairement choisis par le développeur ou le fournisseur de la source XML. Le transit de fichier XML est indépendant du réseau, de la plate-forme ou de la connaissance préalable du fichiers XML. C'est aujourd'hui le format de communication le plus utilisé sur Internet. Vous connaissez sans doute les fameux flux RSS basés sur XML. Les flux RSS ne sont rien d'autre que des fichiers de données XML respectant la norme RSS. C'est pour cette raison qu'un lecteur de flux RSS est capable de lire n'importe quelle source d'où qu'elle provienne tant que la norme RSS est respectée. Voici un exemple de contenu au format XML :

 
Sélectionnez
<DISCOGRAPHIE>
    <INTERPRETE prenom="Patrick" nom="Hernandez">
        <ALBUM annee="1979" titre="Born to be alive">
            <JACQUETTE url=www.amazon.com/images/P/Patrick.jpg />
            <CHANSON>Born to be alive</CHANSON>
            <CHANSON>You Turn me On</CHANSON>
            <CHANSON>It Comes So Easy</CHANSON>
        </ALBUM>
        <ALBUM annee=1980 titre=Crazy day's Mystery nights”>
            
        </ALBUM>
    </INTERPRETE>
        ...
</DISCOGRAPHIE>

Vous remarquez que les données sont structurées suivant des relations familiales. Ainsi la balise INTERPRETE contient plusieurs balises ALBUM qui contiennent elles-mêmes plusieurs balises CHANSON. Les balises ALBUM sont sœurs les unes par rapport aux autres, car elles sont situées sur le même niveau d'imbrication : elles sont toutes contenues par la balise mère INTERPRETE. Nous pouvons donc comparer une structure XML à un arbre généalogique. Toutefois les noms des balises sont arbitraires et ne relèvent pas du langage proprement dit. Nous allons maintenant étudier les règles d'écriture du XML qui s'appliquent également aux langages dérivés tels que XAML.

2-1-2. La grammaire

Le langage XML est constitué de balises et d'attributs. Les balises peuvent être ouvrantes, fermantes ou autofermantes. Les attributs sont définis au sein des balises ouvrantes ou auto-fermantes. Ils sont suivis de l'opérateur d'affectation = et leurs valeurs sont toujours entre guillemets (voir Figure 2.1).

Image non disponible
Figure 2.1 - Structure d'un fichier XML

Bien que les balises soient définies arbitrairement, il existe des règles d'écriture à respecter sous peine que le fichier ne soit pas lisible :

  • la balise xml est proscrite, c'est une balise réservée, utilisée uniquement pour décrire l'encodage du fichier XML ;
  • les balises ne doivent pas commencer par un chiffre ou par un caractère spécial ;
  • le seul caractère spécial autorisé est _ ; les autres ne doivent pas être contenus dans un nom de balise, attention donc à éviter les caractères avec accent dont la langue française fourmille ;
  • il doit y avoir un unique nœud racine ; dans le code XML précédent, la balise racine est DISCOGRAPHIE ;
  • une balise ouverte doit impérativement être suivie d'une balise de fermeture qui commence par "</" ou être elle-même auto-fermante, c'est le cas de la balise JACQUETTE ;
  • les attributs de balise, par exemple annee, ne doivent pas contenir de caractères spéciaux ;
  • les valeurs des attributs de balise doivent toujours être entre guillemets ou entre simples apostrophes.

Vous pouvez également vous référer à des fichiers de type DTD (Document Type Definition). Ceux-ci contiennent une définition de type de document permettant de spécifier certaines règles.

2-1-3. L'ambiguïté des relations familiales

Décider arbitrairement d'une structure de données basée sur des relations familiales peut s'avérer très risqué. Dans un arbre généalogique, la structure familiale représente la vie réelle, il est donc facile de concevoir des données XML basées sur ce modèle. Toutefois, l'exemple de la discographie est intéressant, car de nombreuses méthodes de tri existent dans ce cas. Nous avions classé notre discographie par interprète, puisque les balises INTERPRETE contenaient un ou plusieurs albums. Cependant, nous aurions pu classer les albums au sein de balises GENRE contenant un attribut type avec des valeurs comme funk, rap, disco. Puis, à l'intérieur des balises GENRE, nous aurions pu avoir des balises ALBUM. Les données contenues auraient été les mêmes, mais leur structure aurait été différente. Nous aurions également pu classer les albums par date :

 
Sélectionnez
<DISCOGRAPHIE>
   <DATE annee="1979" month="05">
      <ALBUM titre="Born to be alive">
         <INTERPRETE prenom=Patrick nom=Hernandez />
         <JACQUETTE url=www.amazon.com/images/P/Patrick.jpg />
         <CHANSON>Born to be alive</CHANSON>
         <CHANSON>You Turn me On</CHANSON>
         <CHANSON>It Comes So Easy</CHANSON>
      </ALBUM>
   </DATE>
   <DATE annee=1980>
   </DATE>
      ...
</DISCOGRAPHIE>

Bref, notre première structure aurait pu différer alors que nous avons les mêmes données. Il en est de même pour tous les langages dérivés du XML. Les conséquences pour les fichiers XML peuvent être importantes, car cela peut rendre difficile les opérations de tri et d'accès aux données. Pour les langages comme le XAML, les différentes approches d'imbrication de balises permettent une souplesse de conception unique. Nous allons donc nous intéresser d'avantage au XAML afin de comprendre en quoi ces relations peuvent engendrer des visuels différents et innovants.

2-2. XAML, un langage déclaratif puissant

2-2-1. L'utilité du XAML

Comme nous l'avons expliqué au Chapitre 2 Le couple XAML / C#, le XAML, dérivé de XML, est le nouveau langage apporté par .Net 3. Ce langage permet simplement à un graphiste de formaliser n'importe quel visuel. Créer des éléments graphiques, grâce à ce langage, peut donc être réalisé de deux manières.

  1. La première consiste à écrire du XAML via n'importe quel traitement de texte ou éditeur de code. C'est l'approche que va adopter le développeur. Un outil comme Note-Pad suffit, mais Visual Studio est recommandé, car le développeur bénéficie de l'IntelliSense. Ce concept facilite grandement la programmation en évitant aux concepteurs de connaître le vocabulaire d'un langage dans sa totalité. Cela suppose que le graphiste code le visuel, ce qui n'est pas l'idéal, car concevoir du graphisme de cette manière se révèle être une démarche abstraite et indirecte, qui fait appel à peu de sensibilité. L'apprentissage technique du langage est tel que le coder directement dans un premier temps nuit à la création graphique.
  2. La seconde méthode passe par l'utilisation d'un outil dédié aux graphistes ou aux designers interactifs. Dans ce cas, deux logiciels de la suite Expression répondent à cette problématique : Expression Blend et Expression Design. Avec ces deux logiciels, les graphistes peuvent concevoir le design d'éléments visuels ou d'une application. Ces logiciels ont la capacité de traduire le visuel en XAML. Une fois le design créé, le développeur peut non seulement en avoir un aperçu, mais aussi récupérer le code XAML généré de manière transparente. Si vous travaillez avec des outils tels que Adobe Illustrator ou Photoshop, il est tout à fait possible d'importer les fichiers produits par ces logiciels au sein d'Expression Design ou Expression Blend directement. Dans ce cas, les fichiers psd ou ai sont transformés en bitmap ou au format vectoriel XAML selon le type du fichier importé.
    Le code généré est directement réutilisable au sein d'une production, le développeur ne le modifiera que très peu et laissera la place au designer interactif. Le travail de ce dernier est donc réellement respecté. Voici une liste de ce qui peut être formalisé en XAML par le graphiste :
  3. les tracés vectoriels ;
  4. les couleurs unies ;
  5. les dégradés de couleurs ;
  6. les animations ;
  7. les composants visuels ;
  8. l'agencement d'une interface visuelle ;
  9. les styles de composants ;
  10. les modèles de composants ;
  11. les filtres ;
  12. la 3D.

Comme nous pouvons le voir, le XAML est un formidable outil de communication conçu pour chaque acteur d'une production. L'ergonome, le directeur artistique, le graphiste, le designer interactif, l'intégrateur, le développeur et le directeur technique peuvent participer pleinement à la création d'une application sans empiéter sur le travail des uns ou des autres.

2-2-2. Comparaison XAML / C#

Ces deux langages sont de même niveau. Cela signifie que tout ce qui est créé en XAML peut l'être via C#, bien qu'il ne soit pas adapté à la création graphique. L'inverse n'est pas vrai, XAML est orienté visuel avant tout, ainsi il n'est pas possible de créer d'algorithmes quelconques sans C# ou un autre langage logique, comme VB.

Voici l'écriture d'un bouton en C# :

 
Sélectionnez
Button monBouton = new Button();
monBouton.Width = 156;
monBouton.Height = 80;
monBouton.Content = "Hello";
Canvas.SetTop(monBouton, 50);
Canvas.SetLeft(monBouton, 75);
monBouton.Background = new SolidColorBrush(Color.FromArgb(0xFF, 
                            0xC3, 0x02, 0x02));
monBouton.Click += new RoutedEventHandler(button_Click);
monCanvas.Children.Add(monBouton);

Voici la même écriture en version XAML :

 
Sélectionnez
<Canvas x:Name="monCanvas" >
   <Button x:Name="monBouton" Width="156" Height="80" 
   Content="Bonjour" Canvas.Top="50" Canvas.Left="75" 
   Background="#FFC30202" Click="button_Click" />
</Canvas>
Image non disponible
Figure 2.3 - Visuel d'un bouton généré par XAML avec case à cocher

L'exemple de la Figure 2.3 ne serait pas réellement ergonomique pour l'utilisateur, imbriquer une case à cocher au sein d'un bouton n'a pas de réelle utilité, toutefois cela montre à quel point le XAML s'appuie sur les relations familiales héritées du XML. Vous pourrez donc imaginer toutes sortes d'interfaces visuelles et de nouvelles ergonomies pour vos applications. Ce n'est qu'un aperçu de ce dont le XAML est capable. Vous allez maintenant créer votre premier visuel XAML.

2-2-3. Afficher un visuel XAML

L'exemple précédent est illustratif, mais nous ne bénéficions d'aucun moyen pour le moment d'en avoir un aperçu. Bien sûr, vous pourriez utiliser Expression Blend ou Visual Studio, mais ces logiciels s'inscrivent dans une logique de projet. Vous souhaiterez souvent avoir l'aperçu visuel d'un code XAML sans pour autant créer un projet ou une solution (voir Chapitre 3 Hello World). Pour cela, il existe quelques éditeurs de XAML dont l'un s'appelle Kaxaml. Il a été conçu par Robby Ingebretsen, vous pouvez le télécharger à cette adresse :http://www.kaxaml.com. Comme le dit son concepteur, l'objectif est de vous proposer une IntelliSense poussée tout en fournissant un aperçu de n'importe quel fichier XAML présent sur votre disque dur. Vous trouverez un fichier de démonstration, preview.xaml, dans le dossier chap2 des exemples du livre.

Cependant Kaxaml n'est pas obligatoire. Il existe une autre solution si vous souhaitez envoyer votre travail à un collègue ou à votre directeur artistique, mais que ces derniers ne possèdent pas d'éditeur XAML. En réalité, ils n'en ont pas forcément besoin tant qu'aucun code logique n'est lié au code déclaratif. Tout fichier XAML que vous créez est lisible par votre navigateur, si le lecteur Silverlight est installé, et si ce fichier est correctement formaté. Dans ce dernier cas, le lecteur Silverlight compile directement le fichier XAML à l'exécution et l'affiche dans le navigateur (voir Figure 2.4).

Image non disponible
Figure 2.4 - Fichier preview .xaml directement lisible par Internet Explorer ou Kaxaml

Même si Kaxaml est pratique pour lire des fichiers, il ne permet pas réellement à un graphiste d'élaborer des éléments visuels complexes. Nous utiliserons donc exclusivement Expression Blend et Visual Studio pour concevoir nos projets.

2-2-4. Espaces de noms

Pour visualiser correctement un code déclaratif XAML, vous devez impérativement utiliser les espaces de noms conformes au développement XAML. Voici une portion du code où vous pouvez apercevoir les espaces de noms dans les lignes en gras ; il permet d'afficher la Figure 2.4 :

 
Sélectionnez
<UserControl
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Width="640" Height="480">
   <Grid x:Name=LayoutRoot Background=White>
      <Border Height=134 Width=624 Canvas.Left=8 Canvas.Top=8 
                        CornerRadius=24,24,24,24>
         <Border.Background>
            <LinearGradientBrush EndPoint=0.5,1 StartPoint=0.5,0>
               <GradientStop Color=#FF002352/>
               <GradientStop Color=#FF68A9DE Offset=1/>
            </LinearGradientBrush>
         </Border.Background>
         <Grid Margin=24,20,8,20>
            <TextBlock HorizontalAlignment=Left VerticalAlignment=Top 
            FontSize=24 Foreground=#FFFF 
            Text=Pratique de Silverlight TextWrapping=Wrap/>
            <TextBlock HorizontalAlignment=Left FontSize=16 
            Foreground=#FFFF Text=Conception d'application 
            interactives riches” 
            TextWrapping=”Wrap” Margin=”0,39,0,20” />
         </Grid>
      </Border>
      
   </Grid>
</UserControl>

Vous remarquez que le code précédent répond bien au standard XML. Outre de nombreuses balises permettant de générer du contenu graphique, il s'y trouve un nœud XML racine UserControl. Celui-ci représente le premier objet visuel de l'application Silverlight. Au sein de cette balise, deux attributs nous intéressent particulièrement : xmlns et xmlns:x. Les initiales xmlns signifient XML NameSpace. Ces attributs sont des espaces de noms, c'est-à-dire qu'ils font référence à des dictionnaires nécessaires au développement d'applications basées sur WPF et XAML. Ces espaces de noms doivent toujours se situer dans le conteneur XAML racine. Le premier fait référence à la bibliothèque WPF (Window Presentation Foundation) dont Silverlight émane. C'est l'espace de noms par défaut. Grâce à lui, nous avons accès aux objets complexes de cette bibliothèque, comme Border, Grid ou TextBlock. En interne, cette bibliothèque est basée sur le langage XAML. C'est pourquoi nous avons besoin d'un deuxième espace de noms, xmlns:x, qui fait pour sa part référence au langage XAML proprement dit. À chaque fois que nous ferons référence à cet espace de noms, les balises ou attributs devront être préfixés de x:. C'est, par exemple, le cas de l'objet grille qui contient l'attribut x:Name :

 
Sélectionnez
<Grid x:Name=LayoutRoot Background=White>

Pour adjoindre un comportement logique aux objets visuels, il vous faudra une classe dite partielle. Celle-ci devra être référencée au sein du nœud racine de cette manière (voir Chapitre 3 Hello World pour les classes partielles) :

 
Sélectionnez
<UserControl x:Class="maClasse"  

Cependant, gardez bien à l'esprit qu'un navigateur renverra une erreur d'affichage lors de la lecture directe d'un fichier XAML, si celui-ci référence une classe partielle. Dans ce cas, il vous faudra compiler les fichiers XAML et C# pour en faire un unique fichier xap lisible par le lecteur Silverlight. Nous allons maintenant nous concentrer sur le code logique C# qui contribue largement à la robustesse des applications Silverlight.

2-3. Les fondamentaux du langage C#

Cette section ne concerne que les développeurs non .NET ou les designers interactifs et intégrateurs ayant des bases de programmation dans n'importe quel langage logique (JavaScript, PHP, ActionScript). Si vous êtes développeur .Net expérimenté, vous pouvez passer votre chemin. L'objectif est de présenter et de s'initier aux bases de la programmation orientée objet et de C#, de savoir déclarer des variables et des méthodes et de connaître les types C#.

2-3-1. C# et l'API Silverlight

Le langage C# est sorti dans sa première version avec la plate-forme .Net 1. D'après Microsoft, ce langage est le plus performant en termes de lisibilité et de confort. Il allie la puissance de C++ et la simplicité de Visual Basic. Chaque version de C# est une révolution en soi. Pour ses versions 3 et 4, il a bénéficié d'améliorations visant à assouplir les règles et les méthodologies de développement. L'idée est de permettre des prises de décisions tardives dans les choix d'architecture en intégrant les avantages propres aux langages fonctionnels F# ou JavaScript. Au même titre que Visual Basic, il est géré par la CLR (Common Language Runtime), c'est-à-dire qu'il est tout d'abord compilé en langage intermédiaire avant de l'être en langage machine par le Just In Time Compiler (JITC). C# est un langage orienté objet. Les variables ou méthodes sont fortement typées. Toutefois les types (comme string ou double) n'appartiennent pas au langage lui-même, mais au Common Type System (CTS). Cela signifie qu'un type string en C# ou en Visual Basic est avant tout un type string géré par le CTS propre à .Net. C'est cela qui permet aux développeurs de coder dans différents langages. Le CTS est donc une composante importante de la CLR. Il permet l'interopérabilité des langages.

Le cœur du langage C# est totalement indépendant de l'API Silverlight. Une API est constituée d'une multitude de bibliothèques (ou espaces de noms). L'API (ou framework) Silverlight permet aux développeurs de concevoir des applications Silverlight. L'espace de noms principal est System.
Par exemple, l'une des bibliothèques accessibles dans le framework Silverlight permet de sauvegarder des données sur le poste client local. Il s'agit de la bibliothèque IsolatedStorage. Celle-ci contient plusieurs classes qui permettent d'atteindre cet objectif.

2-3-2. Introduction à la programmation orientée objet

La programmation orientée objet s'oppose à la programmation procédurale. Jusqu'au début des années 1980, avec les langages bas niveau proche de la machine, le code était organisé de manière linéaire : il était lu de haut en bas à la manière d'une procédure que l'on suit. Par la suite, avec des langages comme C++, puis des langages de haut niveau, la programmation orientée objet s'est imposée. La POO part du principe que la plupart des fonctionnalités ou composants d'une application peuvent être décrits sous forme d'objets. Par exemple, si l'on crée une application de gestion de parc automobile, on peut considérer les voitures comme un composant essentiel de l'application, les voitures seront donc envisagées comme des objets. Les objets sont en réalité des exemplaires de modèles d'objets appelés classes. Chaque exemplaire de la classe Voiture sera différencié par les valeurs de ses propriétés et de ses champs (voir la section 2.3.4.6 Différencier les champs et les propriétés). Par convention, on privilégie la notation Pascal où chaque nom de variable ou de méthode commence par une majuscule : MaFonction, MaPropriété, MonChamp. Pour les paramètres de méthode, on utilise plutôt la notation Camel : monParametre. Le modèle de Voiture, appelé classe, permet de décrire tout ce que peut faire une voiture, ainsi que tout ce qui la définit. Le mot-clé class permet de créer une nouvelle classe. Voici en C# comment générer une classe Voiture :

 
Sélectionnez
class Voiture {
   //champs de la classe voiture
   public string Marque;
   public string Modele;
   public string Imat;
   public double Longueur;
   public string TypeCarburant;

   //méthode qui permet à la voiture de rouler
   public void Roule(){
      // permet de tracer ‘la voiture roule' dans une console
      Console.WriteLine("la voiture roule");
      //permet de laisser la console affichée
      Console.ReadLine();
   }

   //méthode qui permet à la voiture de s'arrêter
   public void Stop(){
      Console.WriteLine("la voiture stoppe");
      Console.ReadLine();
   }
}

Comme nous le voyons, tous les exemplaires émanant de la classe auront des champs et des méthodes (ou fonctions) propres au modèle Voiture. Toutefois, les valeurs affectées aux propriétés et aux champs pourront être différentes pour chacun d'eux, c'est cela qui permet de les différencier. Toutes les voitures ont une propriété couleur, mais celle-ci pourra posséder plusieurs valeurs : rouge, verte ou bleue, selon l'exemplaire. Le mot-clé new permet de créer un nouvel exemplaire de la classe Voiture. Voici comment vous pouvez créer des exemplaires (également appelés instances) du modèle Voiture :

 
Sélectionnez
//voici un exemplaire de la classe Voiture
Voiture MaPorsche = new Voiture() { Marque="Porsche", 
        Longueur=1.95,Modele="Carrera 4", TypeCarburant="essence"};
// voici un autre exemplaire
Voiture MaSuper5 = new Voiture() { Marque = "Renault", 
        Longueur = 1.2, Modele = "Super5", TypeCarburant = "diesel" };

On se rend bien compte que chaque voiture instanciée possède les mêmes champs mais que ceux-ci contiennent des valeurs différentes. La déclaration et l'affectation de variables seront abordées à la section 2.3.4 Les types.

2-3-3. Une première application en mode console

Nous allons maintenant mettre en pratique notre apprentissage récent en créant une petite application en mode console. Ouvrez Visual Studio 2008 et choisissez Fichier > Nouveau projet C# > Application console. Entrez le nom de solution : ParcAuto. Voici le code que Visual Studio génère par défaut :

 
Sélectionnez
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ParcAuto
{
   class Program
   {
      static void Main(string[] args)
      {
      }
   }
}

Toute application console possède une classe par défaut qui s'appelle Program, contenue dans un espace de noms correspondant au nom du projet et celle-ci contient une méthode qui s'exécute par défaut : Main. Nous allons tracer un message dans la console Windows. Pour cela, ajoutez les lignes suivantes au sein de la méthode Main :

 
Sélectionnez
static void Main(string[] args)
{
   Console.WriteLine(";Bonjour tout le monde !!);
        //ceci trace le message
   Console.ReadLine();
        //ceci empêche la fermeture de la console
}

Pour compiler notre première application et l'exécuter, rien de plus simple : appuyez sur la touche F5. Lorsque vous utilisez ce raccourci, vous lancez le mode Debug. Ce dernier vous permettra de corriger et de comprendre les dysfonctionnements de votre application lorsque vous compilerez ou durant son exécution. Si vous n'utilisez pas ce mode, votre application échouera en silence si elle contient des erreurs. Pour compiler et exécuter sans déboguer, il suffit d'appuyer sur les touches Ctrl+F5. Si vous souhaitez uniquement compiler sans lancer votre application, vous pouvez utiliser le raccourci Ctrl+Maj+F5.

2-3-4. Les types

2-3-4-1. Variables et champs d'objets

Une variable permet de stocker des données. Voici comment déclarer une variable au sein d'une classe, et dans ce cas, la variable est un champ d'objet (en C#, on différencie champ de propriété) :

 
Sélectionnez
class Program
{
   int monEntier = 13;
}

Vous remarquez que le type est toujours précisé en premier, dans notre cas int signifie entier. Il vous faut, cependant, faire la différence entre la déclaration et l'affectation. Ce sont deux étapes distinctes et l'affectation peut être réalisée plus tard. Nous aurions tout aussi bien pu écrire :

 
Sélectionnez
class Program
{
   int monEntier;
   monEntier = 13;
}

Il arrivera souvent de déclarer une variable sans pour autant l'affecter. Par exemple, dans un jeu, la variable score ne sera pas affectée dès son initialisation mais durant l'exécution du programme. Lorsqu'une variable de type entier est déclarée sans être affectée, sa valeur par défaut est 0. Ce comportement est dû au fait que le type int est un type primitif numérique.

2-3-4-2. Les types primitifs

Il est nécessaire de typer les variables, d'une part par obligation due au langage, et d'autre part parce que cela permet d'améliorer les performances de vos applications. Le choix du type est très important dans ce contexte, car c'est lui qui déterminera l'occupation mémoire allouée par défaut pour stocker la valeur. Parmi tous les types existants, on trouve les types primitifs. Ceux-ci acceptent des valeurs simples. Ils sont à la base de tous développements, sans eux aucune conception ne verrait le jour. Ci-dessous quelques types primitifs importants.

  • sbyte : il contiendra une valeur entière comprise entre -128 et 127. Son nom vient du fait que la valeur pourra être négative (s pour signed) et que le nombre de possibilités correspond à un octet soit 28 (256 chiffres). Il ne faudra pas confondre Byte, en anglais, et bit, qui signifie 0 ou 1.
  • byte : ce type peut accepter un entier entre 0 et 255.
  • uint : il accepte un entier entre 0 et 232, soit 4 294 967 295 possibilités. Le u de uint signifie que la valeur sera toujours non signée (u pour unsigned), donc toujours positive.
  • int : il représente le même nombre de possibilités mais contient la moitié des chiffres en dessous de 0. Soit entre -2 147 483 648 et 2 147 483 647.
  • decimal : il contient des valeurs à virgule. La plage des valeurs admises se situe entre 10-28 et 7.9x1028.
  • double : ce type contient également des valeurs à virgule. Comme pour decimal le stockage des données est assez complexe, mais le nombre de valeurs acceptées est beaucoup plus grand : entre 5x10-324 et 1.7x10308.
  • bool : il est le digne représentant de la logique de Mr George Boole. Ce mathématicien britannique est à l'origine d'une révolution des mathématiques et de la philosophie au XIXe siècle. On en trouve aujourd'hui de nombreuses utilisations dans l'industrie ou dans le secteur informatique. Ce type ne peut contenir que deux valeurs : vrai ou faux (true ou false) ; le bit de donnée repose sur cette logique.
  • string : il représente une chaîne de caractères. La valeur est toujours spécifiée entre guillemets. La chaîne de caractères contenue peut-être infinie, la mémoire utilisée sera allouée au fur et à mesure des besoins. Toutefois le type string n'est pas comme les autres types simples (voir la section Le tas et la pile Le tas et la pile dans ce chapitre).

2-3-4-3. Les types complexes

Stocker des valeurs simples ne suffit pas. Bien souvent, vous aurez besoin d'une variable ayant la capacité de contenir plusieurs données. Voici les types complexes :

  • Les énumérations notées enum. Elles vous permettent de stocker un choix de valeurs arbitrairement. Si l'on reprend l'exemple de la voiture, nous pourrions par exemple proposer une énumération pour le type de carburant au lieu d'une chaîne de caractères. Nous pourrions écrire ceci :

     
    Sélectionnez
    class Program{
       Voiture MaDeLorean = new Voiture() { TypeCarburant = 
            Carburant.Detritus, Marque = ";DeLorean, Modele = ";volante” };
    }
    enum Carburant{
       Diesel,
       Essence,
       propane,
       Detritus,
       Ethanol
    }
    class Voiture{
       public string Marque;
       public string Modele;
       public Carburant TypeCarburant;
    }
  • Vous remarquez que l'énumération est en dehors de la classe, elle se suffit à elle-même. Elle pourrait également en faire partie. Au sein de la classe Voiture, nous déclarons un nouveau champ public de type Carburant. Le mot-clé public est un modificateur d'accès, nous verrons les modificateurs d'accès plus loin dans ce chapitre. L'affectation se produit au niveau de la classe Program, à l'instanciation d'une nouvelle voiture grâce à cette ligne :

     
    Sélectionnez
    Voiture MaDeloreane = new Voiture() { TypeCarburant = 
       Carburant.Detritus, Marque = ";DeLorean, Modele = ";celle qui vole” 
    };
  • Il faut savoir qu'une valeur d'énumération possède toujours un type sous-jacent. Par défaut ce type est int mais vous pourriez le changer.

  • Les structures notées struct. Il s'agit de l'équivalent d'une classe, mais elle est gérée différemment en mémoire. Il faut envisager une structure comme une définition d'objets. Dans la majorité des cas, ce seront des objets simples. Par exemple, un point géographique est défini grâce à deux coordonnées : la latitude et la longitude, toutes deux exprimées en degrés. Nous pourrions ainsi définir la position de notre voiture grâce à une structure qui s'appellerait PositionGPS :

     
    Sélectionnez
    class Program{
       //ceci est une écriture pratique 
       //pour créer un nouvel exemplaire de voiture
       Voiture MaDeloreane = new Voiture(){
          TypeCarburant = Carburant.Detritus,
          Marque = "DeLorean",
          Modele = "volante",
          MyPlace = new PositionGPS() { DegreLat=180, DegreLong=176 }
       };
    }
    struct PositionGPS{
       public int DegreLat;
       public int DegreLong;
    }
    class Voiture{
       public string Marque;
       public string Modele;
       public Carburant TypeCarburant;
       public PositionGPS MyPlace;
    }
  • Les tableaux notés Array et []. Ce sont des objets différents de ceux que nous venons de citer en cela qu'ils sont faits pour stocker de multiples données sous forme d'éléments indexés numériquement à partir de 0. Il faudra toujours préciser le type des objets stockés. Pour initialiser un tableau, il existe deux méthodes. La première consiste à utiliser le mot-clé new et à indiquer le nombre d'éléments qu'il pourra contenir. Dans d'autres langages comme Java ou Action-Script 3, ce type correspond à la classe Vector (vecteur). Voici un exemple d'instanciation :

     
    Sélectionnez
    Voiture[] MesVoitures = new Voiture[3];
  • Par la suite, on affecte une voiture à chaque index numérique du tableau comme ceci :

     
    Sélectionnez
    MesVoitures[0] = MaDeLorean;
    MesVoitures[1] = MaPorsche;
    MesVoitures[2] = MaSuper5;
  • Voici la seconde méthode appelée "écriture littérale" :
 
Sélectionnez
Voiture[] MesVoitures = { MaDeloreane, MaPorsche, MaSuper5 };

Il nous suffit maintenant d'accéder aux voitures du tableau. Pour cela, nous pouvons utiliser une boucle qui nous permettra de tracer le nom de chaque voiture dans une console Windows. La boucle foreach est la mieux adaptée à ce type de situation. Voici comment l'utiliser :

 
Sélectionnez
void TraceMesVoitures(){
   //Pour chaque élément de type Voiture dans le tableau MesVoitures
   foreach(Voiture V in MesVoitures){
      //cette ligne permet d'afficher le résultat dans une Console
      Console.WriteLine("j'ai une {0}", V.Marque);
   }
   //celle-ci permet de figer l'affichage de la console pour la lecture
   Console.ReadLine();
}

Concrètement, la boucle parcourt le tableau MesVoitures, récupère un exemplaire de Voiture à chaque index et trace une phrase en fenêtre de sortie. Cette boucle s'arrête d'elle-même lorsque le tableau est entièrement parcouru. Les tableaux possèdent la propriété length qui permet de connaître le nombre d'éléments contenus en leur sein. L'objet List, en C#, est plus souvent utilisé que l'objet tableau (Array) en raison des nombreuses capacités qu'il offre. Un tableau possède une longueur (ou nombre d'éléments) fixe. La classe List possède pour sa part des méthodes lui permettant d'ajouter ou de supprimer des éléments sans avoir besoin d'initialiser la longueur. Vous la trouverez dans l'espace de noms System.Collections.-Generic.

2-3-4-4. Les modificateurs d'accès

Vous avez sans doute remarqué le mot-clé public présent devant la déclaration de certains membres de classe. C'est un modificateur d'accès. Ces derniers permettent de rendre accessibles ou non des champs, des propriétés et des méthodes de classe. Le mot-clé public permet de rendre un membre de classe accessible depuis l'extérieur de la classe. C'est cela qui nous a permis d'instancier des voitures tout en initialisant leurs champs "Public" depuis l'extérieur. Voici un cas concret :

 
Sélectionnez
class Program{
   //ceci est une écriture pratique 
   //pour créer un nouvel exemplaire de voiture
   Voiture maDeLorean = new Voiture();
   maDeLorean.marque = "DeLorean";
}
class Voiture{
   public string marque;
   Guid numeroSerie = new Guid();
}

La propriété numeroSerie n'est définie avec aucun modificateur d'accès. En fait, lorsque rien n'est précisé, c'est le modificateur private qui est utilisé à la compilation pour un membre de classe. Pour une classe ce sera le mot-clé internal. Un champ de classe, défini avec private, n'est pas accessible en dehors de la classe, mais seulement en son sein. La meilleure pratique consiste toujours à laisser le moins de champs ou de méthodes visibles depuis l'extérieur. Votre classe doit toujours être fermée à la modification et toujours ouverte à l'extension. Seules les propriétés et méthodes utiles doivent être accessibles à celui qui instancie ou utilise votre classe. Dans le cas précédent, il est logique que le numéro de série reste privé et ne soit pas accessible au quidam de base. Le type Guid est en fait une valeur unique, car le nombre de possibilités est quasi illimité. De plus, on affecte la valeur lors de l'instanciation de la classe Voiture. Voici la liste des modificateurs d'accès utilisables :

  • public : permet l'accès depuis n'importe quel endroit du code ;
  • private: définit un membre de classe accessible uniquement depuis la classe contenant ce membre ;
  • internal : autorise l'accès à toutes les classes de l'espace de noms dans lequel est la classe et aux espaces de noms amis ;
  • protected : permet l'accès au code situé au sein de la classe ou dans une de ses sous-classes.

2-3-4-5. Conversion de type implicite et explicite

Il vous arrivera souvent dans un langage orienté objet de transformer le type d'une valeur en un autre type. Catégoriser des objets est une démarche qui permet d'optimiser les performances des applications. Toutefois catégoriser est une démarche rigide, même dans la vie courante. Il est toujours dangereux et rarement vrai à long terme qu'un objet soit d'un seul type. La classification des espèces, bien que nécessaire d'un point de vue scientifique, est remise en question à chaque fois qu'une espèce sortant du cadre est découverte. L'ornithorynque est le cas le plus flagrant qui démontre que classifier des espèces est hasardeux. Celui-ci pond des œufs, allaite ses petits, possède un bec de canard, s'appuie sur des pattes aux cinq orteils palmés. Bref, il regroupe de nombreux aspects que l'on croyait uniques au mammifères, aux reptiles et aux ovipares. Il ne rentre dans aucune catégorie simple et présente certains aspects de plusieurs d'entre elles (depuis la théorie de Darwin, la classification des espèces a évolué, il s'agit bien ici d'un exemple mettant en évidence la dangerosité de cette manière de raisonner). Pour cette raison et de nombreuses autres, il vous arrivera donc souvent, si vous commencez la POO, d'utiliser un type à la place d'un autre ; d'avoir un jour besoin d'un entier à la place d'un décimal ou de transformer un booléen en chiffre. Pour transformer un type en un autre type, vous utiliserez une opération de conversion implicite ou explicite appelée transtypage ou casting en anglais.

La conversion implicite est réalisée par le compilateur sans que vous ayez besoin de lui indiquer un mot-clé. Pour que le compilateur puisse réussir cette opération, il suffit juste que le type de destination accepte la valeur que l'on souhaite définir avec ce type. Par exemple, il est facile de transformer un type byte en type uint, car un byte est toujours positif et la plage de valeurs possibles est contenue dans la plage de valeurs possibles de uint :

 
Sélectionnez
int MonEntier;
byte MonByte = 113;
MonEntier=MonByte; 
// Dans ce cas, le type de b sera converti en int à la compilation, 
// ce qui est faisable, puis a recevra la valeur de b.

Il est donc très facile de déduire les conversions implicites pour les types primitifs numériques. Le deuxième type de transtypage est un peu plus délicat à manier, car il vous faudra préciser vous-mêmes la conversion souhaitée. Nous allons reprendre le même exemple, mais en affectant la valeur de type byte à un entier noté int :

 
Sélectionnez
byte MonByte2;
int MonEntier2 = 113;
MonByte2 = (byte)MonEntier2;
Console.WriteLine(";type de MonByte2 :: "; + MonByte2.GetType());

Dans ce cas, vous devrez mettre le type de destination entre parenthèses devant la valeur à convertir. Tout type possède une méthode GetType renvoyant le type. Si jamais vous oubliez de convertir explicitement, Visual Studio vous surligne la valeur affectée - dans notre cas, cela serait mon-Entier2. Lorsque vous passez la souris au-dessus du surlignage, Visual Studio vous indique que vous avez peut-être oublié une conversion et vous précise qu'une conversion explicite existe. Cela n'est pas toujours le cas.

Que se passe-t-il si jamais une valeur numérique sort de la plage de valeur admise par le type de destination ? Par exemple, que se passerait-il si monEntier3 était égal à 312 ? Le résultat correspond à la valeur à convertir, modulo (%) la longueur de la plage des valeurs du type de destination. Modulo est un opérateur mathématique qui permet de calculer le reste d'une division entière. Dans notre exemple "312 modulo 256" nous renverra 56. 312 divisé par 256 est égal à 256 divisé par 256 (qui est nombre entier) plus 56 divisé par 256. 56 est bien le reste de la division. Ce sera donc la valeur affectée à notre variable de type byte :

 
Sélectionnez
int MonEntier3 = 312;
byte MonByte3 = (byte)MonEntier3;
Console.WriteLine(";type de MonByte3 :: "; + MonByte3.GetType() + "; - 
                    valeur :: "; + MonByte3);

Un autre moyen de convertir explicitement une valeur est d'utiliser la classe Convert. Celle-ci possède des méthodes de conversion. Nous pourrions par exemple écrire :

 
Sélectionnez
byte MonByte4;
int MonEntier4 = 312;
MonByte4 = Convert.ToByte(MonEntier4);
Console.WriteLine(";type de MonByte4 :: "; + MonByte4.GetType());

L'avantage de cette méthode est qu'elle génère par défaut une erreur à l'exécution si jamais la valeur à convertir ne correspond pas à la plage du type de destination. Dans l'exemple précédent, nous aurons donc une erreur levée.

2-3-4-6. Différencier les champs et les propriétés

Jusqu'à maintenant nous n'avons utilisé que des champs de classe. Une propriété est un membre de classe stockant une valeur au même titre qu'un champ de classe. Cependant l'avantage d'une propriété est qu'elle permet l'ajout de logique lorsqu'elle est mise à jour ou lue. Définir une propriété est simple avec Visual Studio, il suffit de taper prop, suivi de deux tabulations pour générer automatiquement le code :

 
Sélectionnez
public string modele { get; set; }

Ceci est la nouvelle écriture écourtée des propriétés depuis C# 3. Vous remarquez le mot-clé get qui signifie obtenir et le mot-clé set qui signifie établir. Ces termes représentent deux méthodes qui vont nous permettre de contrôler les accès à notre propriété. En règle générale, une propriété est toujours accompagnée d'un champ privé, donc non accessible, qui est mis à jour par ces deux méthodes. Nous allons maintenant ajouter un champ privé et un peu de logique dans notre propriété :

 
Sélectionnez
class Program
{
   static void Main(string[] args)
   {
      Voiture maVoiture = new Voiture();
      Console.WriteLine(";modele de la voiture :: "; + maVoiture.modele);
      Console.ReadLine();

   }
}

class Voiture{
// ceci est le champ privé modele de type chaîne de caractères
   private string _modele;
   public string Modele 
   { 
      get{ // on met la logique concernant la lecture de la propriété
           //si le modèle est défini alors je renvoie le nom du modèle
         if ( _modele.Length > 0) return _modele;
         else return "N.C."; //si non je renvoie Non Communiqué
      } 
      set{  // on met la logique concernant l'écriture de la propriété
            // si la valeur que l'on essaie d'établir n'est pas nulle
           // alors on affecte la nouvelle valeur du modèle
         if (value.Length > 0) _modele = value;
      }
   }
}

Par défaut, une propriété générée par Visual Studio est publique. Si vous souhaitez créer une propriété uniquement accessible en lecture, il suffit de supprimer la méthode set. Vous pouvez également préciser un modificateur d'accès devant le mot-clé set, ce qui est plus élégant et avantageux. Le modificateur d'accès internal permettra par exemple d'accepter l'écriture de cette propriété uniquement aux objets contenus dans l'espace de nom ; protected permettra quant à lui de laisser l'accès en écriture aux classes héritant de Voiture.

2-3-4-7. Le tas et la pile

On répartit les types en deux grandes catégories : les références attribuées sur le tas et les valeurs attribuées sur la pile. Concrètement, il s'agit de deux manières différentes de stocker des données. La pile représente l'espace mémoire alloué pour les valeurs, alors que le tas représente l'espace mémoire gérant les références. La gestion de ces espaces mémoire est très différente. L'allocation mémoire est figée dans le cas de la pile mais peut être variable sur le tas. Vous pouvez considérer les références comme des variables qui pointent vers une adresse mémoire, l'affectation sera contenue à cette adresse. Le Tableau 2.1 vous présente ces types.

Tableau 2.1 - Types gérés par la pile et le tas.

Valeurs attribuées sur la pile

Références gérées par le tas

byte/sbyte

array

short/ushort

classe personnalisée

int/uint

string

long/ulong

interface

boolean

list

double, float,decimal

observableCollection

char

 

énumération

 

structure

 

Vous remarquez que le type string est géré sur le tas. Chaque modification d'une chaîne de caractères entraîne une allocation mémoire et donc des accès coûteux en terme de performances. Il faudra utiliser la classe StringBuilder qui permet d'alléger les accès mémoire.

Différencier une référence d'une valeur est assez simple. Il faut se rappeler qu'une référence est une variable qui pointe à une adresse mémoire. Commençons par définir une valeur de type structure allouée sur la pile :

 
Sélectionnez
struct PositionGPS{
   public int DegreLat;
   public int DegreLong;
}
PositionGPS Gpa = new PositionGPS { DegreLat = 141, DegreLong = 76 };
PositionGPS Gpb = Gpa;
Gpa.DegreLat = 35;
Console.WriteLine(Gpb.DegreLat);

La console affiche 141, la structure gpb ne change pas, car nous avons procédé à une simple recopie de valeurs. Les deux variables ciblent un espace mémoire différent. Elles sont donc autonomes. Examinons le même code en remplaçant les structures par des classes personnalisées. Celles-ci sont sont attribuées sur le tas :

 
Sélectionnez
class Point{
   public int X;
   public int Y;
}
Point P1 = new Point(){ X = 141, Y = 76 };
Point P2 = P1;
P1.X = 35;
Console.WriteLine(P2.X);

Cette fois, la console affiche 35, la valeur du champ X de P2 a changé, car l'instance P2 pointe vers la même allocation mémoire que P1. On procède à une recopie de pointeur dans le cas d'une affectation de variables de type référence. P2 et P1 ciblent le même espace mémoire.

2-3-4-8. Les inférences de type

Un nouveau mot-clé est apparu avec C# 3 : var. Ce mot-clé vous évitera de préciser le type d'une variable lors de sa déclaration. Cela signifie que le type d'une variable déclarée avec var n'est fixé qu'au moment de l'affectation de la variable. Cette capacité est très pratique si vous ne connaissez pas à l'avance la valeur d'affectation. Toutefois le mot-clé var n'est pas utilisable pour déclarer des membres de classes, il sert essentiellement à définir des variables locales aux méthodes.

2-3-4-9. Les types anonymes

Les types anonymes ont un peu le même rôle que les structures. Comme pour celles-ci, l'objectif majeur est de créer un objet ne contenant que des propriétés (bien que les structures puissent faire mieux). C'est une reprise directe de ce que vous trouverez dans les langages dynamiques fonctionnels. Ces types anonymes ne peuvent être définis et affectés qu'au sein d'une méthode, grâce au mot-clé var. Il s'agit donc de variables locales. Voici comment créer une classe anonyme :

 
Sélectionnez
var MaVoitureAnonyme = new { Modele="307", Marque="Peugeot", 
                    Prix=8950, Annee=2004 };

En réalité, le typage fort est présent, mais une classe anonyme, ainsi que les champs qui la composent, sont créés dynamiquement.

2-3-5. Déclarer des méthodes

2-3-5-1. Définition et appel de méthodes

Une méthode est une fonction appartenant à une classe. Son premier objectif est de centraliser la mise en œuvre d'une série d'instructions. Celle-ci sera exécutable à l'infini par simple appel du nom de la méthode, suivi de deux parenthèses (ouverte et fermée). Quels que soient les langages, les fonctions ou méthodes permettent la réutilisation du code. En langage C#, les fonctions globales qui n'appartiennent à aucune classe n'existent pas. C'est un abus de langage de dire qu'une méthode statique de classe (donc appartenant et exécutable par la classe) est une fonction globale. A contrario de C#, les fonctions sont considérées comme des objets dans les langages dits fonctionnels. Dans ces langages, dont JavaScript fait partie, une fonction peut exister par elle-même (car elle est considérée comme un objet). Ce n'est pas le cas en C#. Deux types de méthodes existent, les méthodes exécutables par la classe et les méthodes invoquées par les exemplaires ou instances de classe. Il faut tout d'abord définir une méthode pour l'invoquer par la suite. Voici une définition et un appel de la méthode rouler, propre aux exemplaires de la classe Voiture :

 
Sélectionnez
class Voiture{
   public void Rouler(){
      Console.WriteLine("La voiture roule 100 km");
   }
}
class Program{
   static void Main(string[] args){
      //On invoque la méthode rouler sur l'instance de Voiture MaDeLorean
      MaDeLorean.Rouler();
      Console.ReadLine();
      //la console tracera le message définit au-dessus
   }
}

Comme vous pouvez le constater, le nom de la méthode est d'abord précédé du mot-clé public (voir "Les modificateurs d'accès"), puis du mot-clé void qui fait référence au type muet. Cela signifie que la méthode ne retourne aucune valeur. Dans le cas d'une méthode renvoyant une valeur, il suffit de préciser le type à la place de void. On pourrait déduire que Rouler modifie la position de la voiture et renvoie du dioxyde de carbone en même temps. Voici le même exemple mis à jour :

 
Sélectionnez
public class Voiture{
   public int Rouler(){
      Console.WriteLine("La voiture roule 100 km ")
      return 125;
   }
}
class Program{
   static void Main(string[] args){
      Voiture MaDeLorean = new Voiture(){
         TypeCarburant = Carburant.Detritus,
         Marque = ";De Loreane, Modele = ";volante”,
         MyPlace = new PositionGPS() { DegreLat = 180, DegreLong = 176 }
      };
      //On invoque la méthode rouler sur l'instance MaDeLorean 
      Console.WriteLine(";Elle rejette :: "; + MaDeLorean.Rouler() + 
                           "; g CO2 / Km);
      Console.ReadLine();
      //la console tracera le message défini dans la méthode,
      //puis le message défini dans la méthode Main
    }
}

Comme nous l'avons déjà dit, la méthode Main de la classe Program est une méthode d'initialisation, c'est le point d'entrée de notre application console, c'est-à-dire que celle-ci sera exécutée dès le lancement de l'application par la CLR. C'est pour cela que sa définition commence par le mot-clé static. Les méthodes static présentent de nombreux avantages. Nous pourrions par exemple créer une méthode de ce type pour la classe Voiture, qui renverrait le nombre de voitures en circulation.

2-3-5-2. Les paramètres de méthode

Nous allons maintenant améliorer la méthode rouler en lui définissant un paramètre. Comme nous l'avons vu au début de ce chapitre, une méthode doit faciliter la réutilisation(1). Toutefois, on constate que notre méthode rouler ne permet à une voiture de rouler que 100 km. Il serait utile de pouvoir choisir le nombre de kilomètres à chaque appel de cette méthode. Pour cela, il nous faut ajouter un paramètre dans la définition de cette méthode :

 
Sélectionnez
class Voiture
{
   public string Marque;
   public string Modele;
   public Carburant TypeCarburant;
   public PositionGPS MyPlace;
   public int Rouler( int nombreKilometre)
   {
      Console.WriteLine(";La voiture roule + nombreKilometre + ";km”);
      return 125;
   }
}

Puis, nous invoquons celle-ci en lui passant le nombre de kilomètres attendus :

 
Sélectionnez
static void Main(string[] args)
{
   //On invoque la méthode rouler sur l'instance maDeLorean 
   Console.WriteLine("Elle émet :: " + MaDeLorean.Rouler(113) + 
                         " g CO2 / km");
   Console.ReadLine();
   //la console tracera le message défini dans la méthode en 
   //prenant en compte le nombre de kilomètres parcourus 
}

2-3-5-3. Le tableau de paramètres

Dans certains cas, vous aurez besoin de passer un nombre variable de paramètres. C# propose pour cela une signature de fonction spécifique. Il vous faudra utiliser le mot-clé params entre les parenthèses en précisant le type des paramètres reçus. Le terme params représente le tableau des paramètres qui ont été passés lors de l'appel de la méthode. Si vous ne souhaitez pas préciser de type, il suffit de préciser object. Toutes les classes en C# héritent de object, ainsi vous pourrez spécifier n'importe quel type de paramètre. Dans l'exemple suivant, nous calculons le prix moyen d'une voiture :

 
Sélectionnez
static void Main(string[] args){
   Voiture MaDeLorean = new Voiture(){
      Prix=150000,
      TypeCarburant = Carburant.Detritus,
      Marque = "De Loreane",
      Modele = "volante",
      MyPlace = new PositionGPS() { DegreLat = 180, DegreLong = 176 }
   };
   Voiture MaPorsche = new Voiture() { Marque = "Porsche", 
            Modele = "Carrera 4", TypeCarburant = Carburant.Essence, 
            Prix=18500 };
   Voiture MaSuper5 = new Voiture() { Marque = "Renault", 
            Modele = "Super5", TypeCarburant = Carburant.Diesel, 
            Prix=1300 };
   //on trace directement le retour de la méthode CalculPrixMoyen
   Console.WriteLine(";prix moyen des voitures :: {0}, CalculPrixMoyen 
  (MaDeLorean, MaPorsche, MaSuper5) );
}
//on utilise le tableau de paramètre et on précise un type de retour
//car cette fonction renvoie la moyenne calculée
private static decimal CalculPrixMoyen( params Voiture[] mesVoitures){
   decimal Total = 0;
   foreach (Voiture v in mesVoitures){
      Total += v.Prix;
   }    
   return Total / mesVoitures.Length;
}

2-3-5-4. Portée de variables

Les variables que vous définissez au sein d'une méthode ne sont accessibles qu'à l'intérieur de celle-ci. Ce sont des variables locales à la méthode. À la fin de l'exécution et dans le cas de variables de type valeur, celles-ci libèrent la mémoire qui leur est allouée. Ces variables ne sont donc pas accessibles depuis l'extérieur. La variable locale total de l'exemple précédent est donc inaccessible dans la méthode main. Cependant, vous pouvez très bien décider d'affecter une propriété ou un champ de classe. Ils peuvent être atteint depuis n'importe quel endroit au sein de cette classe mis à part au sein de membres ou propriétés statiques.

2-3-5-5. Les méthodes d'extension

Comme nous l'avons dit à plusieurs reprises, C# s'oriente vers les langages dynamiques. Les méthodes d'extension sont un nouveau pas dans cette direction. Celles-ci permettent d'ajouter de manière propre et efficace des méthodes à n'importe quel type. Une méthode d'extension doit toujours être static et public et appartenir à une classe static. Tout se passe au niveau de la signature de la méthode dont voici un exemple simple :

 
Sélectionnez
static class MyExensionsMethods {
   public static bool IsBiggerThan (this int myInt, int compare)
   { 
      return myInt > compare; 
   }
}

Vous remarquez que le premier paramètre commence par le mot-clé this. Cela signifie qu'il fait référence à la variable de type entier qui va faire appel à la méthode. Le second paramètre est le premier paramètre de la méthode IsBiggerThan lorsque celle-ci sera appelée. Voici comment se déroule l'appel :

 
Sélectionnez
int monEntier = 37;
bool myBoolean = monEntier.IsBiggerThan( 13 );

2-3-6. Héritage et implémentations

2-3-6-1. Principes

Un des grands axes de la programmation orientée objet des années 80 à 90 est la capacité des classes à hériter d'autres classes. Dans l'application d'exemple, peut-être souhaitez-vous louer des voitures de type Utilitaire. Dans ce cas, il n'est pas nécessaire d'écrire une classe Utilitaire en reprenant chaque champ de la classe Voiture. Nous pouvons simplement considérer que la nouvelle classe Utilitaire héritera de la classe Voiture. Dans ce dernier cas, nous n'aurons qu'à le préciser au moment de sa définition comme ceci :

 
Sélectionnez
class Utilitaire : Voiture{
   public int Volume;
   public void Charger(){
      Console.WriteLine("on charge l'utilitaire");
      Console.ReadLine();
   }
   public void Decharger(){
      Console.WriteLine("on décharge l'utilitaire");
      Console.ReadLine();
   }
}

Le grand avantage réside dans le fait que nous n'avons pas à recoder une nouvelle classe entièrement. De ce point de vue, l'héritage en POO est une technique de développement qui permet la réutilisation. La sous-classe de Voiture (Utilitaire) bénéficie de tous les comportements et propriétés d'une voiture normale en plus des siennes. Voici comment créer une instance de la classe Utilitaire :

 
Sélectionnez
//une classe héritée de Voiture
Utilitaire MonKangoo = new Utilitaire() { Marque = "Renault", 
Longueur = 1.2, Modele = "Super5", TypeCarburant = "diesel", Volume=6 };

La problématique n'est cependant pas si simple, car imaginez un véhicule de type 4x4. Nous pourrions l'envisager comme un utilitaire ou un véhicule familial. Doit-il hériter dans ce cas de la classe Voiture ou de la classe Utilitaire ? Il n'y a malheureusement aucune bonne réponse à cette question en utilisant l'héritage, car il est impossible d'hériter de plusieurs classes en C#. Nous avons donc là un problème de conception important : n'oubliez pas qu'une fois la décision prise, vous ne pourrez plus revenir en arrière facilement. Votre développement subira à long terme les décisions que vous aurez pris trop tôt face à la nécessité de commencer le développement. Bien que de nombreuses techniques permettent de concevoir un code souple à la modification, la programmation objet tente de classifier les fonctionnalités par type, ce qui ne correspond pas toujours à une réalité. Les langages dynamiques permettent la prise de décisions tardives, mais aussi de rectifier simplement des choix de conception. Plus vous décidez tard, meilleure est votre appréciation de la situation et des besoins. Nous verrons que C# s'oriente dans cette direction depuis sa version 3.

2-3-6-2. Surcharge de méthodes

Comme nous l'avons vu, toutes les classes de C# héritent de la classe object. Celle-ci possède une méthode ToString qui permet d'avoir une représentation, sous forme de chaîne de caractères, de l'instance sur laquelle elle est invoquée. Les classes C# ont pour la plupart une implémentation différente de cette méthode. Les classes personnalisées dont Voiture fait partie, implémentent par défaut la méthode ToString propre à object. Utilisons cette méthode sur notre classe Voiture et voyons le résultat :

 
Sélectionnez
Console.WriteLine( MaDeLorean.ToString());
//renvoie ParcAuto.Voiture

Comme vous le constatez, la méthode retourne par défaut l'espace de noms et le type Voiture. Il peut être utile d'avoir un retour personnalisé pour la classe Voiture. Nous allons donc redéfinir notre propre méthode ToString, il suffit d'utiliser le mot-clé override :

 
Sélectionnez
public override string ToString(){
   return "modèle :: " + Modele + " - marque :: " + Marque;
}

Ce mot-clé permet d'outrepasser la méthode héritée de la classe de base. Ainsi, vous pouvez redéfinir cette méthode à votre convenance. Il est encore possible d'invoquer la méthode originelle de cette manière :

 
Sélectionnez
return "classe :: " + base.ToString()+" - modèle :: " 
            + Modele + " - marque :: " + Marque;

Le terme base fait référence à la classe dont on hérite, dans notre cas c'est object.

2-3-6-3. Déclarer des interfaces

Une interface est constituée des signatures de méthodes, de délégués et d'événements (voir Chapitre 7 Interactivité et modèle événementiel). Elle représente un contrat abstrait d'implémentation. Une classe implémentant une interface doit nécessairement contenir les mêmes signatures de méthodes, délégués et événements mais avec une définition concrète de ceux-ci (à l'exception des classes abstraites). Les interfaces (et les classes abstraites) permettent d'assouplir le développement et rendent le code plus facile à maintenir et évolutif. L'utilisation d'interfaces se révèle au final moins dangereux et plus facile à gérer que l'héritage. Pour implémenter une ou plusieurs interfaces, il faudra ajouter un caractère : après le nom de la classe, suivi du nom des interfaces. Si la classe est héritée d'une autre classe, il faudra suivre cet ordre :

 
Sélectionnez
class MaClasse : ClasseDeBase, interface1, interface2,, interfaceN
class Voiture : Vehicule, IUtilitaire, IFamilial

Par convention, on préfixe les noms d'interface de I, ce qui donne plus de lisibilité.

Nous n'entrerons pas plus dans les détails de C# au sein de ce chapitre, car, comme vous l'avez constaté, C# est un langage performant dont l'apprentissage nécessiterait un livre à part entière. Maintenant que vous êtes familiarisé avec cet environnement, vous allez créer votre première application Silverlight grâce aux outils proposés au sein de la gamme Expression.

Dans le prochain chapitre, vous utiliserez Expression Blend et aborderez les bases de la mise en forme XAML, ainsi que l'architecture des projets Silverlight.

3. Hello World

Nous allons concevoir une première application Silverlight avec le logiciel Expression Blend. Ce projet nous servira de base pour détailler l'architecture par défaut des projets Silverlight ainsi que l'organisation de l'interface Blend. Nous aborderons ensuite la notion d'arbre visuel et logique ainsi que l'utilisation de conteneurs. Nous listerons rapidement les composants de gestion de texte et les options d'alignement. Nous finirons ce chapitre par une première compilation de l'application, ce qui nous permettra d'énumérer les fichiers qu'elle produit tout en abordant ses mécanismes internes.

3-1. Une première application Silverlight

Commencez par créer un nouveau répertoire sur votre disque dur. Vous pouvez le nommer Pratique_de_Silverlight par exemple. C'est dans ce répertoire que seront contenus tous les projets que vous allez créer dans ce livre. Vous devez vous munir de la dernière version d'Expression Blend afin de générer une solution Silverlight. Celle-ci est disponible en version d'essai sur le site de Microsoft : http://www.microsoft.com/france/expression/. De manière générale, vous trouverez les prérequis logiciels sur le blog Tweened.org à cette adresse :http://www.tweened.org/pre-requis-silverlight/. Ils sont mis à jour à chaque nouvelle version de Silverlight.

Une fois que vous avez installé Blend et l'ensemble des prérequis, démarrez l'application. Vous devriez avoir une interface correspondant à la Figure 3.1.

Blend vous permet d'afficher un écran de bienvenue. Cochez la case en bas à droite si vous souhaitez qu'elle apparaisse à chaque démarrage. Vous allez maintenant créer votre premier projet. Pour cela, sélectionnez l'onglet Projects de l'écran de bienvenue, puis cliquez sur New Project... Vous devriez voir un nouvel écran s'afficher (voir Figure 3.2).

Image non disponible
Figure 3.1 - Écran de bienvenue d'Expression Blend
Image non disponible
Figure 3.2 - Création d'un nouveau projet

Dans la boîte de dialogue affichée, plusieurs possibilités s'offrent à vous. Vous pouvez tout d'abord choisir entre deux familles de projets : WPF ou Silverlight.

Au sein de la famille Silverlight, quatre choix sont accessibles par défaut. Les solutions de type Silverlight 3 Application ou Silverlight 3 Application + Website sont très semblables. La première sera exécutée dans une page HTML générée dynamiquement lors de chaque compilation. A contrario, une solution de type site propose des fichiers HTML et JavaScript permettant d'intégrer l'application pour une mise en production et un déploiement rapide (voir Chapitre 4 Un site plein écran en deux minutes). Le troisième type, Silverlight 3 Control Library, génère un projet facilitant la centralisation de contrôles personnalisés. Créer des contrôles personnalisés avec Silverlight est tellement simple que cette manière de procéder est assez naturelle. Le dernier type de projet, Silverlight 3 SketchFlow Application, permet de concevoir, de manière efficace et rapide, l'architecture entière d'une application Silverlight. Nous aborderons la création de prototypes avec SketchFlow au Chapitre 9 Prototypage dynamique avec SketchFlow.

Choisissez Silverlight 3 Application, puis le langage C# (normalement sélectionné par défaut). Si vous êtes développeur Visual Basic, vous avez bien sûr la possibilité de choisir ce langage. Il faut également préciser le chemin d'accès au dossier que nous avons précédemment créé. Il va contenir l'ensemble de nos projets. Voici un exemple : C:\Documents and Settings\invite\Bureau\Pratique_de_Silverlight\. Pour finir, entrez HelloWorld dans le champ Name, cliquez sur OK. Une autre méthode serait de fermer le panneau, d'ouvrir le menu File, puis de sélectionner New Project… Il n'y a aucune différence de résultat entre ces deux manières de faire.

Une fois cette étape réalisée, vous accédez à l'interface de Blend correspondant aux projets de type Silverlight. De légères différences d'interface existent entre les projets WPF et Silverlight. On a coutume de dire que Blend est réalisé avec Blend. Cela fait référence à la nature même de ce logiciel qui repose sur WPF. Vous devriez récupérer une interface correspondant à la Figure 3.3.

Image non disponible
Figure 3.3 - L'interface d'expression Blend à l'ouverture du projet HelloWorld

Blend a placé automatiquement, sur votre disque dur, un nouveau répertoire portant le nom du projet. Créer une application Silverlight, ou WPF, revient à générer un répertoire sur votre disque dur contenant un ensemble de fichiers par défaut. Comme vous le constatez à la Figure 3.3, l'interface de Blend est constituée de plusieurs parties distinctes.

  • La barre de menu, située en haut, permet de créer des boutons et des composants personnalisés, mais donne également accès aux opérations sur les tracés, aux préférences du projet et à de nombreux autres menus inaccessibles d'une autre manière.
  • Complètement à gauche de l'interface, vous trouverez une barre d'icônes. Chaque icône fait référence à un type d'outil. Ceux-ci sont catégorisés en cinq familles : les outils de sélection, de manipulation de la vue (le zoom par exemple), de modification, de création d'objets primitifs (tels que les rectangles, les tracés) et de création de contrôles (du plus simple conteneur aux objets visuels et logiques complexes comme la ListBox).
  • En haut à gauche se trouve une série de panneaux gérant les états visuels, la liste des composants et des ressources, ainsi que l'arborescence du projet présent sur le disque dur. Par défaut, vous trouverez l'état visuel Base qui contient le visuel par défaut de l'application. Le panneau Projects contient une arborescence des fichiers nécessaires au fonctionnement de l'application (voir la section 3.2 Architecture d'une solution). La notion d'états visuels, telle qu'elle est réalisée dans Silverlight, est relativement innovante. Nous ajouterons des états visuels au sein de nos applications afin de scinder visuellement leurs fonctionnalités et de gérer les transitions (voir Chapitre 5 L'arbre visuel et logique).
  • En bas à gauche se situe le panneau contenant l'arbre visuel et logique de notre application. Ce panneau affiche l'ensemble des composants d'une application. Ceux-ci sont hiérarchisés selon leur ordre d'imbrication et leurs liens de parenté. On peut considérer que l'arbre visuel et logique est la représentation graphique des liens familiaux entre les objets XAML. C'est également dans ce panneau que l'on pourra concevoir des animations
  • Le centre de l'application correspond à l'espace alloué aux designers et aux intégrateurs pour concevoir les éléments visuels ainsi que l'architecture de l'interface utilisateur. Le rectangle blanc au milieu est la grille principale d'agencement, nommée LayoutRoot. La couleur d'arrière-plan par défaut est le blanc, mais vous pourriez décider de ne pas en définir dans l'optique de créer une application Silverlight transparente. Cette vue est donc réellement importante, car c'est son ergonomie qui rend possible la participation des designers. Cet espace permet également d'afficher le code XAML généré par Blend lorsque le graphiste crée l'interface visuelle.
  • En bas, au centre, le panneau sortie et erreur permet d'afficher des informations de sorties lors de la compilation de l'application Silverlight. Il permet également d'exposer les erreurs levées lors de la compilation ou de l'utilisation de Blend.
    Complètement à droite se concentre une série d'onglets : Data, Properties et Resources.
  • Le panneau Data permet de gérer des sources de données externes ou propres à la solution. Il permet également de créer des jeux de données fictives et de simuler un flux RSS ou un tableau d'objets C#.
  • Le panneau Properties est contextuel, c'est-à-dire qu'il est mis à jour en fonction de l'objet sélectionné dans l'arbre visuel et logique ou dans la fenêtre de design. Il dresse un inventaire complet de toutes les propriétés de l'objet en cours de sélection. Celles-ci sont parfois si nombreuses qu'un champ texte de saisie permet de les filtrer.
  • Le panneau Resources est également contextuel au fichier XAML sélectionné ou à l'objet sélectionné. Il liste l'ensemble des ressources visuelles ou logiques accessibles. Nous consacrerons le Chapitre 10 Ressources graphiques aux ressources visuelles et logiques.

3-2. Architecture d'une solution

Le panneau Projects affiche l'ensemble des fichiers faisant partie de l'application. L'élément hiérarchique le plus élevé est la solution. Une solution est l'unité d'organisation principale. Celle-ci est constituée d'au moins un projet. Tous les fichiers nécessaires pour la conception ou produits lors de la compilation sont répartis au sein de divers projets. Chaque projet a pour but de gérer une application, un module, une fonctionnalité ou une bibliothèque de contrôles facilitant l'organisation du développement. On a donc, en premier lieu, une solution contenant un projet du même nom (voir Figure 3.4).

Voici la liste des fichiers générés, par défaut, au sein du projet lorsque vous créez une application Silverlight.

  • Le fichier MainPage.xaml. Il contient le code XAML décrivant les objets graphiques et logiques de l'interface utilisateur. Par défaut, c'est également la première page chargée par l'application. Le travail du graphiste et de l'intégrateur se répercute dans un premier temps dans ce fichier. Lors de l'avancement du projet, d'autres fichiers XAML sont ajoutés, notamment des dictionnaires de ressources.
  • Le fichier MainPage.xaml.cs contient le code logique C#. Celui-ci assure la partie fonctionnelle de la première page de l'application. Ce fichier concerne les développeurs C# et les intégrateurs. Aucun fichier logique JavaScript n'est présent dans ce type de solution. Toutefois, dans le cas de projets Visual Basic, le fichier aura une extension propre à ce langage.
  • Le répertoire References contient toutes les bibliothèques C# ou assemblies nécessaires par défaut. Ce sont des fichiers dll qui permettent d'étendre à volonté les capacités fonctionnelles de l'application. Si vous souhaitez utiliser des formats de données comme XML ou JSON, par exemple, vous devrez ajouter une référence sous forme de dll. Pour cela, il suffit de faire un clic droit, puis de choisir Add Reference... ou Add Project Reference...
  • Le répertoire Properties contient deux fichiers décrivant l'application dans ses grandes lignes : auteur, compagnie, objectif, version, etc.
  • Les fichiers App.xaml et App.xaml.cs contiennent et centralisent du code inhérent au projet lui-même. Ils permettent de préciser quelle est la première page de l'application qui va être chargée. Ils peuvent également contenir des styles ou des modèles de composants accessibles pour toutes les pages XAML. Ces fichiers sont importants, car ils représentent l'instance de l'application Silverlight dans la page HTML. Ils permettent donc de définir des comportements précis à l'initialisation ou à la fermeture de celle-ci.

Les fichiers énumérés ci-dessus ne sont pas les seuls pouvant faire partie d'une solution. De nombreux types de documents peuvent être utilisés au sein de solutions Silverlight. Ce sont généralement des ressources générées depuis des applications externes, comme Illustrator et Photoshop, ou créées au sein de Blend ou de Visual Studio. Vous pouvez à tout moment ajouter une nouvelle ressource, par exemple une police de caractères ou un fichier de code logique, à un projet existant.

Image non disponible
Figure 3.4 - Le panneau Projects

Nous allons maintenant voir ce qui a été généré sur le disque dur. Ouvrez votre explorateur Windows afin d'accéder au répertoire contenant votre solution. Si celui-ci est sur votre bureau et que vous avez suivi la procédure indiquée, voici son chemin d'accès : C:\Documents and Settings\invite\Bureau\Pratique_de_Silverlight\. Votre répertoire doit contenir un dossier du nom de la solution. Celui-ci comprend un répertoire du même nom (correspondant au projet généré par défaut), ainsi que deux autres fichiers : HelloWorld.sln et HelloWorld.suo qui est un fichier caché contenant des paramètres propres à la solution (voir Figure 3.5).

Image non disponible
Figure 3.5 - Contenu du répertoire de la solution Hello-World

L'extension .sln indique un fichier solution. Il référence les projets contenus par la solution ainsi que diverses informations. Le répertoire HelloWorld possède un contenu correspondant à ce qui apparaît dans le panneau projet de Blend plus un répertoire bin. Celui-ci reçoit les fichiers binaires générés lors de la compilation (voir section 3.5.3 Fichiers générés). Lorsque vous ajoutez un fichier de type ressource ou code logique à un projet Silverlight, celui-ci est automatiquement placé dans le répertoire du projet. Cela permet au projet d'être autonome, déplaçable librement sur le disque dur ou encore d'être partagé.

3-3. Le conteneur racine

Maintenant que nous avons vu l'architecture d'une solution, nous allons étudier celle d'une page d'application Silverlight. Lors de l'initialisation d'une application Silverlight, celle-ci charge une page par défaut. La page en question est issue de la compilation des deux fichiers MainControl.xaml et MainControl.xaml.cs. Le premier fichier est de type XML, il contient donc un nœud racine. Ce nœud est le conteneur parent de tous les objets visuels et logiques de la page. Il est de type UserControl. Il détermine la dimension de la page, si elle possède un fond transparent ou encore tout ce qui a trait directement ou indirectement à son affichage. Les composants de type UserControl ne peuvent contenir qu'un unique objet enfant. Ainsi, déposer un composant directement dans un UserControl efface l'enfant qui s'y trouve éventuellement. Lors de la création d'un nouveau projet Silverlight, un contrôle Grid nommé LayoutRoot lui est ajouté, par défaut, comme élément enfant. Celui-ci peut, contrairement au composant UserControl, contenir plusieurs enfants visuels et logiques.

Image non disponible
Figure 3.6 - Arbre visuel et logique d'une page

Comme vous pouvez vous en rendre compte, une page est constituée d'éléments imbriqués. Il n'y a donc pas réellement de notions de calques ou de couches. Un calque (ou une couche) est une unité d'organisation abstraite qui regroupe plusieurs objets sur une même profondeur visuelle. Les conteneurs Silverlight sont, quant à eux, des objets sélectionnables ayant un aspect physique et une logique d'agencement propre. Ce n'est pas un handicap, bien au contraire. Cela force à concevoir l'imbrication dès le départ et rend plus propre et efficace le développement d'applications ou de sites. L'ensemble des éléments imbriqués constitue l'arbre visuel et logique de l'application.

3-4. Ajouter du texte

3-4-1. Créer le champ texte

Vous allez maintenant ajouter un champ texte centré. Pour cela, vous trouverez une icône de champ texte dans votre barre d'outils. Cliquez dessus et maintenez l'icône enfoncée. Trois pictogrammes apparaissent, correspondant aux trois types de champs texte que vous pouvez créer : TextBlock, TextBox et PasswordBox. Le contrôle de type TextBlock vous permet d'afficher du texte. Les deux autres types autorisent l'utilisateur à saisir du texte lors de l'exécution de l'application. Le composant PasswordBox est utile pour la saisie de mots de passe utilisateur. Sélectionnez le premier type de champ (TextBlock). Nous allons ajouter une instance de type TextBlock dans notre conteneur LayoutRoot. Double-cliquez sur l'icône. Un nouveau TextBlock est automatiquement créé en haut à gauche, dans la grille LayoutRoot. À sa création un TextBlock est en mode édition de texte (voir Figure 3.7). Pour en sortir, appuyez sur la touche Esc ou cliquez hors du composant.

Image non disponible
Figure 3.7 - Création d'un TextBlock

3-4-2. Alignement

Lors de sa création via un double-clic et au sein d'un contrôle de type Grid, un objet est toujours placé en haut à gauche. Vous allez centrer le champ texte au sein de la grille. Pour cela, sélectionnez-le dans l'arbre visuel, puis ouvrez l'onglet Properties. À l'intérieur de la catégorie Layout, vous trouvez deux séries d'icônes reflétant les options d'alignement ainsi que quatre champs de saisie juste en dessous. Ceux-ci permettent de spécifier des marges extérieures et doivent être utilisés conjointement avec les options d'alignement. C'est le paramétrage de l'ensemble de ces propriétés qui détermine le positionnement des objets au sein d'un conteneur. Assurez-vous que ces champs ont tous une valeur égale à zéro. Si un quelconque chiffre apparaît dans un de ces champs, il faut le remettre à zéro. Si vous avez créé le champ texte en le dessinant directement au sein de la grille, ces propriétés ne sont pas vides. Pour réinitialiser n'importe quelle propriété dans Blend, il vous suffit de cliquer sur l'icône carrée présente à droite de chacune des propriétés et de sélectionner Rétablir. Cliquez maintenant sur les deux pictogrammes qui permettent l'alignement horizontal et vertical (voir Figure 3.8).

Image non disponible
Figure 3.8 - Les options d'alignement

Votre texte est maintenant centré. Les propriétés largeur et hauteur (Width et Height) possèdent une valeur Auto. Cela signifie que leur valeur s'adaptera automatiquement, soit au contenu du champ texte (la chaîne de caractères), soit au conteneur, en fonction des marges et de l'alignement spécifié. Vous allez paramétrer l'affichage de ce champ. Pour cela, ouvrez la catégorie Text du panneau Properties. Vous y trouverez plusieurs options, notamment pour choisir la police de caractères ou encore la mise en forme du texte. L'une d'entre elles, FontSize, permet de modifier la taille de la police en pixels. Choisissez une taille d'au minimum 24 pixels. Si vous avez correctement configuré les options d'alignement du texte, celui-ci devrait s'étendre dans les deux sens, mais rester centré dans la grille principale. Dans l'onglet Common Properties, pour la propriété Text, entrez la valeur "HelloWorld" ou la chaîne de caractères de votre choix. Une fois de plus, le TextBlock s'étend dans les deux directions pour s'adapter à son contenu (voir Figure 3.9).

Image non disponible
Figure 3.9 - Le projet Hello World.

3-5. Tester et compiler

Contrairement aux langages tels que HTML ou JavaScript qui sont interprétés par le navigateur, le lecteur Silverlight exécute des fichiers de type xap compilables par Blend ou Visual Studio. La compilation permet de fusionner les fichiers contenant le code XAML et C# en langage intermédiaire. Cette étape est donc importante.

3-5-1. Première compilation

Vous allez maintenant compiler votre application. Pour cela, il y a trois solutions :

  • dans le panneau Project, après un clic droit sur la solution, sélectionnez Run Project ;
  • au sein du menu Project, sélectionnez Run Project ;
  • utilisez le raccourci clavier F5.

Ces trois méthodes compilent le projet et affichent le résultat dans votre navigateur configuré par défaut. Un lecteur autonome existe, mais tester ou déboguer une application Silverlight ne peut être réalisé qu'au sein d'un navigateur. Appuyez sur la touche F5. Une série de messages apparaît en fenêtre de sortie. Ils vous indiquent la progression de la compilation. La page HTML contenant votre application apparaît (voir Figure 3.10).

Lors de projets plus complexes, il peut arriver qu'une erreur soit levée à la compilation. Dans ce cas, l'application ne compile pas. Il vous faudra lire le message d'erreur présent en fenêtre de sortie. Toutefois Blend ne bénéficie pas d'un débogueur aussi puissant que celui de Visual Studio. C'est pourquoi les messages d'erreur sont parfois très génériques et peuvent ne pas vous donner d'indices suffisants pour résoudre le bogue.

Image non disponible
Figure 3.10 - L'application chargée dans le navigateur

3-5-2. Une application 100 % Silverlight

Vous remarquez qu'au sein de notre page, le composant TextBlock n'est pas centré. En réalité, il est correctement centré, mais par rapport à la page de notre application chargée par défaut. Il est donc centré par rapport au conteneur principal du fichier MainControl.-xaml et son composant racine UserControl. Si vous faites un clic droit n'importe où sur la page HTML, vous verrez un menu contextuel Silverlight. Celui-ci vous indique que l'instance du plug-in Silverlight occupe toute la place dans la page HTML. Cependant, par défaut, le UserControl racine possède des dimensions fixes (640 pixels de large par 480 de hauteur). Pour le vérifier, sélectionnez dans Blend le UserControl principal et vérifiez les valeurs de ses propriétés Width et Height. Pour résumer, la page HTML générée par défaut affiche l'instance du plug-in dans une balise de type DIV occupant 100 % de hauteur et de largeur, c'est-à-dire que l'instance Silverlight occupe toute la place au sein du navigateur. Toutefois, le UserControl racine possède des dimensions fixes exprimées en pixels (voir Figure 3.11).

Image non disponible
Figure 3.11 - Mise en page par défaut d'une application Silverlight

Si vous souhaitez centrer le TextBlock dans la page HTML, il vous faut donc faire en sorte que le UserControl racine de MainControl.xaml s'adapte au navigateur continuellement. Pour cela il vous faut redéfinir la largeur et la hauteur du UserControl. Fermez votre navigateur et revenez sous Blend. Sélectionnez le UserControl dans l'arbre visuel, au-dessus du composant Grid, nommé LayoutRoot par défaut. Ensuite, définissez ses propriétés Width et Height sur Auto en cliquant sur le pictogramme situé à droite.La valeur Auto signifie que la largeur du composant va s'adapter à l'espace qui lui est permis dans la page HTML. Au sein de Blend, la largeur et la hauteur n'étant pas fixées en pixels, le composant s'adapte pour afficher au minimum le champ texte contenu dans la grille LayoutRoot. Des icônes apparaissent autour du contour, elles indiquent que vous pouvez agrandir les dimensions du UserControl racine (voir Figure 3.12).

Image non disponible
Figure 3.12 - Mise à jour des propriétés du UserControl

Les icônes les plus éloignées permettent d'étirer le projet pour simulant les dimensions fictives d'une page HTML. Vous pourrez par exemple obtenir un aperçu du redimensionnement de votre application dans une résolution plus faible. Pour gérer de manière précise les dimensions d'affichage fictives, vous devez passer en mode d'édition XAML. Pour cela, Blend propose un mode d'édition XAML. Cliquez sur l'icône représentant une balise en haut à droite de la fenêtre de design. C'est la deuxième icône en partant du haut (voir Figure 3.13). Elle permet d'accéder au mode XAML. La première icône, en haut, passera la vue en mode création. Vous pourrez également utiliser le mode mixte qui affiche à la fois le code XAML et le visuel en cliquant sur l'icône située en bas.

Image non disponible
Figure 3.13 - Bouton d'accès au mode d'édition XAML

Une fois en mode XAML, modifiez les propriétés d:DesignWidth et d:DesignHeight du nœud UserControl racine en leur donnant respectivement les valeurs 800 et 600. Vous pouvez voir un aperçu du code ci-dessous :

 
Sélectionnez
<UserControl 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    x:Class="HelloWorld.MainControl" Width="Auto" Height="Auto" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600">

Votre application possède maintenant une résolution de 800 pixels de large et de 600 pixels de hauteur. Toutefois ces valeurs ne sont uniquement prises en compte et utiles que lorsque vous êtes sous Expression Blend. Elles sont pratiques pour avoir une prévisualisation de la mise en page dans cette résolution. Elles ne sont pas prises en compte lors de l'exécution de l'application dans une page HTML. Quoi qu'il en soit, l'instance de l'application Silverlight est dans une balise à 100 %, le UserControl occupera le maximum d'espace dans la page HTML, car les propriétés Width et Height sont en mode Auto et son alignement vertical et horizontal est en mode Stretch. Cette fois, le texte est correctement centré au sein du navigateur. De plus, lorsque vous redimensionnez la fenêtre du navigateur, le texte se repositionne dynamiquement (voir Figure 3.14).

Image non disponible
Figure 3.14 - Repositionnement du TextBlock

3-5-3. Fichiers générés

Une fois l'application compilée, votre navigateur se lance et affiche la page Silverlight. Le site s'affiche via le serveur Web de développement lancé par Blend. C'est l'icône que vous pouvez apercevoir en bas à droite. Tous les fichiers relatifs à la création du site se situent dans le répertoire Debug (voir Figure 3.15).

Image non disponible
Figure 3.15 - Emplacement des fichiers générés

Comme vous pouvez le voir, les fichiers produits à la compilation sont situés dans le répertoire Debug. Lorsque votre projet est en phase finale et prêt pour sa mise en ligne, vous pouvez, sous Visual Studio, définir l'application en mode Release. Cela permet de gagner en performances. Les bibliothèques nécessaires pour tracer des messages dans la fenêtre de sortie ou pour déboguer sont ignorées, le code est également beaucoup plus optimisé. De plus, c'est une bonne pratique, lorsque l'on arrive à une version stable, de compiler en mode Release, puis de la mettre en production. Dans tous les cas, vous trouverez au moins les fichiers suivants :

  • TestPage.html est le fichier généré automatiquement lors de chaque compilation pour tester l'application Silverlight. Ce fichier propose une intégration minimale. Il contient simplement une balise object définissant l'instance du plug-in Silverlight dans la page Web. Voici un exemple de la balise générée par défaut :

     
    Sélectionnez
    <div id="silverlightControlHost">
       <object data="data:application/x-silverlight-3," 
            type="application/x-silverlight-3" width="100%" height="100%">
          <param name="source" value="SL3WebSite.xap"/>
          <param name="onerror" value="onSilverlightError" />
          <param name="background" value="white" />
          <param name="minRuntimeVersion" value="3.0.40304.0" />
          <param name="autoUpgrade" value="true" />
          <a href="http://go.microsoft.com/fwlink/?LinkID=141205" 
            style="text-decoration: none;">
            <img src="http://go.microsoft.com/
            fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" 
            style="border-style: none"/></a>
       </object>
    </div>

    Vous pouvez remarquez que le premier paramètre indique le chemin d'accès vers le fichier xap à lire (voir HelloWorld.xap au point suivant). Le deuxième paramètre, onerror, redirige quant à lui toutes les erreurs à l'exécution ou lors de l'ouverture du fichier vers une fonction JavaScript qui gère leur affichage dans une balise DIV dédiée. Le paramètre autoUpgrade est par défaut à true. Cela signifie que celui-ci permet la mise à jour automatique de Silverlight lorsqu'une nouvelle version est disponible.

  • HelloWorld.xap. C'est le format de fichier lisible par le lecteur Silverlight. Malgré les apparences, il n'est pas le produit direct de la compilation mais un fichier zip renommé avec l'extension xap. Il contient la dll de l'application ainsi qu'un manifeste décrivant tout ce qui est à l'intérieur. Dans notre cas, il possède HelloWorld.dll, le fichier produit par la compilation.

  • HelloWorld.dll. C'est exactement le même fichier que celui situé dans le document xap. Il peut être référencé par n'importe quel autre projet Silverlight. Cela permet au projet qui le référence de pouvoir créer des instances de notre application. Vous pourriez ainsi créer un portfolio de tous les sites que vous avez réalisés en Silverlight, simplement en référençant leur dll et en les instanciant sous forme de miniature au sein d'Expression Blend.

  • AppManifest.xaml. Le code montré ci-dessous expose le contenu de ce fichier :
 
Sélectionnez
<Deployment 
   xmlns="http://schemas.microsoft.com/client/2007/deployment" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   EntryPointAssembly="HelloWorld" 
   EntryPointType="HelloWorld.App" 
   RuntimeVersion="3.0.40624.0">
   <Deployment.Parts>
      <AssemblyPart x:Name="HelloWorld" Source="HelloWorld.dll" />
   </Deployment.Parts>
</Deployment>

Comme vous le constatez, il définit les informations indispensables permettant au lecteur Silverlight de lire le fichier xap. Le lecteur compare la version de Silverlight exposée par l'attribut RuntimeVersion à la valeur de la propriété minRuntime-Version définie dans la balise object au sein du document TestPage.html. Si les versions définies dans chacun de ces fichiers ne correspondent pas, le lecteur peut lever une erreur.

3-5-4. Processus de compilation

3-5-4-1. Les classes partielles

Les classes partielles sont nées avec l'apparition de .Net 2. Elles avaient déjà pour but de séparer le fond et la forme au sein de fichiers distincts. Pour cela, le développeur pouvait glisser/déposer n'importe quel composant .Net au sein d'une fenêtre représentant l'interface visuelle. Le code logique était bien séparé du code décrivant l'interface visuelle mais ce dernier n'héritait pas du XML (contrairement à XAML). Lorsque le développeur compilait son application, celle-ci était donc générée à partir de deux fichiers. Ce mécanisme, permis grâce aux classes partielles, est exactement le même que celui utilisé aujourd'hui par Silverlight. Toutefois ce concept est bien plus efficace et pertinent avec cette technologie. Comme nous l'avons dit lors du précédent chapitre, une classe est un modèle d'objet. Une classe partielle est donc une partie d'un modèle d'objet contenue, la plupart du temps, dans un fichier séparé. Examinons le code XAML de notre projet HelloWorld :

 
Sélectionnez
<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Class="HelloWorld.MainPage"
    Width="Auto" Height="Auto" mc:Ignorable="d"></UserControl>

Vous remarquez que le UserControl principal contient la propriété x:Class qui a pour valeur HelloWorld.MainPage. Cela signifie que ce nœud est une définition de la classe MainPage héritant de la classe UserControl, et que cette définition est contenue dans l'espace de noms HelloWorld. Tout ce qui est déclaré dans le fichier XAML est donc un nouveau membre de cette classe. Le fichier MainPage.xaml.cs permet d'ajouter du code logique à l'application et aux objets déclarés dans MainPage.xaml. C'est lui qui permet les interactions utilisateur et la création d'algorithmes complexes. Voici un extrait de ce texte :

 
Sélectionnez
namespace HelloWorld
{
   public partial class MainPage : UserControl
   {
      public MainPage()
      {
         // Required to initialize variables
         InitializeComponent();
      }
   }
}

Cette classe se nomme MainPage. Elle hérite, elle aussi, de la classe UserPage et elle est contenue dans l'espace de nom HelloWorld. Vous remarquez l'utilisation du mot-clé partial. Ce mot signifie que la classe est partielle. Il s'agit donc de la même classe au sein de deux fichiers séparés. Chacun a pour but de définir une partie de celle-ci et non la totalité. C'est une approche très performante car XAML est bien plus pratique pour gérer l'arbre visuel et logique ainsi que les animations. C# sera, quant à lui, beaucoup plus performant pour développer l'aspect fonctionnel et les interactions complexes. Nous allons maintenant étudier les mécanismes du compilateur permettant de gérer deux langages aussi différents que C# et XAML.

3-5-4-2. Just In Time Compiler

La compilation d'un projet Silverlight s'effectue toujours en deux étapes. La première consiste à transformer les langages de haut niveau gérés par la CLR comme C#, VB, XAML ou les langages gérées par la DLR, comme IronPython en langage intermédiaire. Quel que soit le langage d'origine, le langage intermédiaire résultant de la compilation est le même pour tous les langages gérés par .NET. La compilation est réalisée par le compilateur propre au langage dans lequel vous développez. Ce langage intermédiaire est contenu dans la dll qui est encapsulée au sein du fichier xap. Dans notre cas, elle se nomme "HelloWorld.dll" que nous avons évoquée plus haut. La deuxième étape est une compilation effectuée lors de la lecture du fichier xap par le plug-in Silverlight. L'implémentation du lecteur Silverlight diffère selon les systèmes d'exploitation et le navigateur utilisé. Cette compilation est donc à chaque fois différente mais l'objectif est le même : transformer le langage intermédiaire en langage machine. C'est le Just In Time Compiler qui gère cette étape. Son but est de compiler au dernier moment en langage machine afin d'obtenir les meilleures performances possibles selon le système. Comme il est différent et spécialisé selon les systèmes et les navigateurs, il produit du code machine plus adapté qu'un compilateur générique (voir Figure 3.16).

Image non disponible
Figure 3.16 -Compilation Silverlight en deux étapes

Dans le prochain chapitre, nous plongerons dans l'interface d'Expression Blend pour créer un site Silverlight très simple et gérer le redimensionnement des éléments visuels.

4. Un site plein écran en deux minutes

Au sein de ce chapitre, vous allez créer votre premier site Silverlight, puis vous identifierez les différences et avantages existants avec les projets de type application. Vous apprendrez à créer des boutons au sein de conteneurs spécifiques. Ceux-ci joueront le rôle de menus et rafraîchiront le contenu de la page principale. L'un d'eux permettra notamment l'affichage du site en mode plein écran. Le navigateur ne sera plus visible et l'application occupera tout l'espace sur l'écran lorsque ce mode sera activé. Vous verrez donc comment gérer le redimensionnement ou le repositionnement des objets dans la grille principale. L'interactivité utilisateur est abordée à travers trois méthodologies différentes : les comportements, l'utilisation du XAML ou la création de code logique C#. Pour finir, nous listerons les fichiers qui doivent être déployés sur le serveur.

4-1. Les projets de type site Internet

Les projets de type site permettent aux designers interactifs de se passer partiellement d'un développeur .Net ou d'un intégrateur HTML CSS. Ils fournissent par défaut les mécanismes d'intégration des applications Silverlight au sein d'une page Web.

Créez un nouveau projet de type Silverlight Application + Web Site. Nommez-le AgencePort-folio. Vérifiez bien que le type de langage choisi est C# et que le répertoire contenant le projet s'appelle Pratique_de_Silverlight. Pour finir, cliquez sur OK. Votre panneau Projects affiche le visuel correspondant à la Figure 4.1 après un court instant.

Vous remarquez qu'il existe une nette différence avec les applications Silverlight 3 standard. La solution est constituée de deux projets au lieu d'un seul. Le premier projet est l'application Silverlight elle-même. Vous pouvez le constater car sa structure est la même que celle étudiée au Chapitre 3 Hello World. Le second projet représente le site Web lui-même. Son nom est le même que celui de la solution, mais se termine par le suffixe Site. Trois fichiers sont visibles : Default.html, favicon.ico et Silverlight.js. Le premier représente la page HTML intégrant l'application Silverlight, le deuxième correspond à l'icône par défaut du site dans la barre d'adresse et le troisième est un document JavaScript qui facilite et améliore l'intégration de Silverlight.

Image non disponible
Figure 4.1 - Arborescence d'un projet de type Silverlight

Dans le précédent type de projet, la page HTML se nommait TestPage.html au lieu de
Default.html. Elle était générée par défaut lors de chaque compilation. Lorsque vous la modifiez directement dans le répertoire Debug, pour améliorer la mise en page ou l'intégration Silverlight, ces modifications étaient écrasées à chaque compilation. Elles n'étaient donc pas prises en considération. Cela n'était pas pratique et rendait obligatoire la sauvegarde de TestPage.html sous un nom différent. Vous n'avez pas ce type de problème avec notre projet, vous pourrez modifier à loisir Default.html car il n'est pas recréé à chaque compilation. Un serveur Web est lancé avec sa racine pointant sur le dossier site. Le serveur Web pointe directement sur le fichier quand le navigateur s'ouvre. Appuyez sur la touche F5 pour tester votre site Internet. Le navigateur affiche une page blanche car aucun élément visuel n'est contenu au sein de LayoutRoot. Au moment où vous testez votre site, le projet Application est compilé en premier, puis le projet AgencePort-folioSite, qui est défini en tant que projet de démarrage, est lancé. Le projet en gras au sein d'une solution est toujours le projet de démarrage. Ce projet est donc exécuté au sein du navigateur après la compilation réussie du projet Application.

Le deuxième projet vous sert donc de base pour produire une intégration HTML performante. En interne, son code est équivalent à celui utilisé pour lancer les applications classiques car il référence le fichier JavaScript Silverlight.js pour vérifier que le plug-in est déjà installé sur le poste client ou demander sa mise à jour. Le fichier Silver-light.js
donne également accès aux propriétés de l'instance du plug-in représenté par la balise
Object. Voilà les propriétés apportés par ce fichier :

  • il contient un mécanisme dedétection du lecteur Silverlight, de sa version (si celui-ci est déjà présent) et propose l'installation du lecteur ;
  • il facilite la conception d'une intégration avancée et permet l'utilisation de Javascript comme langage logique ;
  • au même titre que la balise Object, il permet de gérer le préchargement de l'application Silverlight, vous pourrez donc afficher un indicateur de préchargement pour les applications Silverlight complexes et celles dont le poids excède une certaine valeur ; la décision concernant la valeur limite vous incombe et dépend du réseau sur lequel l'application est déployée ou le type de public ciblé ;
  • il donne accès aux mêmes paramètres avancés que la balise Object et c'est le cas de la propriété IsWindowLess, équivalente à windowless pour la balise Object (permettant la création d'applications transparentes).

Lorsque vous avez compilé, un répertoire ClientBin a été automatiquement ajouté dans le projet Internet. Ce répertoire contient uniquement le fichier xap. Il est recopié par défaut à la fin de la compilation du projet de type application. Dans ce répertoire, vous ne trouverez aucun fichier dll ou propre au débogage car l'objectif est de rester le plus simple possible. Le dossier ClientBin est le dossier de publication. Les fichiers du dossier bin\debug sont empaquetés dans le fichier xap qui est déployé dans ClientBin. Vous obtenez ainsi le meilleur des deux mondes. Le projet Internet reste simple et épuré, mais le débogage et la réutilisation demeurent toutefois possibles.

4-2. Créer des conteneurs redimensionnables

Pour créer une interface redimensionnable, plusieurs possibilités s'offrent à vous. Vous pourriez, par exemple, positionner les menus directement à l'intérieur de la grille principale. Il est pourtant préférable de centraliser les objets ayant la même fonctionnalité au sein d'un conteneur dédié. Cela permet de les manipuler tous ensemble plus facilement, si besoin est - il vaut mieux prévenir que guérir.

Nous allons créer deux types de conteneurs. Le premier, un StackPanel, contiendra les éléments du pied de page, essentiellement des champs texte cliquables. Le deuxième sera un conteneur de type WrapPanel. Il contiendra et agencera les menus présents dans le haut de notre page. Ceux-ci seront en réalité des composants de type Button. Notre site correspondra à la Figure 4.2. Cette figure est un croquis plus ou moins fidèle du résultat escompté. Ne vous formalisez donc pas sur les dimensions ou les rapports de proportions. Ce visuel nous sert avant tout de base de conception. Il a été réalisé avec SketchFlow, le nouvel outil de prototypage conçu par Microsoft, que étudierons au Chapitre 9 Prototypage dynamique avec SketchFlow.

Image non disponible
Figure 4.2 - Croquis du site plein écran

4-2-1. Créer le pied de page

4-2-1-1. Créer et configurer le StackPanel

Vous allez commencer par les menus les plus simples : ceux du pied de page. Ils sont contenus dans un StackPanel. Ce type de conteneur permet d'empiler verticalement ou horizontalement un ensemble d'objets. Dans notre cas, il s'agira de champs texte de type TextBlock. Tout contrôle visuel est cliquable au sein des projets Silverlight ou WPF. Le TextBlock n'échappe pas à cette règle, il peut donc jouer le rôle de liens ou de boutons. Commencez par créer un composant StackPanel. Dans la barre d'outils de Blend, cliquez en laissant appuyer sur l'icône représentant le composant Grid. Une série d'icônes apparaît. Sélectionnez l'icône représentant le StackPanel (voir Figure 4.3).

Image non disponible
Figure 4.3 - Icône du StackPanel

L'icône StackPanel remplace l'icône Grid au 1er niveau, double-cliquez dessus. StackPanel est automatiquement placé en haut à gauche de la grille principale LayoutRoot. Il possède des dimensions de 100 pixels de hauteur comme de largeur. Vous allez définir une largeur et une hauteur en mode Auto, pour cela cliquez sur l'icône à droite de la propriété Width et Height. Le StackPanel n'a plus de largeur ni de hauteur, il s'est adapté à son contenu. Comme il n'en a pas encore, il possède des dimensions en largeur et hauteur égales à zéro pixel. Ce comportement est lié au fait qu'il est aligné en haut à gauche et qu'il n'est pas en mode Stretch (étirement).

Lorsqu'un conteneur vide possède une largeur en mode Auto, cela signifie que la valeur s'adapte, soit par rapport au contenu du conteneur, soit par rapport au conteneur parent de celui-ci. Au sein d'une grille, si son alignement horizontal est à gauche, à droite ou centré, cela implique qu'il s'adapte par rapport à son contenu. Si l'option d'alignement est en mode étirement (Stretch) et que des marges sont définies, il s'étirera pour correspondre à la largeur de son conteneur, moins les dimensions des marges qui sont définies à gauche et à droite. C'est exactement le même principe pour la propriété hauteur (Height). Le mode Auto est spécifique aux propriétés largeur et hauteur. Pas toujours facile à assimiler, il possède le même comportement que celui existant en XHTML. Il est bidirectionnel car les valeurs en pixels sous-jacentes sont récupérées, soit par le conteneur parent, la grille LayoutRoot dans notre cas, soit par le contenu. Ce mode permet de gérer des comportements avancés tout en étant cohérent.

Cliquez sur l'icône correspondant à un alignement horizontal à droite dans les options d'alignement (Image non disponible), puis définissez une marge à droite de 30 pixels. Spécifiez ensuite un alignement vertical vers le bas ainsi qu'une marge de 10 pixels par rapport à la bordure basse de la grille LayoutRoot. Ne spécifiez aucune marge à gauche (entrez la valeur  0). Pour finir, vérifiez bien qu'Orientation est en mode Horizontal. Ce mode indique la direction d'empilement des éléments contenus.

4-2-1-2. Ajouter des champs texte et notion de contexte conteneur

Lorsque vous avez double-cliqué sur l'icône du StackPanel, vous l'avez directement imbriqué dans la grille LayoutRoot. Ce n'est pas un hasard. Vous avez pu réaliser cette opération, car cette grille était sélectionnée comme contexte conteneur.

Le contexte conteneur est toujours entouré d'un liseré bleu ou jaune. Cela signifie que toutes les actions du graphiste, la création de composants, leur sélection ou encore leur modification, seront réalisées dans le conteneur sélectionné comme contexte actif. Pour créer une série de menus à l'intérieur du StackPanel, il vous faut donc cliquer sur celui-ci dans la vue de design pour le définir en tant que contexte actif. Ensuite, vous pouvez double-cliquer sur l'icône TextBlock dans la barre d'outils. Lors de chaque double-clic, vous imbriquez une nouvelle occurrence de TextBlock au sein du StackPanel. Répétez cette opération trois fois. Une fois les trois champs texte créés, définissez des marges à droite de 20 pixels pour les deux premiers. Vous pouvez sélectionner les deux éléments et spécifier leurs marges en même temps dans le panneau des propriétés. Ensuite, changez la valeur contenue dans la propriété Text, qui est accessible via le panneau des propriétés ou directement par double-clic sur le composant TextBlock dans la fenêtre de design (voir Figure 4.4). Vous pouvez également choisir une police afin d'affiner le visuel. Je vous conseille la police Trebuchet MS dans un premier temps car cela évite d'avoir à l'embarquer puisqu'elle est intégrée par défaut à Silverlight.

Image non disponible
Figure 4.4 - Menu bas du site représenté par un StackPanel contenant plusieurs contrôles Textblock

Vous remarquez que le StackPanel s'agrandit vers la gauche automatiquement à chaque nouvel élément visuel ajouté en son sein.

4-2-2. Créer le menu du haut

Nous allons maintenant créer le menu du haut. Le composant bouton (Button) propose des interactions visuelles avantageuses informant l'utilisateur que ce menu est réactif. Par exemple, le menu pourrait changer de couleur lorsqu'il est survolé.

4-2-2-1. Principe du WrapPanel et du mode Auto

Comme la navigation de notre site repose essentiellement sur le menu du haut, nous allons imbriquer des boutons au sein d'un conteneur. Afin de gérer les redimensionnements extrêmes de notre site, nous allons utiliser un composant WrapPanel. Celui-ci est proche du StackPanel dans son principe, c'est-à-dire qu'il empile les éléments les uns après les autres. Toutefois, il met à la ligne un élément visuel si jamais celui-ci tend à dépasser le bord droit ou le bord bas du conteneur selon son orientation (voir Figure 4.5).

Couplé au mode de redimensionnement Auto, propre aux propriétés largeur et hauteur, nous pourrions ajuster les dimensions dynamiquement à chaque remise à la ligne. Par exemple, si nous définissons la hauteur (Height) du WrapPanel sur Auto et son orientation à horizontale et un alignement vertical haut (éviter le mode Stretch dans ce cas, ainsi que les marges), alors le WrapPanel s'ajustera en hauteur pour chaque nouvelle ligne d'éléments visuels. Vous pouvez voir l'exemple précédent mis à jour en utilisant le mode Auto (voir Figure 4.6).

Image non disponible
Figure 4.5 - Principe d'imbrication au sein d'un WrapPanel
Image non disponible
Figure 4.6 - Principe d'imbrication au sein d'un WrapPanelavec la valeur Auto activée

Comme vous pouvez le constater, sur le premier schéma de la Figure 4.6, les rectangles (22, 23, 24) ne sont plus coupés, la hauteur est réajustée en fonction du nombre de lignes. Sur le second schéma, il ne reste plus aucun espace à droite de l'élément 18 car la largeur du WrapPanel s'ajuste au contenu.

4-2-2-2. Créer et configurer le WrapPanel

Pour créer un conteneur de ce type, vous devez utiliser la bibliothèque de composants, car il ne se trouve pas dans la liste des conteneurs proposés par défaut. Cliquez sur la dernière icône de la barre d'outils, celle ressemblant à une double flèche (Image non disponible). Elle vous permet d'ouvrir la bibliothèque de composants. Sélectionnez le mode d'affichage avec grandes icônes pour identifier les contrôles disponibles (voir Figure.4.7).

Image non disponible
Figure 4.7 - Bibliothèque de composants SilverLight

Le champ de recherche sert de filtre, entrez-y les lettres wr. Le contrôle de type Wrap-Panel n'est pas visible dans la liste. C'est en fait très logique, il n'est pas instanciable par défaut au sein des projets Silverlight. Pour y remédier, il vous suffit de télécharger et d'installer la bibliothèque nommée Silverlight Toolkit. Elle est maintenue par l'équipe Silverlight de Microsoft et regroupe de nombreux avantages dont certains contrôles qui ne sont, par défaut, accessibles qu'au sein des projets WPF. Afin d'assurer un maximum de fonctionnalités au sein de Silverlight, ces composants ont été redéveloppés et mis à disposition dans le Silverlight ToolKit. Vous le trouverez à l'adresse : http://www.codeplex.com/Silverlight/. Cliquez sur le menu Download (voir Figure 4.8).

Image non disponible
Figure 4.8 - Le portail CodePlex hébergeant le projet silverlight Toolkit

Une fois la bibliothèque installée, sauvegardez votre projet et relancez Blend. Cette fois-ci, le composant WrapPanel est affiché. Cela prend parfois quelques secondes car Blend doit explorer les nouvelles bibliothèques de contrôles installées. Sélectionnez le contrôle WrapPanel, son icône apparaît juste en dessous de celle permettant l'accès à la bibliothèque. Double-cliquez dessus, une instance de type WrapPanel est automatiquement créée au sein du contexte conteneur sélectionné. Dans notre cas, cela doit être LayoutRoot.

Vous pouvez également accéder à la liste des contrôles via le panneau Assets situé en haut à gauche. Ce panneau est parfois plus pratique d'utilisation, cela dépendra essentiellement de la manière dont vous utilisez Blend.

Nous allons l'ancrer sur les bordures droite, gauche et haute de la grille principale. Pour cela, vous allez définir une largeur et une hauteur en mode Auto, comme nous avons fait pour le menu du bas avec le composant StackPanel. Ensuite, cliquez sur l'icône d'alignement vers le haut et spécifiez une marge de 10 pixels par rapport au bord haut. Cliquez sur l'icône étirement horizontal pour activer ce mode. Définissez des marges à 30 pixels pour la bordure gauche et à 90 pixels pour la bordure droite. Cliquez sur l'icône à droite des propriétés Width et Height. Le WrapPanel n'a plus de hauteur. Il s'est adapté à son contenu qui est vide. Il possède donc une hauteur égale à 0 pixel. Au contraire, comme nous avons défini des marges à gauche et à droite ainsi qu'un alignement horizontal étiré, il possède une largeur égale à la largeur totale de l'application moins les marges (voir Figures 4.9 et 4.10).

Image non disponible
Figure 4.9 - Configuration des options de mise en forme du WrapPanel
Image non disponible
Figure 4.10 - Visuel du composant WrapPanel configuré

Comme vous pouvez le voir, le WrapPanel est ancré en haut, à gauche et à droite. Les icônes représentant un verrouillage indiquent l'ancrage des objets ou non sur les bords du conteneur Grid principal. Elles sont accompagnées de nombres indiquant la valeur en pixel de chaque marge. Elles sont cliquables et permettent de modifier les options d'alignement horizontal et vertical.

4-2-2-3. Ajouter et configurer les boutons du menu

Vous allez maintenant ajouter des boutons correspondant à chacun des menus de la Figure 4.2. Sélectionnez le WrapPanel, puis double-cliquez cinq fois sur l'icône représentant un bouton au sein de la barre d'outils. Vous venez à l'instant d'imbriquer cinq boutons dans le conteneur WrapPanel. Vous auriez pu les dessiner directement au sein de ce conteneur. Cette manière de faire présente l'avantage d'éviter de préciser, pour chaque bouton, les dimensions en hauteur et largeur. Avec cette méthode, celles-ci ont été définies en mode Auto pour chacun d'eux. Les valeurs des propriétés Width et Height s'ajustent en fonction de leur contenu textuel, qui est par défaut la chaîne de caractères Button. Dès cet instant, le WrapPanel possède une hauteur qui s'ajuste à chaque bouton. Si vous définissez une police différente ou une taille différente, le bouton réajustera sa hauteur qui réajustera celle du WrapPanel. Si vous avez défini des marges verticales sur l'un des boutons, celles-ci affecteront directement la hauteur finale du WrapPanel.

Sélectionnez chaque bouton en maintenant la touche Maj enfoncée. Ensuite, au sein du panneau des propriétés, dans l'onglet des propriétés communes, spécifiez la police Trebuchet MS, ainsi qu'une hauteur de caractères de 14 pixels.

Le corps des polices est exprimé par défaut en pixels et non en points. Il est toutefois possible de le modifier. Pour cela, ouvrez le menu Tools > Options… choisissez ensuite l'onglet Units. Dans la liste sélectionnez Points au lieu de Pixels.

Désélectionnez tous les boutons en cliquant n'importe où sur la scène, puis cliquez sur le premier bouton et entrez la chaîne de caractères Nouveautés dans la propriété Content. Procédez de même avec tous les autres afin d'avoir un menu correspondant à la Figure 4.2. Ensuite, créez un dernier bouton à l'extérieur du WrapPanel au sein du conteneur Layout-Root. Il doit être ancré en haut à droite et posséder des marges de 10 pixels en haut et de 8 à droite. Dans sa propriété Content, insérez la chaîne de caractères Plein écran. Ce bouton ne fait pas partie directement du WrapPanel car il ne donne pas accès au même niveau d'utilisation. Il nous permettra de passer alternativement du mode plein écran au mode d'affichage normal. Vous devriez maintenant avoir un visuel similaire à la Figure 4.11.

Image non disponible
Figure 4.11 - Menu du haut avec un bouton plein écran

Pour finir, sélectionnez le UserControl racine et définissez une largeur et une hauteur en mode Auto, cela vous permettra de profiter du navigateur à 100 %. Étirez ensuite la vue de design avec les manipulateurs pour simuler une largeur de 640 pixels et une hauteur de 320 pixels. Voici le code XAML généré en arrière-plan par Blend, pour la totalité de la grille principale :

 
Sélectionnez
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF" Margin="0,0,0,0" >
   <controlsToolkit:WrapPanel Height="Auto" HorizontalAlignment="Stretch" 
        VerticalAlignment="Top" Width="Auto" Margin="30,10,90,0" 
        d:LayoutOverrides="Width">
      <Button Height="Auto" Width="Auto" Content="Nouveautés" 
        Margin="0,0,20,0" FontSize="14" FontFamily="Trebuchet MS" 
        Visibility="Visible"/>
      <Button Height="Auto" Width="Auto" Content="Portfolio" 
        Margin="0,0,20,0" FontSize="14" FontFamily="Trebuchet MS" 
        Visibility="Visible"/>
      <Button Height="Auto" Width="Auto" Content="Médias" 
        Margin="0,0,20,0" FontSize="14" FontFamily="Trebuchet MS" 
        Visibility="Visible"/>
      <Button Height="Auto" Width="Auto" Content="Savoir faire" 
        VerticalAlignment="Center" Margin="0,0,20,0" FontSize="14"     
        FontFamily="Trebuchet MS" Visibility="Visible"/>
      <Button Height="Auto" Width="Auto" Content="Contact" 
        Margin="0,0,0,0" FontSize="14" FontFamily="Trebuchet MS" 
        Visibility="Visible"/>
   </controlsToolkit:WrapPanel>
   <StackPanel Height="Auto" HorizontalAlignment="Right" Margin="0,0,30,8" 
        VerticalAlignment="Bottom" Orientation="Horizontal">
      <TextBlock Text="A propos " TextWrapping="Wrap" Margin="0,0,30,0" 
            FontFamily="Trebuchet MS" Foreground="#FF6C6C6C" 
            FontWeight="Bold" FontSize="12" 
        TextDecorations="Underline"/>
      <TextBlock Text="Qui sommes nous ? " TextWrapping="Wrap" 
            Margin="0,0,30,0" FontFamily="Trebuchet MS" 
            Foreground="#FF6C6C6C" FontWeight="Bold" FontSize="12"/>
      <TextBlock Text="Newsletter" TextWrapping="Wrap" 
            FontFamily="Trebuchet MS" Foreground="#FF6C6C6C" 
            FontWeight="Bold" FontSize="12"/>
   </StackPanel>
   <Button Height="45" Width="60" Content="Plein écran" Margin="0,10,8,0" 
        HorizontalAlignment="Right" VerticalAlignment="Top" FontSize="10" 
        Visibility="Visible"/>
   <Grid Margin="30,70,30,50" Background="#FF8C8C8C" 
        Visibility="Collapsed">
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" 
        Text="Contenu des Pages" TextWrapping="Wrap" FontSize="36" 
        FontFamily="./segoepr.ttf#Segoe Print" Foreground="#FFE2E0E0" 
        FontWeight="Bold"/>
   </Grid>
</Grid>

En très peu de lignes, nous sommes parvenus à créer une interface redimensionnable. Vous remarquez que la balise WrapPanel est préfixée de controlsToolkit. Cela signifie qu'il n'est pas référencé dans l'assembly System.Windows.dll par défaut. En réalité, lorsque vous avez ajouté ce composant dans l'application, Blend a automatiquement référencé deux bibliothèques issues du Silverlight ToolKit : System.Windows.Controls.dll et System.Windows.Controls.Toolkit.dll. Pour le vérifier, il suffit d'ouvrir votre panneau projet et de déplier le répertoire Reference. Blend a également fait en XAML l'équivalent d'un using C# pour référencer l'espace de noms controlsToolkit. Voici le code ajouté dans le UserControl racine :

 
Sélectionnez
xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Toolkit"

Lorsque vous concevrez et partagerez vos composants personnalisés, les personnes désirant les utiliser agiront de la même manière. Elles devront donc ajouter la bibliothèque contenant vos composants en tant que nouvelle référence. Toutefois Blend fait ce travail pour vous dans la plupart des situations. Pour l'instant, ce visuel est un peu brut, mais nous apprendrons rapidement à créer des composants et des styles personnalisés.

4-2-3. Créer la grille centrale

Vous allez créer un conteneur Grid situé au centre de la grille principale. Il contiendra le contenu des pages de notre site plein écran. Sélectionnez le conteneur LayoutRoot pour le définir comme contexte conteneur, puis dessinez une grille au centre.

4-2-3-1. Gérer le redimensionnement

Affectez à cette grille une hauteur et une largeur automatique avec un alignement étiré (Stretch). Pour éviter qu'elle ne recouvre les autres objets, elle doit posséder des marges d'environ 30 pixels à gauche, 60 pixels en haut, 30 pixels à droite et 30 pixels en bas (voir Figure 4.12).

Image non disponible
Figure 4.12 - Paramétrage de l'alignement de la grille centrale

Pour finir, créez un composant TextBlock centré et sans marges au sein de la grille avec dimensions en mode Auto.

4-2-3-2. Modifier la couleur d'arrière-plan

Afin de rendre la grille plus visible lors de la compilation, définissez une couleur
d'arrière-plan gris clair. Pour cela sélectionnez, en haut du panneau des propriétés, l'attribut Background présent dans l'inspecteur de couleurs. Ensuite, cliquez sur la deuxième icône en partant de la gauche. Elle permet de spécifier une couleur unie (voir Figure 4.13).

Comme vous pouvez le remarquer, le code couleur hexadécimal est codé sur quatre octets soit #FFFFFFFF.

Image non disponible
Figure 4.13 - Paramétrage de la couleur d'arrière-plan de la grille centrale

Un octet représente 8 bits, c'est pour cela que l'on parle d'images 32 bits (4x8). Un bit possède une valeur 0 ou 1. Une valeur 8 bits représente donc une combinaison de 8 chiffres possibles de 0 ou 1, soit 28 possibilités. En hexadécimal, cela se traduit par FF, ce qui donne en tout 256 possibilités, de 0 à 255. Dans la plupart des logiciels de graphisme, la couche de transparence n'est pas directement affichée dans le code hexadécimal mais elle est plutôt affichée sur une échelle qui va de 0 à 100 % d'opacité. Blend propose les deux affichages, ce qui évite les ambiguïtés. La couleur #00FF0000 ne sera donc pas visible, car le premier couple de 0 indique qu'il n'y a aucune opacité.

4-3. Le composant bouton

Étudier les propriétés du composant bouton, permet de comprendre de nombreux principes propres au framework Silverlight, car celles-ci sont communes à de nombreux autres composants.

4-3-1. Définir un nom d'exemplaire

Donner un nom d'exemplaire aux objets permet d'y accéder facilement depuis le code logique. Une fois nommés, les objets deviennent des champs de la classe associée au UserControl. Ces champs sont définis par défaut avec le modificateur d'accès internal. Ainsi vous pourrez atteindre leurs méthodes, cibler leurs propriétés dynamiquement ou encore écouter les événements qu'ils diffusent (comme le clic de la souris). Sélectionnez le bouton plein écran, celui-ci ne possède pour l'instant pas de nom d'instance. Pour savoir si un composant possède un nom d'exemplaire, il suffit de regarder dans l'arbre visuel. Si le composant est décrit par son type entre crochet, cela signifie qu'il n'est pas nommé. Vous pouvez également regarder dans le panneau des propriétés si son attribut Name, situé tout en haut, est défini. Définir un nom d'instance est assez simple, vous pouvez soit double-cliquer sur l'occurrence directement dans l'arbre visuel, soit insérer une chaîne de caractères dans sa propriété Name. Définissez Plein-EcranBT comme nom d'instance. Par la suite, nous définirons un comportement propre à ce bouton pour passer en mode plein écran (voir Figure 4.14).

Image non disponible
Figure 4.14 - Différence d'affichage entre objets nommés et anonymes

4-3-2. Afficher une bulle d'information au survol

On peut assigner un ToolTip à tout élément visuel. Cette propriété est dite attachée, elle n'est pas définie sur l'objet, elle est fournie par la classe statique ToolTipService. Pour mieux comprendre ce principe, insérez la phrase suivante dans l'attribut ToolTip : « Ce bouton permet de passer d'un mode d'affichage normal à un mode d'affichage plein écran ». Ouvrez le mode d'édition XAML. Voici ce que vous pouvez voir dans la balise Button :

 
Sélectionnez
ToolTipService.ToolTip="Ce bouton permet de passer d'un mode d'affichage 
                  normal à un mode d'affichage plein écran"

Compilez votre application en appuyant sur le raccourci F5 (voir Figure 4.15).

Image non disponible
Figure 4.15 - Affichage de l'infobulle au survol du bouton

Comme vous pouvez le constater, la propriété ToolTip est fournie par ToolTipService :

 
Sélectionnez
ToolTipService.ToolTip=""

Pour finir, si vous souhaitez aligner le bouton Newsletter et le bouton plein écran verticalement à gauche (voir Figure 4.15), il suffit de définir une marge de 30 pixels à droite pour le bouton PleinEcranBT.

4-3-3. Choisir un curseur de survol personnalisé

Tout élément visuel possède par défaut la propriété Cursor. Celle-ci permet de définir un curseur système lors du survol d'un objet. Selon le système d'exploitation sur lequel vous serez, vous aurez donc des curseurs avec des visuels différents. Cela peut vous permettre, par exemple, d'indiquer à l'utilisateur un chargement de données en cours de progression. Il vous suffirait de spécifier un curseur d'attente (Wait) pour une liste de données. Lors de la réception des données, vous pourriez ensuite redéfinir un curseur standard. Cette propriété prend comme valeur une constante de la classe Cursors (au pluriel).

4-3-3-1. Définir un curseur en C#

Si vous n'êtes pas un familier de .Net, cette méthode est intéressante car elle permet de redéfinir à l'exécution le curseur d'un objet. La classe statique Cursors liste tous les curseurs disponibles lors du survol d'un objet graphique. Double-cliquez sur MainPage.xaml.cs et insérez le code en gras suivant :

 
Sélectionnez
public partial class MainPage : UserControl
{
   public MainPage()
   {
      InitializeComponent();
      PleinEcranBT.Cursor = Cursors.Hand;
   }
}

Assigner une valeur dans le XAML est équivalent à affecter une valeur dans le constructeur. Pour rappel, le constructeur d'une classe représente sa méthode d'initialisation, celle qui se lance à l'instanciation de l'objet. L'intérêt d'affecter les valeurs dans le code C# est de coupler l'assignation de ces valeurs à une logique métier.

4-3-3-2. Définir un curseur avec Expression Blend

Expression Blend permet de simplifier le processus pour accomplir cette opération. Il vous suffit d'ouvrir le panneau des propriétés propre au bouton plein écran, puis de définir un curseur de type Hand (voir Figure 4.16).

Image non disponible
Figure 4.16 - Affectation d'un curseur de survol

4-3-4. La propriété Content

La propriété Content est assez difficile à cerner au premier coup d'œil. Jusqu'à maintenant, nous l'avons utilisée pour afficher un texte au sein des boutons, ce qui constitue son comportement par défaut. Nous pourrions donc nous demander pourquoi cette propriété ne se nomme pas Label ou Title. Dans les faits, elle est capable de stocker beaucoup plus qu'une simple chaîne de caractères. Elle est typée Object, ce type représente la classe située au niveau le plus haut de l'arbre d'héritage. Des classes aussi différentes que String, Panel ou Button-Base héritent par conséquent de la classe Object. La propriété Content permet donc d'afficher n'importe quel objet visuel ou non. Si l'objet à afficher n'est pas de type visuel (UIElement), il est converti en chaîne de caractères de type string, et cette dernière est affichée. Toutefois, la propriété Content ne peut contenir qu'un seul enfant. Tous les objets héritant de la classe abstraite ContentControl héritent par défaut de la propriété Content. Nous y reviendrons au Chapitre 5 L'arbre visuel et logique dédié à l'arbre visuel et logique.

Une classe abstraite est une classe dont on ne peut pas créer d'instances directement. Ces classes servent de modèle et contiennent du code qui sera, soit surchargé par les classes qui en héritent, soit directement réutilisé. Panel, ButtonBase, RangeBase sont des classes abstraites qui sont héritées par de nombreuses classes - classes dont on peut créer des instances. Par exemple, Slider ou ProgressBar sont des classes que l'on peut instancier et qui héritent des méthodes et des propriétés de la classe abstraite RangeBase. En programmation orientée objet, une classe abstraite est l'un des moyens mis en œuvre en vue de la réutilisation. Toutefois, l'héritage multiple n'étant pas permis (contrairement à C++), ce n'est pas toujours le moyen le plus souple.

4-3-4-1. Créer l'icône de redimensionnement

Nous allons créer une icône de redimensionnement que nous placerons au sein du bouton plein écran. Cette icône est très simple à réaliser, et elle va nous permettre d'apprendre les opérations de base que Blend propose pour les tracés vectoriels (voir Figure 4.17).

Image non disponible
Figure 4.17 - Icône de redimensionnement

Dans un premier temps, faites un carré de 16 pixels de côté via la primitive Rectangle. Ensuite, légèrement en dessous, créez deux rectangles allongés de 6 pixels de hauteur par 14 de longueur. Alignez-les horizontalement côte à côte en gardant un espace entre eux de 8 pixels (voir Figure 4.18).

Image non disponible
Figure 4.18- Création de l'icône, étape 1

Une fois les rectangles réalisés, sélectionnez-les, puis ouvrez le menu Object et, au sein de l'onglet Combine, cliquez sur l'option Unite. Vous obtenez une primitive de type Path. Les deux rectangles ont donc été convertis en tracé vectoriel. Dans l'arbre visuel, en lieu et place des deux rectangles, vous remarquez qu'est apparu un objet [Path]. Sélectionnez le carré, puis le nouveau tracé et cliquez droit dessus. Grâce au menu Align, centrez les deux objets horizontalement et verticalement. Ensuite, au sein du panneau des propriétés, dans l'onglet des transformations RenderTransform, cliquez sur la deuxième icône en partant de la gauche. Vous ouvrez ainsi le panneau qui gère la rotation des objets. L'onglet RenderTransform est commun à tous les objets graphiques. Entrez la valeur de 45 degrés dans le champ Rotation (voir Figure 4.19).

Image non disponible
Figure 4.19 - Création de l'icône, étape 2

Pour finir, désélectionnez tout, et dans l'ordre, cliquez sur le tracé (les deux anciens rectangles), puis sur le carré, en maintenant la touche Maj enfoncée. Ensuite, ouvrez le menu Object, puis Combine et sélectionnez l'option Substract. Le carré s'est vu retirer tout l'espace occupé par le tracé vectoriel (voir Figure 4.16). Il ne nous reste plus qu'à insérer cette image dans notre bouton plein écran en l'affectant à sa propriété Content. Cela est assez simple à faire : glissez l'icône au-dessus du bouton. Dès que le message vous indiquant la possibilité d'imbriquer l'élément apparaît, cliquez sur la touche Alt et relâchez le bouton de la souris. C'est fait, l'icône est intégrée au bouton plein écran. Afin d'obtenir un visuel élégant, vous pouvez redimensionner le bouton à 18 pixels de hauteur et de largeur, puis sélectionner le tracé à l'intérieur et lui affecter des dimensions en mode Auto. Cette dernière modification lui permet de s'adapter à son conteneur (le bouton). Pour ma part, j'ai également défini des marges extérieures (Margin) en haut et à droite de 8 pixels sur le bouton (voir Figure 4.20).

Image non disponible
Figure 4.20 - Application finale sans interactivité

Finalement, on considère les objets bénéficiant de la propriété Content comme des conteneurs à enfant unique. Au sein de la plate-forme Silverlight, de nombreux composants utilisateur en bénéficient. Les primitives échappent assez logiquement à cette particularité. Le site, sans interaction logique, est disponible en ouvrant pleinEcran_maquette.zip dans le chap4 des exemples du livre.

4-3-4-2. Affecter la propriété Content en C#

Vous l'aurez compris, au sein de Silverlight presque tous les objets peuvent être imbriqués les uns dans les autres. Mais comment faire en C# pour affecter la propriété Content avec un élément existant ou instancié dynamiquement ?

Pour répondre à cette question, nous allons affecter un composant Image à la propriété Content du bouton Contact. Ce type de composant permet d'afficher des images bitmap. Pour cela, nommez le bouton ContactBtn, puis téléchargez dans votre répertoire projet l'image IconeContact.png, présente dans le dossier chap4 des fichiers d'exemples. Ensuite, au sein de Blend, cliquez droit sur le projet et sélectionnez l'option Add Existing Item… Dans la fenêtre d'exploration, sélectionnez l'image téléchargée et cliquez sur OK. Cette opération vous a permis d'ajouter l'image en tant que ressource. Double-cliquez sur MainPage.xaml.cs. Voici le code C# commenté permettant d'affecter l'image à la propriété Content :

 
Sélectionnez
public MainPage()
{
   InitializeComponent();
   PleinEcranBT.Cursor = Cursors.Hand;
   //on écoute l'événement Loaded diffusé lors du premier affichage
   //du UserControl soit this
   Loaded +=new RoutedEventHandler(MainPage_Loaded);
}

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{

   // 1 - ici on crée un composant Image
   Image myImage = new Image();
   // 2 - puis on définit son mode de redimensionnement
   myImage.Stretch = Stretch.None;
   // 3 - on crée ensuite un objet Uri qui pointe vers 
   // notre fichier image à utiliser
   Uri adresseImage = new Uri("IconeContact.png",UriKind.Relative);
   // 4 - on crée un objet de type ImageBitmap en lui spécifiant 
   // l'adresse relative que nous venons de définir
   BitmapImage bi = new BitmapImage( adresseImage );
   //5 - on précise au composant la source de l'image qu'il doit afficher
   myImage.Source = bi;
   //6 - pour finir, on ajoute le composant Image à la 
   // propriété Content de notre bouton Contact
   ContactBtn.Content = myImage;
}

Vous remarquez que l'image n'a pas été copiée dans le répertoire ClientBin. En réalité, elle a été compilée au sein du fichier xap, plus exactement dans le fichier dll contenu dans le xap. Il faudra toujours utiliser un objet de type BitmapImage que l'on affectera ensuite à la propriété Source du composant Image. Finalement, la partie la plus simple est d'attribuer le composant Image à la propriété Content de notre bouton. Pour affecter un composant plus complexe, tel que Grid, à cette propriété, nous aurions simplement pu écrire ContactBtn.Content = new Grid();. Cette grille aurait pu, à son tour, contenir des objets visuels, il n'y a pas limite. Le code n'est pas entièrement expliqué car nous ferons référence au mode redimensionnement ainsi qu'au chargement d'image ultérieurement.

4-4. Ajouter de l'interactivité

Nous allons, dans un premier temps, utiliser les comportements. Depuis sa version 3, Blend permet de coder dans les langages C# et Visual Basic. Comme Visual Studio, il propose une aide au code IntelliSense, pour ces deux langages ainsi que pour le code déclaratif XAML. Nous utiliserons le C# pour passer du mode plein écran au mode d'affichage normal.

4-4-1. Utiliser les comportements

Nous allons nous contenter de simuler le changement de page. À chaque fois que nous cliquerons sur un bouton, le champ texte situé au milieu de la page sera mis à jour. Nommez le champ texte contenu dans la grille au centre de notre site, TitrePage. Ensuite, ouvrez la bibliothèque, puis l'onglet Behaviors. Celui-ci contient une liste de comportements. Glissez, puis déposez le comportement [ChangePropertyAction] sur le premier bouton du menu. Le comportement apparaît dès lors dans l'arbre visuel (voir Figure 4.21).

Image non disponible
Figure 4.21 - Ajout d'un comportement au premier menu

Nous allons maintenant configurer ce comportement pour qu'il change le champ texte TitrePage lors d'un clic sur le bouton. Pour cela, après avoir sélectionné le comportement dans l'arbre visuel, ouvrez le panneau des propriétés. À chaque fois que l'utilisateur cliquera sur le premier menu, nous affecterons dynamiquement la propriété Text du champ TitrePage avec la valeur Nouveautés (voir Figure 4.22).

Image non disponible
Figure 4.22 - Configuration du comportement

L'événement Click représente l'interaction utilisateur qui déclenchera le changement de valeur. Le paramètre SourceName est optionnel, il indique quel objet est à la source de l'événement diffusé. Comme le comportement est créé sur le bouton, c'est lui qui, par défaut, diffusera l'événement Click. PropertyName permet de spécifier la propriété affectée lors du Click. Pour modifier le contenu textuel d'un composant TextBlock, il faut affecter la propriété Text. Le champ Value représente la valeur de cette propriété. TargetName est l'objet ciblé, dans notre cas c'est le champ texte TitrePage. L'icône avec les trois points, située à droite du champ de saisie (), vous permet de naviguer dans l'arbre visuel et logique afin de rechercher l'objet cible. Vous devez maintenant répéter cette opération pour les autres menus. Pour accomplir cette tâche rapidement, vous pouvez copier-coller ce comportement sur les autres menus, puis changer le paramètre Value pour chaque menu.

4-4-2. Mode plein écran

Le mode d'affichage plein écran est très pratique pour certains types d'applications, par exemple pour afficher de la vidéo ou un contenu nécessitant une grande résolution. Cela permet à l'utilisateur de s'immerger complètement dans l'application en faisant abstraction des menus propres au navigateur. Pour des raisons de sécurité, certaines possibilités du lecteur Silverlight sont désactivées dans ce mode. Pour passer d'un affichage normal au mode plein écran, une seule ligne de code C# et un paramétrage XAML suffisent.

La première chose à faire est de définir une méthode C# qui se déclenchera lorsque l'événement Click est diffusé par le bouton plein écran. Pour cela, sélectionnez ce bouton, puis ouvrez le panneau des propriétés. Cliquez sur l'icône en forme d'éclair qui se trouve en haut à droite de ce panneau. Vous venez d'ouvrir la liste des événements disponibles pour le bouton plein écran. Pour l'événement Click, définissez la méthode SetFullScreen (voir Figure 4.23).

Image non disponible
Figure 4.23 - Le panneau Events

Voici le code du bouton généré en XAML :

 
Sélectionnez
<Button x:Name="PleinEcranBT" Height="18" Width="18" Margin="0,8,8,0"
HorizontalAlignment="Right" VerticalAlignment="Top" FontSize="10" 
    Visibility="Visible" ToolTipService.ToolTip="Ce bouton permet de 
    passer d'un mode d'affichage normal à un mode d'affichage plein écran" 
    BorderThickness="1,1,1,1" BorderBrush="#FFACACAC" Click="SetFullScreen" >
   <Path Stretch="Fill" Stroke="{x:Null}" Height="Auto" Width="Auto" 
        UseLayoutRounding="False" Data="M4.242609,0 L16,0 L16,11.757387 
        L12.94972,8.7071075 L8.7070818,12.949746 L11.757336,16 L0,16 
        L0,4.2426682 L3.0502257,7.2928939 L7.2928643,3.0502553 z">
      <Path.Fill>
         <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="#FFC1C1C1" Offset="0"/>
            <GradientStop Color="#FF9E9E9E" Offset="1"/>
         </LinearGradientBrush>
      </Path.Fill>
   </Path>
</Button>

XAML permet de déclarer des méthodes d'écoute sur les événements diffusés. Ce n'est pas toujours l'idéal, car C# offre plus de souplesse de ce point de vue. Toutefois, dans une certaine mesure, cela donne aux designers interactifs une certaine autonomie. En phase finale de développement, c'est plutôt au développeur de gérer la diffusion et l'écoute des événements. Les performances de l'application sont en partie liées à la notion de programmation événementielle (voir Chapitre 7 Interactivité et modèle événementiel). Lorsque vous avez défini la fonction d'écoute, Blend a automatiquement créé une méthode correspondante dans le fichier MainPage.xaml.cs et a ouvert ce fichier. Supprimez la ligne commentée et remplacez-la par la ligne en gras du code suivant :

 
Sélectionnez
private void SetFullScreen(object sender, RoutedEventArgs e)
{
   App.Current.Host.Content.IsFullScreen = 
       !App.Current.Host.Content.IsFullScreen;
}

Le code précédent est déclenché lorsque l'utilisateur clique sur le bouton. La ligne C# en gras est assez simple, la propriété booléenne IsFullScreen est affectée de l'opposé de sa valeur actuelle. Par exemple, si celle-ci est false (donc si l'application n'est pas en mode plein écran), alors elle passe en mode plein écran. Si elle est true, c'est-à-dire en mode plein écran, alors on repasse en mode normal. Cette syntaxe nous a évité d'utiliser une condition if, ainsi qu'un code fastidieux. Testez votre site, redimensionnez la fenêtre du navigateur et cliquez sur les menus. Le positionnement et l'affichage des menus s'adaptent automatiquement, même au sein d'une fenêtre de 640 par 480 pixels. Le WrapPanel gère le retour à la ligne des menus, si besoin est. Pour finir, cliquez sur le bouton plein écran plusieurs fois pour vérifier que le code logique C# correspond au besoin.

4-4-3. Spécificités du mode plein écran

Pour des raisons de sécurité, dès lors que vous êtes passés en mode plein écran, un message vous l'indiquant est apparu durant quelques secondes (voir Figure 4.24).

Image non disponible
Figure 4.24 - Message d'indication plein écran

Ce n'est pas la seule contrainte de sécurité imposée par le mode plein écran. Ce mode ne peut pas être défini par d'autres événements que ceux découlant des interactions utilisateur. Ainsi, vous ne pouvez pas forcer le mode plein écran dès le démarrage de l'application, par exemple. Dans ce cas, affecter la propriété IsFullScreen ne provoque aucun changement d'affichage. Le mode plein écran limite également l'utilisation du clavier. Cela évite à l'utilisateur de saisir des données confidentielles. Ce mode d'affichage peut en effet simuler n'importe quel type d'interface. Voici la liste des touches auxquelles vous pouvez accéder :

  • le pavé des flèches directionnelles (Up, Down, Left et Right) ;
  • la barre d'espace (Spacebar) ;
  • la touche Tabulation (Tab) ;
  • les touches Page Précédente et Suivante (Page Up, Page Down) ;
  • la touche Maison et Fin (Home, End) ;
  • la touche Entrée (Enter).

Plusieurs instances du plug-in Silverlight ne peuvent pas être en même temps en mode plein écran. En réalité, lorsqu'une application Silverlight plein écran perd le focus, celle-ci revient à un mode d'affichage normal. Par exemple, le simple fait, sur Windows, de basculer via la combinaison de touches Alt+Tab d'une application à une autre, fait perdre le focus à l'application Silverlight. Deux applications Silverlight ne peuvent donc pas être en mode plein écran en même temps, car l'une d'entre elles a forcément perdu le focus utilisateur.

La dernière particularité du mode d'affichage plein écran est d'empêcher l'affichage d'applications transparentes (via la propriété windowless). L'arrière-plan de l'application sera alors complètement opaque. Lorsque l'utilisateur quittera ce mode, l'application pourra à nouveau bénéficier de la transparence.

4-4-4. Détecter le changement d'affichage

Il peut être très utile d'écouter l'événement FullScreenChanged de l'instance du plug-in. Cet événement est déclenché lors du changement de mode. Le code suivant permet d'afficher la résolution de l'écran utilisateur dans le champ TitrePage à chaque changement de mode :

 
Sélectionnez
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{//cette ligne permet d'écouter l'événement FullScreenChanged
   App.Current.Host.Content.FullScreenChanged += new System.
                                EventHandler(Content_FullScreenChanged);
}
private void Content_FullScreenChanged(object sender, EventArgs e)
{
   double l = App.Current.Host.Content.ActualWidth;
   double h = App.Current.Host.Content.ActualHeight;
   TitrePage.Text = "largeur :: " + l + " - hauteur :: " + h;
}

Comme vous le constatez, les dimensions de l'application ne sont pas récupérées grâce aux propriétés Width et Height car celles-ci ont été définies en mode Auto. Par contre, elles peuvent l'être via les propriétés ActualWidth et ActualHeight. Ces propriétés contiennent les valeurs absolues en pixels du conteneur racine UserControl. Le Chapitre 7 Interactivité et modèle événementiel fournit plus d'information sur l'écoute d'événements.

Un autre événement existe, Resized, mais il n'est pas diffusé lors du changement de mode d'affichage. Il est diffusé lors que l'utilisateur redimensionne l'instance du plug-in Silverlight. Il permet, par exemple, de repositionner des objets contenus de manière animée en fonction des nouvelles dimensions du plug-in.

4-5. Fichiers déployés

Les fichiers à déployer sont tous contenus dans le projet AgencePortfolioSite. Pour déployer votre application, il vous suffit de copier le contenu de ce répertoire sur votre serveur Web via une connexion FTP. Au sein de votre répertoire distant, vous devrez donc avoir les fichiers :

  • Default.html, la page HTML qui contient l'application Silverlight ;
  • Silverlight.js, fichier JavaScript qui facilite et améliore l'intégration de Silverlight ;
  • ClientBin/AgencePortfolio.xap, le fichier Silverlight compilé.

Le site finalisé est disponible dans l'archive pleinEcran_interactif.zip du chap4 des exemples.

Au prochain chapitre, nous étudierons les mécanismes liés à l'arbre visuel et logique, ainsi que les méthodes permettant de manipuler la liste d'affichage. Comme nous le verrons, Blend propose de nombreux outils permettant aux designers de faire des applications contextuelles capables d'adapter l'affichage aux besoins et contraintes des utilisateurs.


précédentsommairesuivant
La réutilisation est un principe connu des développeurs, qui consiste à réutiliser le code ou les fonctionnalités déjà conçues.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par Eric Ambrosi et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.