Comment “cuisiner” une application Windows 8 avec HTML 5, CSS3 et JavaScript en une semaine–Jour 2 - Eternal Coding - HTML5 / Windows / Kinect / 3D development - Site Home - MSDN Blogs

Comment “cuisiner” une application Windows 8 avec HTML 5, CSS3 et JavaScript en une semaine–Jour 2


 

Comment “cuisiner” une application Windows 8 avec HTML 5, CSS3 et JavaScript en une semaine–Jour 2

  • Comments 1

(La version finale est accessible ici: http://blogs.msdn.com/b/eternalcoding/archive/2012/06/08/comment-cuisiner-une-application-windows-8-avec-html-5-css3-et-javascript-en-une-semaine-jour-5.aspx)

Au menu aujourd’hui nous allons nous intéresser aux sujets suivants:

  • Internationalisation
  • Gestion de l’activation du mode offline
  • Intégration d’une flipView pour la page des cartes
  • Gestion des vues “snapped”
  • Adaptation aux différentes résolutions
  • Gestion des éléments de tailles différentes

image

 

Comme d’habitude, la solution complète est disponible ici : http://www.catuhe.com/msdn/urza/day2.zip

L’intégralité de la série est disponible ici:

Internationalisation

Comme nous visons tous les places de marché supportées par le store Windows 8, nous allons devoir internationaliser notre application. Pour ce faire, JavaScript pour Windows 8 Metro fournit un service très simple.

Pour commencer, il faut ajouter un fichier .resjson dans un répertoire portant le code de la langue à gérer (par exemple “en” pour la langue anglaise) :

image

image

Ce fichier contient objet au format JSON contenant des paires clefs/valeurs pour la traduction:

{
    "Settings"              : "Settings",
    "CardsZoomLevel"        : "Cards zoom level",
    "OfflineFiles"          : "Offline files",
    "OfflineFilesRestart"   : "(You must restart to apply this settings)",
    "On"                    : "On",
    "Off"                   : "Off",
    "DownloadInProgress"    : " download(s) in progress",
    "LoadingData"           : "Loading data...please wait",
    "ErrorConnection"       : "Error during connection to server. Please check you connectivity.",
    "All"                   : "All ",
    "Colors"                : "colors",
    "Authors"               : "authors",
    "Rarities"              : "rarities",
    "Types"                 : "types",
    "Cards"                 : " cards",
    "Number"                : "Number:",

    "Type"                  : "Type:",
    "Color"                 : "Color:",
    "Power"                 : "Power:",
    "Rarity"                : "Rarity:",    
    "Text"                  : "Text:",
    "Flavor"                : "Flavor:",
    "Author"                : "Author:",
    "AveragePrice"          : "Average price:",
    "ByNumber"              : "By number",
    "ByName"                : "By name",
    "OnlyMissing"           : "Only missing",
    "AllExceptMissing"      : "All except missing"
}

Vous pouvez avoir un fichier pour chaque langue en les mettant dans les bons répertoires. Windows 8 se chargera pour vous de choisir le bon fichier en fonction de la langue active.

Par la suite, pour référencer ces données au niveau de l’HTML, il suffit d’utiliser le tag data-win-res :

<span data-win-res="{innerText: 'OfflineFilesRestart'}"></span>

Cette option sera traitée lorsque vous ferez appel à la méthode suivante :

WinJS.Resources.processAll();

Il faudra juste bien penser à appeler cette fonction pour chaque page. En général, je l’appelle juste au début de la fonction ready de mes pages :

 ui.Pages.define("/pages/card/card.html", {
     ready: function (element, options) {
         WinJS.Resources.processAll();

De plus dans votre code, vous pouvez également interroger le système pour obtenir une chaine de caractères à partir d’une clef :

var resourceLoader = new Windows.ApplicationModel.Resources.ResourceLoader();
var getString = function (name) {
    if (!name)
        return "";

    return resourceLoader.getString(name);
};

 

Gestion de l’activation du mode offline

Certains utilisateurs ne souhaiteront peut-être pas utiliser de l’espace sur leur disque pour sauvegarder les images en local. Nous allons donc rajouter une petite propriété dans les settings pour gérer l’activation du mode offline:

image

Pour ce faire il suffit juste rajouter un contrôle dans notre div des settings :

<div id="settingsDiv" data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{width:'narrow'}">
    <div class="win-header">
        <button type="button" onclick="WinJS.UI.SettingsFlyout.show()" class="win-backbutton">
        </button>
        <div class="win-label" data-win-res="{innerText: 'Settings'}"></div>
    </div>
    <div class="win-content">
        <h4 data-win-res="{innerText: 'CardsZoomLevel'}"></h4>
        <input type="range" id="zoomRange" min="20" max="80" value="50" />
        <fieldset class="controlGroup">
            <legend class="controlGroupName"><span data-win-res="{innerText: 'OfflineFiles'}"></span>
                <br />
                <i><span data-win-res="{innerText: 'OfflineFilesRestart'}"></span></i>
            </legend>
            <label class="radioLabel horizontalLabelLayout">
                <input type="radio" name="offlineMode" checked id="offlineModeOn"/>
                <span data-win-res="{innerText: 'On'}"></span>
            </label>
            <label class="radioLabel horizontalLabelLayout">
                <input type="radio" name="offlineMode" id="offlineModeOff"/>
                <span data-win-res="{innerText: 'Off'}"></span>
            </label>
        </fieldset>
    </div>
</div>

Chaque radio va permettre de modifier un réglage itinérant:

// Offline mode
var on = document.getElementById("offlineModeOn");
var off = document.getElementById("offlineModeOff");
var offlineMode = Windows.Storage.ApplicationData.current.roamingSettings.values["offlineMode"];

if (offlineMode)
    on.checked = true;
else
    off.checked = true;

on.addEventListener("change", function () {
    Windows.Storage.ApplicationData.current.roamingSettings.values["offlineMode"] = true;
});

off.addEventListener("change", function () {
    Windows.Storage.ApplicationData.current.roamingSettings.values["offlineMode"] = false;
});

Nous allons ensuite modifier le chargement des images pour alterner entre un mode local (ms-appdata) et un mode distant (http) :

 var offlineMode = Windows.Storage.ApplicationData.current.roamingSettings.values["offlineMode"];
 if (offlineMode) {
     block.logo = "ms-appdata:///local/blocks/" + block.name.replace(":", "_") + ".png";
     block.banner = "ms-appdata:///local/blocks/" + block.name.replace(":", "_") + "_banner.png";
 }
 else {
     block.logo = root + "/blocks/" + block.name.replace(":", "_") + ".png";
     block.banner = root + "/blocks/" + block.name.replace(":", "_") + "_banner.png";
 }

Et voila Sourire. Seul soucis pour le moment, le réglage nécessite de redémarrer l’application.

Intégration d’une flipView pour la page des cartes

Nous allons maintenant nous intéresser à la page des cartes. En effet cette dernière permet de visualiser une seule carte et je voudrais que l’on puisse les voir en séquence sans avoir à revenir à la liste de l’extension.

Pour ce faire, nous allons rajouter un nouveau contrôle sur l’intégralité de la page : le contrôle flipView.

Ce dernier va nous permettre d’afficher les cartes une par une mais tout en disposant d’un moyen de les passer en revue simplement (soit avec un déplacement au doigt soit en cliquant sur les contrôles de navigation présent sur les bords à la souris :

image

Du coup nous allons passer d’un mode ou nous connections les données en dur à un mode à base de template :

<body>
    <!--Template-->
    <div class="itemTemplate" data-win-control="WinJS.Binding.Template">
        <div class="item-root">
            <header aria-label="Header content" role="banner">
                <button class="win-backbutton" aria-label="Back"></button>
                <h1 class="titlearea win-type-ellipsis"><span class="pagetitle" 
                    data-win-bind="innerText: name">
                </span></h1>
            </header>
            <section aria-label="Main content" role="main">
                <h3 class="item-number-label" data-win-res="{innerText: 'Number'}"></h3>
                <div class="item-number" data-win-bind="innerText: number"></div>
                <h3 class="item-type-label" data-win-res="{innerText: 'Type'}"></h3>
                <div class="item-type" data-win-bind="innerText: type"></div>
                <h3 class="item-color-label" data-win-res="{innerText: 'Color'}"></h3>
                <div class="item-color" data-win-bind="innerText: color"></div>
                <h3 class="item-power-label" data-win-res="{innerText: 'Power'}"></h3>
                <div class="item-power" data-win-bind="innerText: power"></div>
                <h3 class="item-rarity-label" data-win-res="{innerText: 'Rarity'}"></h3>
                <div class="item-rarity" data-win-bind="innerText: rarity"></div>
                <h3 class="item-text-label" data-win-res="{innerText: 'Text'}"></h3>
                <div class="item-text" data-win-bind="innerText: text"></div>
                <h3 class="item-flavor-label" data-win-res="{innerText: 'Flavor'}"></h3>
                <div class="item-flavor" data-win-bind="innerText: flavor"></div>
                <h3 class="item-author-label" data-win-res="{innerText: 'Author'}"></h3>
                <div class="item-author" data-win-bind="innerText: author"></div>
                <h3 class="item-price-label" data-win-res="{innerText: 'AveragePrice'}"></h3>
                <div class="item-price" data-win-bind="innerText: price UrzaGatherer.Tools.PriceConverter">
                </div>
                <div class="item-image-container" data-win-control="UrzaGatherer.Tools.DelayImageLoader"
                    data-win-options="{root: 'cards'}">
                    <img class="item-image-container" data-win-bind="src: logo" />
                </div>
                <div class="expansion-image-container" data-win-control="UrzaGatherer.Tools.DelayImageLoader">
                    <img class="expansion-picture" src="#" data-win-bind="src: expansion.banner" />
                </div>
            </section>
        </div>
    </div>
    <!--Content-->
    <div class="card fragment">
        <div id="flipView" data-win-control="WinJS.UI.FlipView" 
            data-win-options="{ itemTemplate : select('.itemTemplate') }">
        </div>
    </div>
</body>

On note donc la présence à la fois des data-win-res pour nommer les champs et des data-win-bind pour les liaisons de données.

La connexion de la liste des cartes est similaire à ce que nous faisions pour les listViews :

var flipView = document.getElementById("flipView").winControl;
ui.setOptions(flipView, {
    itemDataSource: options.cards.dataSource,
    currentPage: options.cardIndex
});

 

Gestion des vues “snapped”

Windows 8 Metro impose le support de la vue “snapped” pour chacun des écrans de l’application. Ce mode s’active quand votre application est mise sur le coté d’une application principale. Elle va alors occuper une largeur de 320 pixels.

Il faut donc décider dans le cadre du design de son application, ce que devra contenir ce mode pour chacun des écrans.

Dans le cas de UrzaGatherer, j’ai pris le parti de fournir une expérience quasi-complète en repensant mes écrans sans enlever de fonctionnalités.

Ainsi l’écran principal va évoluer ainsi :

image

L’écran des extensions en mode “snapped” donnera (on peut noter la disparition des filtres) :

image

Et les cartes vont être présentées ainsi :

image

 

Toutes ces évolutions sont faites en utilisant une seule fonctionnalité : les media queries (http://msdn.microsoft.com/en-us/library/windows/apps/hh453556.aspx). Cette fonctionnalité permet de faire des feuilles de styles qui ne s’activent que sous certaines conditions (par exemple, le fait d’être en mode snapped).

Voici par exemple un exemple pour la page des extensions :

@media screen and (-ms-view-state: snapped) {

    #cardsCount {
        display: none;
    }

    #cardsPrice {
        display: none;
    }

    .expansion section[role=main] {
        -ms-grid-rows: 0px 1fr;
    }
}

 

Ces règles viendront remplacer (ou compléter) les règles d’origine. Par exemple, la version de base de “.expansion section[role=main]” est la suivante :

.expansion section[role=main] {
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 50px 1fr;
    display: -ms-grid;
}

En mode “snapped” en tenant compte de notre media query, cela va devenir :

.expansion section[role=main] {
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 0px 1fr;
    display: -ms-grid;
}

La technique consiste donc à utiliser les media queries pour bouger les éléments de la page d’une orientation horizontale vers une orientation verticale (grâce au support des CSS3 Grids). De plus on peut aussi en profiter pour changer les tailles pour s’adapter au fait que la vue “snapped” est plus petite.

Adaptation aux différentes résolutions

Les media queries peuvent aussi servir à adapter nos écrans aux tailles de résolutions. Par exemple, la page de visualisation d’une carte va utiliser une media query pour adapter son affichage aux petites résolutions :

@media screen and (min-height: 1024px) {
    .card section[role=main] {
        -ms-grid-columns: auto 1fr 960px;
    }

    .card .item-image-container {
        margin-top: 0px;
    }
}

Ainsi on obtient cet affichage si la résolution verticale est supérieure à 1024 :

image

 

Et sur une affichage plus petit cela donne (avec l’aide du simulateur):

image

On peut voir que les colonnes sont plus petites et que l’image est un peu relevée (elle vient rogner un peu sur le bandeau du titre) pour la faire tenir en hauteur.

Gestion des éléments de tailles différentes

Dans ma collection de cartes, il y a une extension qui est particulière en ce sens qu’elle introduit des cartes deux fois plus large:

image

Cela va donc impacter l’écran des extensions puisqu’il va falloir avoir deux templates dans notre listView : un pour les cartes standards et un pour les cartes larges.

Pour ce faire nous allons fournir non pas un template statique mais une fonction JavaScript qui se chargera de fournir le bon template.

Nous allons donc tout d’abord modifier les options de notre listView pour qu’elle prenne comme template une fonction :

var listView = element.querySelector(".cardsList").winControl;

ui.setOptions(listView, {
    itemTemplate: this.itemRenderer,
    oniteminvoked: this.itemInvoked.bind(this)
});

La fonction itemRenderer va donc être en charge de produire les templates. Dans notre cas, elle va tester la carte courante et si c’est une carte large, il nous faudra modifier un style (celui qui donne la largeur de la cellule) :

itemRenderer: function (itemPromise) {
    return itemPromise.then(function (currentItem, recycled) {
        var template = document.querySelector(".itemTemplate").winControl.renderItem(itemPromise, recycled);
        template.renderComplete = template.renderComplete.then(function (elem) {

            if (currentItem.data.isLarge) {
                var zoomLevel = Windows.Storage.ApplicationData.current.roamingSettings.values["zoomLevel"];

                if (!zoomLevel)
                    return;

                var level = zoomLevel / 100.0;
                elem.querySelector(".item-container").style.width = (480 * level * 2) + "px";
            }
        });
        return template.element;
    })
},

On peut noter au passage que nous tenons compte du zoom local pour calculer la bonne largeur.

Toutefois si on s’arrête là, cela ne fonctionnera pas car il faut également que la listView soit en mode “multisize” pour permettre d’avoir plusieurs tailles différentes. Pour ce faire, il faut lui préciser son paramètre “groupInfo” :

ui.setOptions(listView, {
    itemDataSource: filtered.dataSource,
    layout: new ui.GridLayout({
        groupHeaderPosition: "top",
        groupInfo: function () {
            var zoomLevel = Windows.Storage.ApplicationData.current.roamingSettings.values["zoomLevel"];

            if (!zoomLevel)
                zoomLevel = 50;

            var level = zoomLevel / 100.0;

            return {
                multiSize: true,
                slotWidth: Math.round(480 * level),
                slotHeight: Math.round(680 * level)
            };
        }
    })
});

Il est important ici de fournir des tailles de cellules (slotWidth et slotHeight) proportionnelles à la taille des cartes car contrairement à ce que l’on pourrait croire la listView ne gère pas n’importe quelle taille pour les éléments mais uniquement des tailles proportionnelles à une unité de base !

A suivre

Notre prochaine étape nous permettra de bien nous intégrer à Windows 8 :

  • Gestion du contrat Search
  • Gestion du contrat Share
  • Gestion du contrat FileOpenPicker
  • Gestion des vignettes dynamiques et secondaires
Leave a Comment
  • Please add 5 and 8 and type the answer here:
  • Post
  • Article très interessant, merci pour le partage.

Page 1 of 1 (1 items)