| |  |
Ce site n'est pas un site sur le cyclimse.
Merci de votre compréhension. |
mardi 13 mai 2008 |
| mardi 11 septembre 2007 |
| 11:08 » Silverlight - programmation, silverlight |
Comme la techno m'intriguait, j'ai passé un petit week-end au début du mois à m'amuser avec Silverlight, le pseudo-Flash de Microsoft. J'ai donc réalisé une petite interface pour ma collection de CDs, qui pour une raison que j'ignore ne fonctionne pas sous Firefox. Je vous conseille donc fortement de la consulter avec Internet Explorer, ou, si vous ne pouvez pas vous passer du Renard Qui A Le Feu Au Cul, avec IE Tab, la géniale extension qui permet de consulter des pages via IE sans quitter Firefox.
Comme d'hab, la première page vous présente la liste de CDs sous forme d'images de leurs pochettes respectives :

Lorsqu'on clique sur une de ces pochettes, un cadre contenant des informations complémentaires s'affiche. Ces infos sont récupérées grâce aux web services d'amazon.

Je vous préviens tout de suite, ce n'est absolument pas terminé : il n'y a entre autres pas de scrollbars si le contenu de la page dépasse la taille de son contenant, il est préférable d'attendre que toutes les images soient chargées avant de cliquer partout, et je vous conseille d'attendre quelques secondes s'il ne se passe rien après que vous ayez cliqué sur la pochette d'un CD.
Hop, un petit lien pour télécharger les sources si vous avez envie de vous plonger là-dedans.
Il s'agit de Silverlight 1.0, autrement dit tout le code est en Javascript (la version 1.1 de Silverlight permet d'utiliser les langages du framework .NET).
Vous y trouverez les fichiers suivants :
- Default.html : c'est sur cette page que vous vous rendez pour voir l'application. Elle contient les références aux fichiers Javascript ainsi qu'un appel à la fonction createSilverlight(), qui crée le contrôle Silverlight sur la page.
- Silverlight.js : ce fichier, fourni avec le sdk de Silverlight, définit quelques méthodes nécessaires au bon fonctionnement de l'application (notamment CreateObject, mais on y reviendra).
- Default.html.js : contient la fonction createSilverlight, qui est appelée par Default.html pour créer le contrôle Silverlight. Elle contient également la méthode Silverlight.createDelegate (on y reviendra également). Je ne sais plus trop comment j'ai obtenu ce fichier, il me semble que Visual Studio me l'a généré. Au pire, la fonction createSilverlight est disponible dans le fichier CreateSilverlight.js du sdk.
- Scene.xaml : ce fichier définit l'interface graphique générale.
- Scene.xaml.js : contient le code qui peuple la page avec les images des CDs.
- ImageButton.xaml : contient la définition graphique du contrôle chargé d'afficher l'image d'un pochette.
- ImageButton.js : contient le code lié au contrôle précédent, qui décrit notamment la réaction à certains évènements.
- CDInfo.xaml : contient la définition graphique du cadre qui est affiché lorsqu'on clique sur une pochette de CD.
- CDInfo.js : contient le code qui peuple le cadre et qui gère la réaction à certains évènements.
Pour vous expliquer un peu le code, nous allons suivre le déroulement du programme.
Au début il n'y avait rien. Puis un navigateur web se rendit à la page Default.html. Cette page, comme je l'ai écrit plus haut, appelle la fonction createSilverlight() pour créer le contrôle Silverlight. Vous pouvez éditer cette fonction afin de customiser le contrôle qui va être créé :
function createSilverlight()
{
var scene = new Peuw.Scene();
Silverlight.createObjectEx({
source: 'Scene.xaml',
parentElement: document.getElementById('SilverlightPlugInHost'),
id: 'SilverlightPlugIn',
properties: {
width: '100%',
height: '100%',
background:'#FF000000',
isWindowless: 'false',
version: '1.0'
},
events: {
onError: null,
onLoad: Silverlight.createDelegate(scene, scene.handleLoad)
},
context: null
});
}
La fonction commence par instancier la classe Peuw.Scene (définie dans le fichier Scene.xaml.js), qui correspond à la "fenêtre" principale de l'application. La fonction createObjectEx est ensuite appelée, pour créer le contrôle Silverlight, et c'est son appel que nous pouvons modifier. Le paramètre source indique quel fichier xaml doit être chargé au démarrage (si le terme "xaml" ne vous dit rien, sachez qu'il s'agit du langage utilisé pour décrire la partie graphique des applications Silverlight (et WPF) ). Le paramètre properties vous permet de customiser l'apparence du contrôle. Ici, nous avons demandé un contrôle qui occupe la totalité de l'espace disponible et dont la couleur d'arrière-plan est le noir (les couleurs sont indiquées comme en html, avec un premier octet supplémentaire pour la transparence). Le paramètre events permet de définir quelles méthodes répondront à quels évènements (il s'agit bien sûr uniquement des évènements liés au contrôle Silverlight, on ne doit pas tous les définir ici). Ici, nous demandons à ce que l'évènement onLoad soit géré par la méthode scene.handleLoad (ce qui explique pourquoi la classe Peuw.Scene a été instanciée), qui servira donc de point d'entrée au programme.
Notez l'utilisation de la méthode Silverlight.createDelegate, qui facilite le branchement d'une méthode d'instance sur un évènement (elle retourne une fonction correspondant à l'appel objet.methode, objet et methode étant passés en paramètre).
Ceci fait, notre contrôle Silverlight est créé. Que se passe-t-il ensuite ? L'évènement Load du contrôle Silverlight est déclenché, ce qui a pour effet d'exécuter la méthode que nous avons indiqué dans createSilverlight().
handleLoad: function(plugIn, userContext, rootElement)
{
this.plugIn = plugIn;
this.root = plugIn.content.findName("Main");
this.CDs = plugIn.content.findName("CDs");
this.data = "";
var leecherData = this.plugIn.createObject("downloader");
this.tokenDownloader = leecherData.addEventListener("completed", Silverlight.createDelegate(this, this.GetCDData));
leecherData.open("GET", "/json-server.php5?action=GetCDs");
leecherData.send();
}
Cette méthode a pour but, en plus de faire un peu d'initialisation, de lancer la récupération de la liste des CDs au format JSON (JavaScript Object Notation, qui permet de représenter des objets Javascript en simple texte).
Elle commence donc par mettre de côté une référence au contrôle Silverlight, qui nous est donnée dans le paramètre plugIn. On récupère ensuite des références vers le cadre principal de l'application et celui qui va contenir les images des pochettes de CD. Pour mieux comprendre ces deux lignes, regardons le code xaml qui est chargé :
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Main" Width="640" Height="480">
<Canvas x:Name="CDs">
</Canvas>
</Canvas>
La balise Canvas représente un canevas sur lequel nous allons pouvoir disposer des contrôles graphiques. Nous en avons défini un à la racine du document avec le nom "Main", qui est défini par l'attribut x:Name. Nous lui avons également donné une taille, mais ce n'est pas tellement important puisque, même si nous ajoutons des contrôles à des coordonnées qui "dépassent" du canevas, ils seront affichés magré tout. Dans ce canevas, nous avons créé un second canevas portant le nom "CDs", qui contiendra les images des pochettes de CD.
Revenons à présent au code Javascript : pour récupérer une référence vers un élément graphique, nous utilisons la méthode findName. Elle prend en paramètre le nom de l'élément qu'on souhaite récupérer : ce nom correspond à ce qui a été défini dans les attributs x:Name du code xaml. Nous appelons cette méthode depuis la propriété content de l'objet représentant le contrôle Silverlight, mais elle est disponible sur n'importe quel contrôle, et l'élément dont le nom est passé en paramètre est recherché dans l'intégralité de l'arbre des contrôles et pas uniquement dans les descendants de l'élément à partir duquel l'appel est réalisé, on peut donc y aller comme des foufous :)
Nous en arrivons à présent à la classe downloader, dit "L'Indispensable". On en crée une instance en appelant la méthode createObject de l'objet correspondant au contrôle Silverlight, qui à ma connaissance n'a pour l'instant pas d'autre utilité. L'objet que nous obtenons permet de télécharger un contenu depuis une url. Le contenu doit être dans le même domaine que l'application Silverlight : l'objet downloader est basé sur l'objet XMLHttpRequest et hérite donc de l'impossiblité de faire des requêtes cross-domain. Nous utilisons cet objet pour récupérer la sortie d'un script php qui nous renvoie la liste des CDs ainsi que quelques infos, le tout au format JSON.
Si vous souhaitez tester l'application en local et que ne voulez pas vous embêter avec votre serveur web, j'ai inclus deux fichiers texte dans l'archive contenant les sources. Ces deux fichiers contiennent le JSON correspondant à la liste de CDs (cds.txt) et celui des détails d'un CD (review-tracks.txt).
Revenons à nos moutons : notre downloader est créé, mais avant de lui faire récupérer des choses, nous ajoutons un handler pour l'évènement completed, qui survient lorsque le téléchargement est terminé. En effet, le downloader effectue ses tranferts de manière asynchrone, nous devons donc forcément gérer l'évènement completed si on souhaite récupérer les données téléchargées.
On indique ensuite à l'objet quoi télécharger, par le biais de la méthode open, qui prend en premier paramètre la méthode http à utiliser (pour nous, "GET"), et en second l'adresse de la ressource à récupérer. Ici, nous avons inscrit l'adresse du script php qui nous renvoie les données au format JSON.
Enfin, on lance le transfert grâce en appelant la méthode send.
Voyons à présent ce qui se passe lorsque le téléchargement est terminé :
GetCDData: function(sender, eventArgs)
{
this.data = eval('(' + sender.getResponseText("") + ')');
sender.removeEventListener("completed", this.tokenDownloader);
sender = null;
var leecherCDInfo = this.plugIn.createObject("downloader");
this.tokenDownloader = leecherCDInfo.addEventListener("completed", Silverlight.createDelegate(this, this.CreateCDInfo));
leecherCDInfo.open("GET", "CDInfo.xaml");
leecherCDInfo.send();
}
Le gestionnaire de l'évènement accepte deux paramètres : sender, qui est une référence sur l'objet qui a signalé cet évènement, et eventArgs, qui représente les paramètres associés à l'évènement (si vous faites du .NET, ça doit vous sembler étrangement familier :) ). Ici, sender représente donc l'objet downloader que nous avons créé dans la méthode handleLoad. Nous appelons sa méthode getResponseText() pour obtenir les données transférées. L'argument passé à la méthode permet d'indiquer, dans le cas où on a téléchargé un fichier zip, quel fichier de l'archive on souhaite récupérer. Ici, nous récupérons directement une chaine de caractères, donc nous passons une chaine vide en paramètre (on aurait pu utiliser la propriété ResponseText, qui aurait eu le même effet).
Cette chaine de caractères est immédiatement evaluée afin de transformer le JSON en objets Javascript.
Au passage, j'aurais bien utilisé le parser JSON de json.org, mais il n'avait pas l'air de s'entendre avec le contenu de Silverlight.js. Le risque de sécurité étant mineur (je suis le producteur des données qui sont consommées par l'application, donc bon), je n'ai pas insisté ^_^
Ceci fait, je supprime le handler de l'évènement completed puis je mets la référence au downloader à null, ce qui d'après une page de la MSDN permet d'améliorer les performances des applications Silverlight.
Notez que la méthode removeEventListener() réclame un token en paramètre : ce token est renvoyé par la méthode addEventListener (on l'a récupéré dans la méthode handleLoad).
Nous avons donc notre liste de CDs. Maintenant, nous créons à nouveau un objet downloader afin d'aller récupérer un fichier xaml à partir duquel nous créerons le contrôle destiné à l'affichage des informations de détail sur un CD particulier.
J'aurais pu effectuer cette tâche dans la méthode handleLoad, mais j'ai préféré le faire ici afin d'être sûr que le chargement se faisait dans un certain ordre, et surtout que le chargement de l'ensemble des données était réalisé avant de laisser les utilisateurs cliquer partout. Je me rends compte maintenant que j'aurais mieux fait d'utiliser un fichier zip... enfin bref.
Donc voilà, je ne détaille pas plus, on a déjà vu tout ça quand on a récupéré la liste de CDs.
Voyons voir ce qui se passe lorsque notre fichier xaml a été téléchargé :
CreateCDInfo: function(sender, eventArgs)
{
var cdInfo = sender.ResponseText;
sender.removeEventListener("completed", this.tokenDownloader);
sender = null;
var cdi = this.plugIn.content.CreateFromXaml(cdInfo, true);
this.root.children.add(cdi);
this.cdInfo = new Peuw.CDInfo(this.plugIn, cdi);
var leecherImageButton = this.plugIn.createObject("downloader");
this.tokenDownloader = leecherImageButton.addEventListener("completed", Silverlight.createDelegate(this, this.CreateImages));
leecherImageButton.open("GET", "ImageButton.xaml");
leecherImageButton.send();
}
Nous récupérons la chaine de caractères correspondant au contenu du fichier demandé grâce à la propriété ResponseText (pour changer un peu). Nous souhaitons transformer cette chaine en objets Javascript que nous pourrons ajouter à l'arbre de nos contrôles. Pour cela, nous utilisons la méthode CreateFromXaml() de la propriété content de l'objet représentant le contrôle Silverlight. Il prend en premier paramètre le code xaml à partir duquel créer les contrôles, et en second un booléen optionnel (false par défaut) qui indique, s'il est à true, que la méthode doit modifier les éventuels noms des contrôles (i.e le contenu de l'attribut x:Name) afin d'éviter les collisions.
Nous obtenons donc nos contrôles, que nous ajoutons à l'arbre des contrôles : this.root contient une référence vers le canevas général. Sa propriété children correspond à l'ensemble des contrôles qu'il contient, et nous y ajoutons les contrôles que nous venons d'obtenir grâce à la méthode add.
On instancie ensuite la classe Peuw.CDInfo, qui s'occupe de gérer les contrôles que nous venons de charger (redimensionnement, récupération des infos, gestionnaires d'évènement, etc). On garde une instance de cette classe en variable membre pour plus tard.
Nous attaquons ensuite la dernière étape du chargement : récupérer le code xaml qui définit le "bouton" qui va afficher les pochettes de CD et sur lequel les utilisateurs pourront cliquer afin de consulter les informations détaillées du CD.
Je ne détaille pas la méthode CreateImages, qui n'apporte rien de nouveau. Elle crée les contrôles à partir du code xaml autant de fois qu'il y a de CDs et les ajoute au canevas en les positionnant correctement.
Soulignons tout de même que, pour obtenir la taille de l'application, nous utilisons les propriétés actualWidth et actualHeight de la propriété content de l'objet Silverlight :
var largeur = this.plugIn.content.actualWidth;
var hauteur = this.plugIn.content.actualHeight;
Ces propriétés représentent la taille à l'écran, pas celle spécifiée. Dans notre cas, nous avions demandé à ce que le contrôle Silverlight prennent 100% de la hauteur et 100% de la largeur disponibles, ce qui est certes pratique, mais devient ennuyeux lorsqu'on souhaite adapter la disposition du contenu en fonction de la taille du contenant. Heureusement, ces propriétés nous renvoient des valeurs en pixels (qui seront donc différentes si par exemple on change la taille de la fenêtre du navigateur).
A ce stade, l'application est chargée. Graphiquement, nous sommes arrivés à la première capture d'écran.
C'est ici que se terminent les explications pour ce post : nous avons créé des contrôles, branché des gestionnaires d'évènement et chargé des données, donc je pense que vous avez le nécessaire pour comprendre comment fonctionne le reste.
Je vais reprendre les notions vues ici sous une forme plus digeste dans un autre post, auxquelles j'ajouterai les quelques petites choses que j'ai découvert en écrivant cette application.
En attendant, bilan de l'opération : j'aime bien Silverlight, mais si j'en refais ça sera du 1.1, c'est trop fatigant le Javascript :)
Ah, et ça manque de contrôles : typiquement, les scrollbars. S'il n'y en a pas, c'est pas parce que j'aime pas les scrollbars (everybody loves scrollbars \0/), mais plutôt parce que si vous en voulez, vous devrez les coder vous-même, et bizarrement j'ai eu la flemme ^_^
D'après ce que j'ai vu, le SDK de la version 1.1 viendrait avec quelques contrôles en exemple, dont des scrollbars... à suivre.
|
|
| mardi 11 septembre 2007 - 18:09 |
C'est marrant sous Mac OS X avec le plugin Silverlight installé il me demande d'installer Silverlight :)
En tout cas merci pour l'introduction, c'est très intéressant. Je doute quand même utiliser Silverlight puisqu'on a déjà Flash et Flex qui marchent bien partout :)) |
| Gfx |
| (http://www.progx.org) |
|
| mardi 11 septembre 2007 - 22:14 |
Pour la demande d'installation alors que c'est installé j'ai eu le problème aussi, mais je sais plus trop comment je l'ai résolu ^_^
Probablement en désinstallant/réinstallant...
Et effectivement, si tu es déjà à l'aise en Flash/Flex, aucun intérêt :) |
| nop |
| (http://www.peuw.net) |
|
| mercredi 12 septembre 2007 - 11:27 |
J'ai un zoli écran noir avec rien dedans en affichant ta page sous ie6 :/ |
| Shiingo |
| (http://www.kamino.org/) |
|
| mercredi 12 septembre 2007 - 14:50 |
Pinaise, y'a pas à ch*er, Silverlight c'est la classe :-/
Je me demande si je ferais pas mieux de faire une vidéo, au final :) |
| nop |
| (http://www.peuw.net) |
|
| Réagir : |
Le html n'est pas autorisé, pour mettre en forme vos textes, utilisez les codes suivants:
* [b]gras[/b]
* [u]souligné[/u]
* [i]italique[/i]
* [s]barré[/s]
* [sup]exposant[/sup]
* [url=http://www.monlien.com]texte du lien[/url] (pour les feignants, sachez que tout texte de la forme http://quelquechose sera automatiquement transformé en lien)
* [img]http://www.monlien.com/monimage.jpg[/img]
* [center]texte centré[/center]
* [quote]citation[/quote]
* Toute ligne commençant par "/me" sera remplacée à la sauce IRC. Si votre pseudo est Martine et que vous inscrivez :
/me va à la plage
Le résultat sera :
* Martine va à la plage
* [mp3]http://www.monlien.com/mamusique.mp3[/mp3]
* [code language="C#|php|vb.net|etc..."]public void DuCode() { }[/code]
|
back!
|
|
|