| |  |
| Sauvez un arbre, mangez un castor |
vendredi 09 mai 2008 |
| mercredi 12 septembre 2007 |
| 17:51 » Silverlight 1.0 Mini FAQ - programmation, silverlight |
Chose promise, chose dûe.
Comment obtenir une référence Javascript sur un élément graphique ?
Il faut utiliser 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. On peut appeler 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 et l'appeler de n'importe où :)
Comment changer la valeur des propriétés des éléments graphiques ?
Dans la plupart des cas, on le fait de manière classique :
monElementGraphique.Propriete = valeur;
Les attached properties doivent cependant être traitées différemment. Par exemple, pour changer la valeur de la propriété Canvas.Top, on écrira :
monElementGraphique.["Canvas.Top"] = valeur;
Comment télécharger du contenu ?
Grâce à l'objet downloader. On en crée une instance en appelant la méthode createObject de l'objet correspondant au contrôle Silverlight. 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.
var objetDownloader = controleSilverLight.createObject("downloader");
Avant de faire récupérer des choses à l'objet, nous devons ajouter un handler pour son évènement completed, qui annonce la fin du téléchargement. 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.
var tokenDownloader = objetDownloader.addEventListener("completed", Silverlight.createDelegate(this, this.HandleDownloadCompleted));
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 (ex : "GET"), et en second l'adresse de la ressource à récupérer.
objetDownloader.open("GET", "/UneSuperImage.png");
Enfin, on lance le transfert grâce en appelant la méthode send.
Le téléchargement étant à présent lancé, voyons comment récupérer les données dans le gestionnaire de l'évènement completed. Ce dernier accepte deux paramètres : sender, qui est une référence sur l'objet qui a signalé cet évènement (i.e notre objet downloader), et eventArgs, qui représente les paramètres associés à l'évènement. Pour récupérer les données, nous pouvons appeler la méthode getResponseText() de l'objet downloader. Cette méthode prend un argument qui 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. S'il ne s'agit pas d'un fichier zip, il suffit de passer une chaine de caractères vide en paramètre, ou d'utiliser directement la propriété ResponseText.
HandleDownloadCompleted: function(sender, eventArgs)
{
// ces deux lignes ont le même effet
var data = sender.getResponseText("");
data = sender.ResponseText;
// pour ne pas dégrader les performances
sender.removeEventListener("completed", tokenDownloader);
sender = null;
}
Comme vous pouvez le voir dans le code ci-dessus, une fois les données récupérées 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.
J'ai écrit le code d'un joli bouton en xaml. Comment l'afficher ?
Je pars du principe que le code xaml dont on parle ne fait pas partie du fichier xaml qu'on indique comme source au contrôle Silverlight (sinon il serait déjà affiché :) ).
Premièrement, si ce n'est pas déjà fait, il faut télécharger le contenu du fichier xaml (voir Comment télécharger du contenu ?).
Ensuite, une fois qu'on a récupéré la chaine de caractères correspondant au contenu du fichier xaml, il faut transformer cette chaine en objets Javascript.
Cela se fait à l'aide de 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.
// on suppose que la variable "xaml" contient le code xaml à afficher
var controle = controlSilverLight.content.CreateFromXaml(xaml, true);
Nous obtenons donc notre/nos contrôle(s), que nous devons à présent ajouter à l'arbre des contrôles pour qu'ils soient affichés. Pour cela, il suffit d'avoir une référence à un élément de type Canvas, qui fait office de contrôle conteneur en Silverlight. 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 à sa méthode add.
// on suppose que la variable "root" contient une référence vers un objet Canvas
root.children.add(controle);
Comment cacher un élément ?
Mettez sa propriété Visibility à... Collapsed :) (hé non, pas Hidden)
Les deux valeurs possibles de cette propriété sont Visible et Collapsed.
this.MainCanvas.Visibility = "Collapsed";
Comment être notifié lorsque l'image affichée par un contrôle Image a terminé son chargement ?
Si ça n'a pas grand intérêt pour les images en local (le temps de chargement est proche de zéro), ça devient intéressant lorsqu'on va chercher les images à la demande.
Pour cela, il suffit d'ajouter un handler à l'évènement DownloadProgressChanged. Dans le handler, vous pouvez consulter la valeur de sender.DownloadProgress pour savoir où en est le téléchargement. La valeur varie entre 0 et 1 (1 indique que le téléchargement est terminé).
this.Image.addEventListener("DownloadProgressChanged", Silverlight.createDelegate(this, this.ImageLoading));
this.Image["Source"] = "http://www.mondomaine.com/monImage.png";
// handler de l'évènement DownloadProgressChanged
ImageLoading: function(sender, eventArgs)
{
if(sender.DownloadProgress == 1) // on vérifie que le téléchargement est terminé
{
// ...
}
}
Comment contrôler une animation depuis le code Javascript ?
Supposons que votre xaml contienne un Storyboard portant le doux nom de "pouet". Il faudra d'abord obtenir une référence dessus (grâce à findName), et vous pourrez ensuite utiliser les méthodes Begin, Pause, Resume, Seek et Stop pour contrôler l'animation.
// Obtenir une référence sur l'animation "pouet"
var animation = this.target.findName("pouet");
// Démarrer l'animation "pouet"
animation.begin();
Comment être notifié lorsqu'une animation est terminée ?
Il suffit de gérer l'évènement Completed de l'animation :
// Obtenir une référence sur l'animation "pouet"
var animation = this.target.findName("pouet");
// Démarrer l'animation "pouet"
animation.addEventListener("Completed", Silverlight.createDelegate(this, this.HandleAnimationCompleted));
Comment récupérer la taille totale de l'application Silverlight en pixels ?
La taille en pixels de l'application est contenu dans les propriétés actualWidth et actualHeight de la propriété content de l'objet Silverlight :
var largeur = controleSilverLight.content.actualWidth;
var hauteur = controleSilverLight.content.actualHeight;
|
| permalien & reactions (1) |
|
| 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.
|
| permalien & reactions (5) |
|
|
|