Dans mon précédent article, j'abordais la possibilité de changer le style de l'application au moment du changement d'état – en particulier vers la vue ancrée – en utilisant les Media Queries de CSS3. Cependant, il y a des scénarii qui impliquent un changement de comportement de l'application dans un état ou un autre ; il faut alors écrire du code JavaScript qui réagit à l'évènement de changement d'état. Nous verrons dans cet article deux cas d'usages assez fréquents : la modification du layout de la ListView et la mise en place d'une navigation spécifique pour la vue ancrée.

S'abonner au changement d'état

Pour savoir quand l'application change d'état en JavaScript, rien de plus simple : il suffit de s'abonner à l'évènement onresize de la variable globale windows (qui est un peu le point d'entrée pour tout un tas de chose en JavaScript). Ce dernier se déclenche à chaque fois que la « fenêtre » de l'application change de taille, ce qui dans le cas d'une application Windows 8, n'arrive logiquement que lors du changement d'état ! On peut alors requêter l'état courant ; pour ça rien de plus simple :

var viewState = Windows.UI.ViewManagement.ApplicationView.value;

On aura tendance à vouloir stocker quelque part cet état, pour le comparer plus tard au nouvel état et ne déclencher de traitement que si l'état a changé … Bonne nouvelle, si vous êtes parti des templates de projet Visual Studio Grid App, Split App ou Navigation App, alors vous avez sans doute un fichier navigator.js dans votre projet qui définit une classe – au sens WinJS j'entends, tout le monde sait qu'il n'y a pas de classe en JavaScript – nommée PageControlNavigator qui est là pour simplifier la gestion de la navigation entre vos différentes pages ; et bien cette classe permet aussi de simplifier la gestion des viewstates. Dans vos pages vous n'avez qu'à utiliser la méthode updateLayout qui sera appelée par le PageControlNavigator pour gérer les changements d'états. Par exemple :

updateLayout: function (element, viewState, lastViewState) {
if (viewState === Windows.UI.ViewManagement.ApplicationViewState.snapped &&
lastViewState !== Windows.UI.ViewManagement.ApplicationViewState.snapped) {
//Passage de n'importe quel état vers la vue ancrée
}
else if (
viewState !== Windows.UI.ViewManagement.ApplicationViewState.snapped &&
lastViewState === Windows.UI.ViewManagement.ApplicationViewState.snapped) {
//Passage de la vue ancrée à n'importe quel autre état
}
}

Je ne peux que vous recommander de partir des templates précédemment cités pour tous vos projets, quitte à supprimer les fichiers qui ne vous sont pas utiles. Le fichier navigator.js est un must have, qui par ailleurs peut être modifié pour s'adapter à vos besoins. A noter par ailleurs que l'on peut aussi demander de sortir de la vue ancrée en JavaScript, en utilisant la fonction suivante :

Windows.UI.ViewManagement.ApplicationView.tryUnsnap();

Le composant ListView et les layouts

En HTML5/JavaScript, il n'y a pas la même distinction en GridView et ListView qu'en XAML/C# car il n'y a qu'un seule contrôle, ListView qui permet de représenter à la fois une grille et une liste. Le fonctionnement de la ListView WinJS est à la fois simple et très puissant et toutes ses possibilités méritent un article à part entière, aussi je ne m'attarderai pas sur son fonctionnement pour aujourd'hui.

Pour choisir si la disposition des éléments sera en forme de liste ou de grille on va utiliser une propriété supplémentaire nommée layout. Cette propriété peut être définie dans le HTML ou en code, ce qui sera très pratique si l'on veut changer le layout de la ListView en fonction du viewstate. Voilà comment définir le layout en HTML :

<div
class="itemlist win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
layout:{type:WinJS.UI.GridLayout}}>
</div>

Pour changer le layout lors du changement de viewstate il suffit de modifier updateLayout comme cela :

updateLayout: function (element, viewState, lastViewState) {
var listView = element.querySelector(".itemlist").winControl;
if (viewState === Windows.UI.ViewManagement.ApplicationViewState.snapped &&
lastViewState !== Windows.UI.ViewManagement.ApplicationViewState.snapped) {
listView.layout = new WinJS.UI.ListLayout();
}
else if (
viewState !== Windows.UI.ViewManagement.ApplicationViewState.snapped &&
lastViewState === Windows.UI.ViewManagement.ApplicationViewState.snapped) {
listView.layout = new WinJS.UI.GridLayout();
}
}

Navigation spéciale pour la vue ancrée

Il arrive que l'on souhaite afficher sur une page une liste d'éléments et un contenu dépendant de cette liste. C'est par exemple le cas de l'application Mail de Windows 8 : elle affiche vos mails à gauche et le détail du mail en cours de lecture à droite. Que se passe-t-il quand on passe à la vue ancrée ? Il n'y a alors clairement plus assez d'espace pour afficher la liste d'emails ET le contenu de l'email sélectionné… La solution est de casser virtuellement la page en deux pages uniquement pour la vue ancrée.

La solution est astucieuse et pas très complexe techniquement ; en vue ancrée, on n'affichera que l'élément sélectionné s'il y en a un, et uniquement la liste d'éléments si aucun élément n'a été sélectionné. Pour cela on utilisera les media queries décrite dans la première partie de l'article. Lorsque l'on sélectionne un élément dans la partie liste, on affiche alors la page de détails pour cet élément : pour cela on utilise la navigation afin de naviguer sur une nouvelle occurrence de cette même page. Cela permettra ensuite d'utiliser le bouton retour pour revenir sur la liste. La seule partie un peu plus complexe est le code de la fonction updateLayout qui doit gérer les différents cas de transitions décrits dans le schéma ci-dessous :

Quand on est dans le cas des transitions en bleu, il faut manipuler l'historique de navigation en code pour pouvoir gérer le bouton retour qui nous renverra sur la liste, et non sur la page précédente !

// On passe en vue ancrée : on ajoute la vue en mode liste dans la pile d'historique
nav.history.current.state = {
groupKey: this._group.key,
selectedIndex: this._itemSelectionIndex
};
nav.history.backStack.push({
location: "/pages/split/split.html",
state: { groupKey: this._group.key }
});

// On passe en mode paysage : on supprime la vue de la pile d'historique
nav.history.backStack.pop();

Bon, ce n'est pas très compliqué mais si vous ne voulez pas écrire tout le code à la main, j'ai une bonne nouvelle : le template Visual Studio Split App met en place exactement ce mécanisme pour vous. Je vous conseille donc d'y jeter un coup d'œil !

- Sébastien Mornas -