Comment ajouter des fonctionnalités communes au modèle de grille (applications du Windows Store en JavaScript et HTML)

Windows 8: Cette rubrique s’applique uniquement à Windows 8. Pour plus d’informations sur l’utilisation d’un modèle de navigation dans Windows 8.1, voir Modèle de navigation hiérarchique de A à Z (HTML).

Vous pouvez étendre le modèle Application grille de Microsoft Visual Studio pour inclure des fonctionnalités principales supplémentaires destinées à ajouter de la valeur à votre application. Ce didacticiel est prévu pour un usage avec Microsoft Visual Studio 2012 sur Windows 8. Vous retrouverez le package de l’application du didacticiel complet à cet emplacement.

Il s’agit de la page de destination par défaut pour le modèle Application grille.

Capture d’écran de la page de destination par défaut pour le modèle Application grille

Une fois le didacticiel terminé, votre application dispose dès lors d’éléments de tailles variées et exploitant différents modèles.

Capture d’écran de l’application grille avec des éléments de différentes tailles et plusieurs modèles

Elle possède également une barre de l’application affichant des commandes contextuelles lorsque des éléments sont sélectionnés.

Capture d’écran de l’application avec une barre de l’application et des commandes contextuelles en cas de sélection d’éléments

Elle implémente le contrôle SemanticZoom.

Capture d’écran de l’application qui implémente le contrôle de zoom sémantique

La transition entre toutes les pages de l’application doit s’animer de façon fluide.

Modèle Application grille

L’utilisation d’un modèle d’application Visual Studio est un excellent moyen de commencer rapidement à travailler sur une application à travers le style de conception Microsoft « prêt-à-l’emploi ». Dans la mesure où les besoins des développeurs ne sont pas tous identiques, toutes les fonctionnalités ne sont pas incluses dans les modèles. Ce didacticiel vous explique comment personnaliser et étendre une application basée sur un modèle tout en respectant les recommandations en matière de style de conception Microsoft. Plus précisément, il illustre comment personnaliser une application d’après le modèle Application grille.

Chaque section de ce didacticiel se concentrera ainsi sur l’ajout d’une fonctionnalité spécifique au modèle Application grille. Nous ajouterons ensuite les animations des pages de transition, une barre de l’application avec des commandes globales et contextuelles, le contrôle SemanticZoom et des éléments de tailles diverses dans la grille. Nous expliquerons la raison de chaque ajout et fournirons des indications pas-à-pas sur la façon d’ajouter la fonctionnalité au modèle. Si notre attention portera sur le modèle Application grille, vous pourrez néanmoins appliquer au modèle Application fractionnée de nombreux enseignements présentés ici.

Prise en main

Avant de commencer ce didacticiel, effectuez les étapes suivantes.

  1. Lancez Visual Studio 2012, puis sélectionnez Fichier > Nouveau projet.
  2. Créez un projet JavaScript à l’aide du modèle Application grille.

Ajout d’animations de transition entre les pages

Les applications comptant plusieurs pages doivent apporter des transitions animées lorsque l’utilisateur navigue entre celles-ci. Dans cette section, nous allons ajouter des animations de transition aux pages du modèle Application grille.

Motivation

L’animation d’ouverture de page permet de s’assurer que la navigation entre les pages est rapide et fluide. Vous devez inclure ces animations chaque fois que l’utilisateur passe d’une page à l’autre.

Implémentation

Dans chaque page du modèle, nous ajoutons une fonction chargée de retourner les éléments de la page à animer, dans l’ordre où ils doivent l’être. Nous ajoutons ensuite une fonction au fichier Navigator.js pour nous assurer que le système appelle les animations chaque fois que l’utilisateur accède à une page.

Pour en savoir plus sur les animations de transition, consulter l’Exemple de bibliothèque d’animations HTML.

  1. Dans GroupedItems.js, ajoutez une fonction à ui.Pages.define qui retourne un tableau d’éléments HTML. Cette fonction fournit l’en-tête et la section de page contenant l’affichage Grille.

    getAnimationElements: function () {
        return [[this.element.querySelector("header")], [this.element.querySelector("section")]];
    },
    

    Remarque  Comme l’exemple précédent le montre, vous devez ajouter une virgule à la fin de cette fonction, sauf si elle correspond à la dernière fonction de la liste. Si tel est le cas, vous devez placer une virgule avant la fonction. La même règle s’applique aux étapes suivantes.

  2. Dans GroupDetail.js, ajoutez une fonction à ui.Pages.define qui retourne un tableau d’éléments HTML à animer.

    getAnimationElements: function () {
        return [[this.element.querySelector("header")], [this.element.querySelector("section")]];
    },
    
  3. Dans ItemDetail.js, ajoutez une fonction à ui.Pages.define qui retourne un tableau d’éléments HTML à animer.

    getAnimationElements: function () {
        return [[this.element.querySelector("header")], [this.element.querySelector(".content")]];
    },
    
  4. Dans Navigator.js, ajoutez à WinJS.Namespace.define une fonction qui tente de récupérer les éléments dans une page à animer. Si la page ne fournit pas de fonction retournant un tableau d’éléments, nous procédons alors à l’animation de la page entière.

    _getAnimationElements: function () {
        if (this.pageControl && this.pageControl.getAnimationElements) {
            return this.pageControl.getAnimationElements();
        }
        return this.pageElement;
    },
    
  5. Pour définir le bon élément d’interface utilisateur actif une fois l’animation terminée, nous devons ajouter une fonction à chaque page. Nous appelons cette fonction après la fin de l’animation d’ouverture.

    Dans GroupedItems.js, ajoutez à ui.Pages.define une fonction qui fait porter le point actif de l’interface utilisateur sur un contrôle de la page.

    setPageFocus: function () {
        this.element.querySelector(".groupeditemslist").winControl.element.focus();
    },
    

    Remarque  Comme l’exemple précédent le montre, vous devez ajouter une virgule à la fin de cette fonction, sauf si elle correspond à la dernière fonction de la liste. Si tel est le cas, vous devez placer une virgule avant la fonction. La même règle s’applique aux étapes suivantes.

    Dans GroupDetail.js, ajoutez à ui.Pages.define une fonction qui fait porter le point actif de l’interface utilisateur sur un contrôle de la page.

    setPageFocus: function () {
        this.element.querySelector(".itemslist").winControl.element.focus();
    },
    

    Dans ItemDetail.js, ajoutez à ui.Pages.define une fonction qui fait porter le point actif de l’interface utilisateur sur un contrôle de la page.

    setPageFocus: function () {
        this.element.querySelector(".content").focus();
    },
    

    Modifiez la fonction prête dans chaque page (ItemDetail.js, GroupDetail.js et GroupedItems.js) en supprimant les lignes suivantes :

    • GroupedItems.js : listView.element.focus();
    • GroupDetail.js : listView.element.focus();
    • ItemDetail.js : element.querySelector(".content").focus();
  6. Pour finir, ajoutez l’animation d’ouverture de la page. Dans Navigator.js, ajoutez le code suivant en haut de la fonction navigated (et non _navigated) pour lancer les animations.

    navigated: function () {
        // Add the following two lines of code.
        var that = this;
        WinJS.UI.Animation.enterPage(that._getAnimationElements(), null).done(function () { 
            that.pageControl.setPageFocus.bind(that.pageControl); 
            });
    
        var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
    
        ...
    

Ajouter une barre de l’application

Nous ajoutons à présent une barre de l’application contenant les commandes globales ou contextuelles que l’utilisateur est susceptible d’exécuter.

Motivation

Le style de conception Microsoft s’appuie entre autres sur le principe du "contenu plutôt que l’interface". L’idée du concept est d’afficher moins d’options pour un plus grand attrait. Seuls les éléments les plus importants doivent se trouver à l’écran. En suivant ce principe, les applications Windows 8 peuvent tirer parti de la barre de l’application, un contrôle courant permettant aux applications de placer des commandes contextuelles appropriées à un emplacement simple d’accès à l’écran. Les commandes fréquemment utilisées sont placées près des bords droit et gauche afin d’être facilement accessibles. La barre de l’application permet à la surface de l’application de se concentrer sur le contenu plutôt que sur les contrôles. Sachant qu’elle est utilisée sur tout le système, les utilisateurs sont alors familiers des interactions avec la barre de l’application. Cela augmente la maniabilité de votre application et améliore la perception de cohésion du système.

Dans la pratique

L’étude de cas du Site Web en application du Windows Store illustre comment les développeurs peuvent repenser leur conception pour prendre en charge les commandes, en utilisant la barre de l’application. La procédure pas à pas ci-dessous montre comment télécharger une photo vers un serveur. À gauche, une procédure pas à pas indique comment cela est mis en œuvre sur le Web. À droite, le scénario équivalent utilise le style de conception Microsoft.

Captures d’écran des étapes pour télécharger une photo vers un serveur

Dans la présentation précédente, le site Web montre la commande pour télécharger vos photos vers la page. À l’opposé, l’application du Windows Store utilise la barre de l’application pour afficher l’action du téléchargement d’une photo. La commande dans la barre de l’application ouvre ensuite le sélecteur de fichiers. Il ne s’agit là que d’un exemple de ce que les applications peuvent entreprendre en plaçant les actions dans la barre de l’application, pour pouvoir accomplir plus de tâches avec un nombre de contrôles moindre, et donner ainsi la priorité au contenu plutôt qu’à l’interface.

Indications

Les commandes de la barre de l’application peuvent varier en fonction de la page que l’utilisateur consulte ou du contexte de l’application. Commandes globales apparaît lorsque l’utilisateur accède à la page, alors que les commandescontextuelles (ou de sélection) s’affichent si certains éléments sont sélectionnés. Nous abordons les commandes contextuelles dans Activation de la propriété de sélection de la grille et affichage des commandes contextuelles de la barre de l’application.

Remarque  Les commandes dont l’utilisateur a besoin pour exécuter un workflow, par exemple l’envoi d’un courrier électronique ou l’achat d’un produit, constituent une exception et peuvent être placées sur la zone de dessin.

Vue d’ensemble de l’exemple

Dans cette partie du didacticiel, nous ajoutons une barre de l’application à la page des éléments groupés et à la page des détails d’élément du modèle Application grille. Nous ajoutons les commandes qui permettent à l’utilisateur de changer la taille des étiquettes dans la page des éléments groupés et celle du texte du corps dans la page des détails d’élément. Nous n’affichons que les commandes adaptées à la page active à l’écran et ne présentons pas la barre de l’application dans la page des éléments groupés.

Dans la capture d’écran suivante, la barre de l’application dans la page des éléments regroupés montre la commande + Label size pour augmenter la taille de l’étiquette :

Capture d’écran de la page des éléments groupés montrant la commande d’augmentation de la taille des étiquettes dans la barre de l’application

La capture d’écran ci-dessous reprend la page des détails de l’élément avec la barre de l’application montrant la commande + Text size pour augmenter la taille du texte :

Capture d’écran de la page des détails d’éléments montrant la commande d’augmentation de la taille du texte dans la barre de l’application

Implémentation

Dans les étapes suivantes, nous ajoutons une barre de l’application globale dotée de boutons également généraux. Nous affichons et masquons les boutons parmi les fonctions prêtes de chaque page. Nous accrochons les commandes aux détecteurs d’événements qui appellent les gestionnaires d’événements pour exécuter les fonctionnalités précédemment décrites. Nous désactivons enfin la barre de l’application dans la page des détails du groupe car cette dernière ne contient aucune commande à afficher.

  1. Dans le fichier Default.html, vous retrouverez une barre de l’application dans le modèle, placée en commentaire pour le moment. Retirez les symboles de commentaire pour intégrer la barre de l’application au code HTML.

  2. L’élément div de la barre contient un espace réservé à un bouton. Remplacez-le par les boutons suivants.

    <button id="labelSize" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'labelSize', section:'global', label:'Label size', icon:'add'}"></button>
    <button id="textSize" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'textSize', section:'global', label:'Text size', icon:'add'}"></button>
    
  3. Dans Default.js, ajoutez les détecteurs d’événements suivants.

    app.addEventListener("activated", function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize your 
                // application here.
    
                // Add the following two lines of code.                                 
                document.getElementById("labelSize").onclick = increaseLabelSize;
                document.getElementById("textSize").onclick = increaseTextSize;
            } else {
    
            ...
    
  4. Dans Default.js, ajoutez les fonctions suivantes à côté des autres fonctions (dans l’étendue du fichier).

    function increaseLabelSize() {
        var titles = document.getElementsByClassName("item-title");
        var subtitles = document.getElementsByClassName("item-subtitle");
        var i;
        for (i = 0; i < titles.length; i++) {
            var prevTitleAttributes = titles[i].getAttribute("style");
            var prevSubtitleAttributes = subtitles[i].getAttribute("style");
            if (prevTitleAttributes != null)
                titles[i].setAttribute("style", prevTitleAttributes + "font-size:20px");
            else
                titles[i].setAttribute("style", "font-size:20px");
            if (prevSubtitleAttributes != null)
                subtitles[i].setAttribute("style", prevSubtitleAttributes + "font-size: 14px");
            else
                subtitles[i].setAttribute("style", "font-size: 14px");
        }
    };
    
    function increaseTextSize() {
        var content = document.getElementsByClassName("item-content");
        content[0].setAttribute("style", "font-size:20px");
    };
    
  5. Assurez-vous que nous n’affichons que les commandes appropriées dans chaque page.

    Dans GroupedItems.js, ajoutez le code suivant à la fonction ready :

    appbar.winControl.disabled = false;
    appbar.winControl.hideCommands([textSize]);
    appbar.winControl.showCommands([labelSize]);
    

    Dans GroupDetail.js, ajoutez la ligne de code suivante à la fonction ready.

    appbar.winControl.disabled = true;
    

    Dans ItemDetail.js, ajoutez le code ci-dessous à la fonction ready.

    appbar.winControl.disabled = false;
    appbar.winControl.hideCommands([labelSize]);
    appbar.winControl.showCommands([textSize]);
    

Activation de la propriété de sélection de la grille et affichage des commandes contextuelles de la barre de l’application

Nous ajoutons à présent un contrôle ListView pour permettre à l’utilisateur de sélectionner les éléments sur lesquels effectuer des opérations. Pour en savoir plus sur le contrôle ListView, voir le Pack exemple du contrôle ListView HTML.

Motivation

L’utilisateur peut vouloir effectuer une série d’opérations sur un élément dans le contrôle ListView. Nous divisons alors les actions en une action principale et un certain nombre d’actions secondaires. Une action principale peut s’effectuer en appuyant ou en cliquant sur un élément. Cela permet généralement à l’utilisateur d’accéder à l’élément voulu.

L’utilisateur effectue une action secondaire en sélectionnant un élément, puis en exécutant une commande sur l’élément depuis la barre de l’application. L’utilisateur peut réaliser un grand nombre d’actions secondaires sur un élément dans la grille, par exemple sa suppression, un ajout à l’écran d’accueil, son marquage en tant que favori, l’indication d’un élément comme lu, le changement de son nom, etc. L’utilisateur peut sélectionner des éléments par un glissement perpendiculaire à la direction de panoramique, cliquer avec le bouton droit de la souris ou appuyer sur la barre d’espace du clavier.

Dans la pratique

L’étude de cas de l’application du Windows Store de l’iPad à Windows 8 montre comment les applications peuvent utiliser une grille sélectionnable et une barre de l’application pour repenser les commandes contextuelles. La procédure pas à pas suivante indique comment supprimer des photos d’une application de journal photographique. À gauche, une procédure pas à pas indique comment cela est mis en œuvre sur l’iPad. À droite, le scénario équivalent utilise le style de conception Microsoft.

Captures d’écran des étapes pour supprimer des photos sur l’iPad et dans une application du Windows Store

Dans l’application pour iPad, l’utilisateur active au préalable le mode de sélection, puis les commandes contextuelles apparaissent en haut de l’écran. Dans l’application du Windows Store, les commandes contextuelles sont masquées dans la barre de l’application. Dans l’étape A, l’utilisateur appelle la barre de l’application. Lorsqu’elle est appelée pour la première fois, aucun élément n’est sélectionné dans la grille. Seule la commande globale s’affiche. Dans l’étape B, l’utilisateur commence par sélectionner les éléments dans la grille qui affiche les commandes contextuelles à gauche de la barre de l’application.

Description

Si un élément est sélectionné, la barre de l’application doit être appelée pour afficher les commandes qui sont contextuelles à l’élément sélectionné. Si plusieurs éléments sont sélectionnés, la barre de l’application doit alors rester déployée et afficher les commandes contextuelles à tous les éléments sélectionnés. Si l’utilisateur désélectionne des éléments, la barre de l’application doit se masquer dès le dernier élément désélectionné.

Lorsque la barre de l’application s’affiche avec des commandes contextuelles, des commandes globales doivent rester à l’écran. Vous devez aligner les commandes globales sur la droite et les commandes contextuelles à gauche dans la barre de l’application.

Vous pouvez choisir de n’autoriser qu’un élément sélectionnable à la fois ou plusieurs éléments en même temps.

Lorsqu’une application est ancrée, l’application doit conserver son état autant que possible. Si la barre de l’application est déployée et que l’application est ancrée, la barre doit rester affichée. De même, si un élément est sélectionné et que l’application est ancrée, sa sélection doit être conservée si cela est possible.

Toutefois, au moment d’ancrer la page des éléments groupés, l’affichage ancré active l’affichage de niveau supérieur des groupes. Par conséquent, il est impossible d’afficher la sélection d’éléments spécifiques dans l’état ancré. Sachant que les éléments ne peuvent pas être affichés séparément, toute commande contextuelle relative à des éléments précédemment sélectionnés doit être masquée.

Vue d’ensemble de l’exemple

Dans cette section du didacticiel, nous configurons les éléments de la grille pour qu’ils soient sélectionnables. Cela permet à plusieurs éléments d’être sélectionnés simultanément. Après un élément sélectionné, la barre de l’application est immédiatement appelée et la commande contextuelle permettant de marquer l’élément comme lu apparaît. Le titre de tous les éléments sélectionnés devient alors gris. La désélection de l’élément masque la barre de l’application. Le marquage d’éléments comme lus déclenche l’exécution de l’action, désélectionne les éléments et masque la barre de l’application.

La capture d’écran ci-dessous montre la page des éléments regroupés avec plusieurs éléments sélectionnés et une barre de l’application reprenant la commande contextuelle Mark as read et la commande globale + Label size :

Capture d’écran montrant la page des éléments regroupés avec plusieurs éléments sélectionnés et une barre de l’application reprenant une commande contextuelle et une commande globale

Implémentation

Dans les étapes suivantes, nous activons la sélection des éléments de la grille. Nous ajoutons également un détecteur d’événements chargé d’afficher une barre de l’application contenant les commandes contextuelles pour l’élément sélectionné.

  1. Dans le fichier GroupedItems.html, indiquez que les éléments GridView peuvent être sélectionnés en changeant le mode de sélection du contrôle ListView, de « none » à « multi ».

    data-win-options="{ selectionMode: 'multi' }"
    
  2. Dans le fichier Default.html, ajoutez la commande contextuelle de la barre de l’application suivante en regard des autres commandes de barre.

    <button id="markItem" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'markItem', section:'selection', label:'Mark as read', icon:'accept'}"></button>
    
  3. Dans le fichier Default.js, ajoutez les détecteurs d’événements suivants, à côté des autres détecteurs de la barre de l’application, dans le gestionnaire app.onactivated.

    document.getElementById("markItem").onclick = markItem;
    
  4. Dans le fichier Default.js, ajoutez la fonction markItem à côté des autres fonctions de commande de la barre de l’application.

    function markItem() {
        var titles = document.getElementsByClassName("item-title");
        var subtitles = document.getElementsByClassName("item-subtitle");
        var listView = document.querySelector(".groupeditemslist").winControl;
        var items = listView.selection.getItems();
        var i;
        for (i = 0; i < items._value.length; i++) {
            var key = parseFloat(items._value[i].key);
            if (titles[0].innerHTML != "") {
                if (key == 0) continue;
                key--;
            }
            var prevTitleAttributes = titles[key].getAttribute("style");
            var prevSubtitleAttributes = subtitles[key].getAttribute("style");
            if (prevTitleAttributes != null)
                titles[key].setAttribute("style", prevTitleAttributes + "color:gray");
            else
                titles[key].setAttribute("style", "color:gray");
    
            if (prevSubtitleAttributes != null)
                subtitles[key].setAttribute("style", prevSubtitleAttributes + "color:gray");
            else
                subtitles[key].setAttribute("style", "color:gray");
        }
        listView.selection.clear();
    }
    
  5. Dans le fichier ItemDetail.js, modifiez l’appel à la fonction hideCommands pour masquer également le bouton Mark Item.

    appbar.winControl.hideCommands([labelSize, markItem]);
    
  6. Dans le fichier GroupedItems.js, modifiez l’appel à la fonction hideCommands pour masquer également le bouton Mark Item.

    appbar.winControl.hideCommands([textSize, markItem]);
    
  7. Dans GroupedItems.js, ajoutez les fonctions ci-dessous avant la fonction ui.Pages.define. Ces fonctions affichent et masquent la barre de l’application par programme.

    function showAppBar(currentItem) {
        // Get the app bar.
        var element = document.activeElement;
        var appbar = document.getElementById("appbar");
    
        // Keep the app bar open after it's shown.
        appbar.winControl.sticky = true;
    
        // Set the app bar context.
        showItemCommands();
    
        // Show the app bar.
        appbar.winControl.show();
    
        // Return focus to the original item which invoked the app bar.
        if (element != null) element.focus();
    }
    
    function hideAppBar() {
        var element = document.activeElement;
        var appbar = document.getElementById("appbar");
        appbar.winControl.sticky = false;
        appbar.winControl.hide();
        hideItemCommands();
        if (element != null) element.focus();
    }
    
  8. Dans GroupedItems.js, ajoutez les fonctions ci-dessous à côté des fonctions précédentes. Les fonctions suivantes affichent et masquent les commandes de la barre de l’application.

    function showItemCommands() {
        appbar.winControl.showCommands([markItem]);
    }
    
    function hideItemCommands() {
        appbar.winControl.hideCommands([markItem]);
    }
    
  9. Dans le fichier GroupedItems.js, ajoutez le code pour ouvrir et fermer par programme la barre de l’application. Dans la fonction ready, ajoutez un gestionnaire d’événements de changement de sélection pour le contrôle ListView de la page.

    listView.onselectionchanged = this.itemSelected.bind(this);
    
  10. Dans le fichier GroupedItems.js, ajoutez les fonctions suivantes pour gérer la sélection du contrôle ListView.

    itemSelected: function (eventObject) {
        var listView = document.querySelector(".groupeditemslist").winControl;
    
        // Check for selection.
        if (listView.selection.count() === 0) {
            hideAppBar();
        } else {
            listView.selection.getItems().then(function (items) {
                showAppBar(items[0]);
            });
        }
    },
    
  11. Dans le fichier GroupedItems.js, ajoutez un appel à la fonction hideItemCommands à partir d’updateLayout. Cet appel masque la commande contextuelle dans la barre de l’application quand l’application est ancrée. La commande contextuelle ne doit pas apparaître si l’application est ancrée, car l’élément ne s’affiche pas comme sélectionné dans l’état ancré.

    updateLayout: function (element, viewState, lastViewState) {
        /// <param name="element" domElement="true" />
        /// <param name="viewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
        /// <param name="lastViewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
    
        var listView = element.querySelector(".groupeditemslist").winControl;
        if (lastViewState !== viewState) {
            if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) {
                var handler = function (e) {
                    listView.removeEventListener("contentanimating", handler, false);
                    e.preventDefault();
                }
            listView.addEventListener("contentanimating", handler, false);
    
            // Add the following five lines of code.        
            this.initializeLayout(listView, viewState);
            hideItemCommands();
            if (viewState === appViewState.snapped)
                listView.selectionMode = "none";
            else
                listView.selectionMode = "multi";
            }
        }
    }
    

Ajout du contrôle SemanticZoom

Dans cette section, nous ajoutons un contrôle SemanticZoom pour fournir un affichage sémantique des éléments de la grille ListView. Chaque élément dans l’affichage de niveau supérieur correspond à un groupe d’éléments de la grille.

Capture d’écran des éléments de la grille ListView

Pour en savoir plus sur le zoom sémantique, voir l’Exemple de groupement d’un contrôle ListView et SemanticZoom en HTML.

Motivation

Le modèle Application grille fait appel à un modèle de navigation hiérarchique. En d’autres termes, le contenu revêtant un intérêt particulier de chaque section principale d’une application peut être promu dans la page de destination. Cette page de destination peut croître pour accueillir un très grand nombre de groupes ou d’éléments. Bien que l’utilisateur puisse faire défiler latéralement l’affichage pour parcourir le contenu, songez à ajouter une fonctionnalité de zoom sémantique pour permettre à l’utilisateur d’accéder directement aux différentes sections de la page.

Le contrôle SemanticZoom permet à l’utilisateur d’activer l’affichage de niveau supérieur de la grille en un affichage sémantique, plutôt qu’optique, de ses groupes. À partir de cet affichage, l’utilisateur peut sélectionner un groupe pour affiner à nouveau le contenu. Le zoom sémantique permet à l’utilisateur de visualiser de grands groupes d’éléments sans faire défiler latéralement l’affichage, mais aussi offre à l’utilisateur une vue d’ensemble des groupes de la grille.

L’affichage de niveau supérieur peut également fournir des méta-informations sur les groupes pour guider l’utilisateur vers des sections importantes de la page. Par exemple, cet affichage de niveau supérieur peut reprendre le nombre d’éléments du groupe en superposition.

Dans la pratique

L’étude de cas de l’application du Windows Store de l’iPad à Windows 8 montre comment les applications peuvent utiliser le zoom sémantique pour permettre à l’utilisateur de passer rapidement d’un groupe d’éléments à l’autre. La procédure pas à pas ci-dessous indique comment utiliser le zoom sémantique pour activer un affichage sémantique des groupes. À gauche, une procédure pas à pas indique comment cela est mis en œuvre sur l’iPad. À droite, le scénario équivalent utilise le style de conception Microsoft.

Capture d’écran comparant le zoom sémantique sur l’iPad pour le passage d’un groupe d’éléments à l’autre

Dans l’application pour l’iPad, l’utilisateur peut appuyer sur le bouton Years sur la barre de navigation supérieure pour afficher une fenêtre contextuelle reprenant la liste des années. Dans l’application du Windows Store, l’utilisateur peut faire appel au mouvement de pincement pour appeler la fonction de zoom sémantique pour revenir à un affichage de niveau supérieur des groupes. Dans cet exemple, l’affichage sémantique a été configuré pour afficher le nombre de commentaires présents dans le groupe Comments et pour afficher les mois répertoriant des photos.

Implémentation

Dans les étapes suivantes, nous ajoutons le conteneur du zoom sémantique à la page des éléments groupés et un modèle pour les groupes de zoom sémantique.

Nous accrochons les groupes sémantiques à la source de données des groupes, puis nous appliquons une feuille CSS (Cascading Style Sheets) pour appliquer de façon appropriée des styles aux groupes.

  1. Dans le fichier GroupedItems.html, accédez au contrôle ListView dans la section de contenu principale :

    <section aria-label="Main content" role="main">
        <div class="groupeditemslist" aria-label="List of groups" data-win-control="WinJS.UI.ListView" data-win-options="{ selectionMode: 'multi' }"></div>
    </section>
    

    Dans sa forme la plus élémentaire, le contrôle SemanticZoom représente un conteneur qui affiche l’un de ses éléments enfants, en fonction du facteur d’échelle. Nous allons ajouter à présent un contrôle SemanticZoom comme « racine » de la section et placer deux contrôles ListView dans le contrôle SemanticZoom.

    Tout d’abord, modifiez la section de contenu principale comme suit :

    <section aria-label="Main content" role="main">
        <div class="sezoDiv" data-win-control="WinJS.UI.SemanticZoom" data-win-options="{ zoomFactor: 0.5, initiallyZoomedOut: false }">
            <div class="groupeditemslist" aria-label="List of groups" data-win-control="WinJS.UI.ListView" data-win-options="{selectionMode: 'multi'}"></div>
            <div class="groupeditemslistZoomOut groupeditemslist" aria-label="List of groups" data-win-control="WinJS.UI.ListView" data-win-options="{selectionMode: 'none'}"></div>
        </div>
    </section>
    
    • Maintenant que nous avons configuré les contrôles de la page, nous pouvons ajouter le code permettant de s’assurer que le contenu approprié s’affiche.

      Dans GroupedItems.js, apportez les ajouts et modifications ci-dessous à la fonction ready. En substance, nous reproduisons simplement ce qui existe déjà pour le premier affichage Liste et l’appliquons à notre nouveau contrôle ListView dans l’affichage de niveau supérieur.

      ready: function (element, options) {
          var listView = element.querySelector(".groupeditemslist").winControl;
      
          // Add the following two lines of code.
          var listViewZoomOut = element.querySelector(".groupeditemslistZoomOut").winControl;
          var semanticZoom = element.querySelector(".sezoDiv").winControl;
      
          listView.groupHeaderTemplate = element.querySelector(".headerTemplate");
          listView.itemTemplate = element.querySelector(".itemtemplate");
      
          // Add the following line of code.
          listViewZoomOut.itemTemplate = element.querySelector(".itemtemplate");
      
          listView.oniteminvoked = this.itemInvoked.bind(this);
      
          // Add the following line of code.
          listViewZoomOut.oniteminvoked = this.groupInvoked.bind(this)
      
          // Change the second and third parameters of the following call as shown.
          this.initializeLayout(listView,listViewZoomOut, semanticZoom, appView.value);
      },
      
  2. Dans le fichier GroupedItems.js, mettez à jour la fonction inializeLayout pour accepter le deuxième élément d’affichage Liste. La fonction se charge également du contrôle de zoom sémantique dans l’affichage ancré.

    // Add the listViewZoomOut and semanticZoom parameters as shown.
    initializeLayout: function (listView, listViewZoomOut, semanticZoom, viewState) {
        /// <param name="listView" value="WinJS.UI.ListView.prototype" />
    
        if (viewState === appViewState.snapped) {
            listView.itemDataSource = Data.groups.dataSource;
            listView.groupDataSource = null;
            listView.layout = new ui.ListLayout();
    
            // Add the following three lines of code.
            semanticZoom.zoomedOut = false;
            semanticZoom.forceLayout();
            semanticZoom.locked = true;
        } else {
            listView.itemDataSource = Data.items.dataSource;
            listView.groupDataSource = Data.groups.dataSource;
            listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
    
            // Add the following four lines of code.
            listViewZoomOut.itemDataSource = Data.groups.dataSource;
            listViewZoomOut.layout = new ui.GridLayout({ maxRows: 1 });
            semanticZoom.forceLayout();
            semanticZoom.locked = false;
        }
    },
    
  3. Dans le fichier GroupedItems.js, adaptez la fonction updateLayout pour configurer le nouveau contrôle ListView afin d’afficher la liste de groupes à l’aide de la source de données groupées.

    updateLayout: function (element, viewState, lastViewState) {
        /// <param name="element" domElement="true" />
        /// <param name="viewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
        /// <param name="lastViewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
        var listView = element.querySelector(".groupeditemslist").winControl;
    
        // Add the following two lines of code.
        var listViewZoomOut = element.querySelector(".groupeditemslistZoomOut").winControl;
        var semanticZoom = element.querySelector(".sezoDiv").winControl;
    
        if (lastViewState !== viewState) {
            if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) {
                var handler = function (e) {
                    listView.removeEventListener("contentanimating", handler, false);
                        e.preventDefault();
                }
                listView.addEventListener("contentanimating", handler, false);
    
                // Add the listViewZoomOut and semanticZoom parameters as shown.
                this.initializeLayout(listView, listViewZoomOut, semanticZoom, viewState);
            }
    
            // Add the following four lines of code.
            if (lastViewState === appViewState.snapped) {
                semanticZoom.zoomedOut = true;
                semanticZoom.forceLayout();
            }
        }
    },
    
  4. Dans le fichier GroupedItems.js, ajoutez la fonction suivante à gérer représentant le moment où l’utilisateur appuie ou clique sur un élément dans l’affichage de niveau supérieur.

    groupInvoked: function (args) {
        var group = Data.groups.getAt(args.detail.itemIndex);
        nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: group.key });
    },
    
  5. Il est temps à présent d’ajouter la feuille CSS pour que les éléments présentent une meilleure apparence.

    Remarque  Pour implémenter les recommandations en matière d’expérience utilisateur pour le zoom sémantique, nous faisons appel à une grille CSS d’une seule case (une seule ligne et une seule colonne). Nous plaçons l’en-tête et le contenu de la section dans cette grille pour qu’ils puissent se chevaucher. En effet, le contrôle SemanticZoom peut couvrir l’intégralité de l’écran, y compris l’en-tête de page.

    Dans le fichier GroupedItems.css, ajoutez les styles suivants pour que le contrôle SemanticZoom occupe tout l’espace disponible à l’écran. Cela entraîne l’adaptation de la disposition du fragment entier, de sorte que le contenu de l’en-tête de la page se place en dessous du contenu de la section, lequel contient la section sezoDiv.

    .groupeditemspage {
        /* Change the display type to have only one row */
        -ms-grid-rows: 1fr;
        display: -ms-grid;
        height: 100%;
        width: 100%;
    }
    
        .groupeditemspage header[role=banner] {
            /* Position this at the top of the screen */
            -ms-grid-row: 1;    
            -ms-grid-columns: 120px 1fr;
            display: -ms-grid;
        }
    
        .groupeditemspage section[role=main] {
            /* Position this at the top of the screen */
            -ms-grid-row: 1;
            height: 100%;
            width: 100%;
        }
    
    .sezoDiv
     {
        height: 100%;
        width: 100%;
    }
    
  6. Nous devons ensuite résoudre certains problèmes découlant du positionnement du contrôle SemanticZoom à une marge négative.

    Pour voir l’en-tête de la page, déplacez l’affichage Liste de 133 pixels vers le bas.

    .groupeditemspage .groupeditemslist .win-horizontal.win-viewport .win-surface {
        margin-left: 45px;
        /* Add the following line. */
        margin-top: 133px;
        margin-bottom: 60px;
    }
    
    /* Add the following lines near the end of the CSS file. */
    .groupeditemspage .groupeditemslistZoomOut .win-horizontal.win-viewport .win-surface {
        margin-left: 116px;
        margin-top: 133px;
    }
    
  7. Nous devons à présent appliquer un style à l’élément pour l’afficher dans l’affichage de niveau supérieur. Ajoutez le code CSS suivant en vous assurant qu’il est bien déclaré après le style groupeditemslist.win-item qui fait déjà partie du fichier :

        .groupeditemspage .groupeditemslistZoomOut .win-item {
            -ms-grid-columns: 1fr;   
            -ms-grid-rows: 1fr 90px;
            display: -ms-grid;
            height: 250px;
            width: 250px;
        }
    .groupeditemspage .groupeditemslistZoomOut .win-item {
        -ms-grid-rows: 1fr 180px;
        display: -ms-grid;
        height: 580px;
        width: 250px;
    }
    
  8. Nous devons maintenant corriger les affichages ancré et portrait, car nous avons effectué des modifications qui les affectent. Ajoutez l’intégralité des styles suivants à la section @media screen and (-ms-view-state: snapped) du fichier GroupedItems.css.

    Ajoutez le style suivant pour réaligner la section de l’en-tête.

    .groupeditemspage header[role=banner] {
        -ms-grid-columns: auto 1fr;
        margin-left: 20px;
    }
    

    Ajoutez une marge en haut de la surface de sorte que la liste ne couvre pas le titre.

    .groupeditemspage .groupeditemslist .win-vertical.win-viewport .win-surface {
        margin-bottom: 30px;
        /* Add the following line. */
        margin-top: 133px;
        margin-left: 6px;
    }
    
  9. Les problèmes dans l’affichage ancré sont à présent résolus. Nous devons ensuite mettre à jour l’affichage portrait.

    Dans GroupedItems.css, ajoutez les styles ci-dessous à la section @media screen and (-ms-view-state: fullscreen-portrait).

    .groupeditemspage .groupeditemslistZoomOut .win-horizontal.win-viewport .win-surface {
        margin-left: 96px;
    }
    

Présentation d’éléments de tailles diverses dans la grille

La présentation d’une grille avec des éléments de différentes tailles est un excellent moyen de personnaliser la grille et de différencier une application. À l’aide d’éléments de tailles diverses, une application peut ainsi mettre l’accent sur certains éléments et accorder moins d’importance à d’autres, en appliquant des polices de tailles supérieures aux éléments plus marquants.

Les éléments de tailles différentes peuvent également exploiter plusieurs modèles. Par exemple, il peut s’avérer judicieux d’afficher des images avec du texte en superposition pour les éléments plus grands et se limiter à du texte pour les éléments plus petits.

Dans la pratique

L’étude de cas de l’application du Windows Store de l’iPad à Windows 8 montre comment les applications peuvent utiliser des éléments de taille diverse et plusieurs modèles, pour personnaliser le modèle de grille et présenter la marque de leur application. Les exemples suivants comparent la page de destination d’une application pour l’iPad et une pour application du Windows Store personnalisant le modèle de grille.

Captures d’écran comparant les pages de destination pour l’iPad et pour une application du Windows Store

Dans l’application pour l’iPad, l’intégralité du contenu est présentée sous forme de grille unique d’images. Dans l’application du Windows Store, les éléments sont présentés dans différentes tailles et avec plusieurs modèles. Le premier groupe contient les éléments dotés d’une petite image et d’une description textuelle côte-à-côte. Le reste des groupes se compose d’images, grandes et petites, avec en superposition du texte de tailles diverses.

Vue d’ensemble de l’exemple

L’exemple présenté dans cette section personnalise la grille dans la page des éléments groupés pour utiliser les éléments de trois tailles différentes. La grille par défaut utilise des éléments de 200 x 200 pixels. Nous utilisons ici des éléments de 310 x 310 pixels, de 310 x 150 pixels et de 150 x 150 pixels. Les éléments plus grands s’appuient sur l’image par défaut avec du texte superposé, tandis que les éléments plus petits contiennent uniquement du texte.

Voici la page des éléments groupés avec les trois tailles d’éléments :

Capture d’écran de la page des éléments groupés illustrant les trois tailles d’éléments

Implémentation

Pour obtenir l’expérience utilisateur voulue pour ce scénario, nous devons définir notre propre fonction de rendu pour l’affichage Liste. Nous avons également besoin d’ajouter la feuille CSS pour les éléments aux différentes tailles.

Pour la fonction de rendu, nous devons réaliser la lecture d’un modèle HTML prédéfini plutôt que d’en créer un manuellement dans la fonction, mais l’une ou l’autre approche est correcte.

En cas de choix de plusieurs tailles d’éléments, n’oubliez pas que les tailles doivent obéir à la formule suivante :itemSize = ((slotSize + margin) x multiplier) – margin

  1. Dans GroupedItems.html, ajoutez le modèle d’élément suivant.

    <div class="multisizebaseitemtemplate" data-win-control="WinJS.Binding.Template">
        <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
        <div class="item-overlay">
            <h4 class="item-title" data-win-bind="textContent: title"></h4>
            <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
        </div>
    </div>
    
  2. Dans GroupedItems.js, ajoutez la fonction suivante avant la définition de page (ui.Pages.define).

    function multisizeItemTemplateRenderer(itemPromise) {
        return itemPromise.then(function (currentItem) {
            var content;
            // Grab the default item template used on the groupeditems page.
            content = document.getElementsByClassName("multisizebaseitemtemplate")[0];
            var result = content.cloneNode(true);
    
            // Change the CSS class of the item depending on the group, then set the size in CSS.
            switch (currentItem.groupKey) {
                case "group1":
                    {
                        // For the first item, use the largest template.
                        if (currentItem.index == 0) {
                            result.className = "largeitemtemplate"
                        }
                        else {
                            result.className = "mediumitemtemplate"
                        }
                        break;
                    }
                case "group2":
                    {
                        result.className = "mediumitemtemplate"
                        break;
                    }
                default:
                    {
                        result.className = "smallitemtemplate"
                    }
            }
            // Because we used a WinJS template, we need to strip off some attributes 
            // for it to render.
            result.attributes.removeNamedItem("data-win-control");
            result.attributes.removeNamedItem("style");
            result.style.overflow = "hidden";
    
            // Because we're doing the rendering, we need to put the data into the item.
            // We can't use databinding.
            result.getElementsByClassName("item-image")[0].src = currentItem.data.backgroundImage;
            result.getElementsByClassName("item-title")[0].textContent = currentItem.data.title;
            result.getElementsByClassName("item-subtitle")[0].textContent = currentItem.data.subtitle;
            return result;
        });
    }
    
  3. Dans GroupedItems.js, ajoutez la fonction groupInfo ci-dessous, également en dehors de la définition de page. Cette fonction indique au contrôle ListView de faire appel à des éléments de taille différente dans l’affichage, et indique la "taille de base" des éléments. La taille de base représente le plus petit élément repris dans la liste. Les autres éléments doivent adopter une taille correspondant à un multiple de cette taille pour que les dispositions fonctionnent correctement.

    function groupInfo() {
        return {
            enableCellSpanning: true,
            cellWidth: 150,
            cellHeight: 150
        };
    }
    
  4. Nous devons à présent accrocher ces nouvelles fonctions aux contrôles ListView de la page des éléments groupés. Sachant que nous ne souhaitons pas utiliser des images de taille variable dans l’affichage ancré, nous devons changer le mode d’application des modèles au code de l’affichage Liste dans les fonctions ready et updateLayout.

    Dans le fichier GroupedItems.js, modifiez la fonction initializeLayout de sorte que, lorsque la page est en mode d’affichage ancré, une simple liste de groupes s’affiche. En affichage Paysage cependant, nous faisons appel à des éléments de taille différente.

    // Add the itemTemplate parameter as shown.
    initializeLayout: function (listView, listViewZoomOut, semanticZoom, viewState, itemTemplate) {
        /// <param name="listView" value="WinJS.UI.ListView.prototype" />
    
        if (viewState === appViewState.snapped) {
            listView.itemDataSource = Data.groups.dataSource;
            listView.groupDataSource = null;
    
            // Add the followign line of code.
            listView.itemTemplate = itemTemplate;
    
            listView.layout = new ui.ListLayout();
            semanticZoom.zoomedOut = false;
            semanticZoom.forceLayout();
            semanticZoom.locked = true;
        } else {
            listView.itemDataSource = Data.items.dataSource;
            listView.groupDataSource = Data.groups.dataSource;
    
            // Add the following two lines of code.
            listView.itemTemplate = multisizeItemTemplateRenderer;
            listView.layout = new ui.GridLayout({ groupInfo: groupInfo, groupHeaderPosition: "top" });
    
            listViewZoomOut.itemDataSource = Data.groups.dataSource;
            listViewZoomOut.layout = new ui.GridLayout({ maxRows: 1 });
            semanticZoom.forceLayout();
            semanticZoom.locked = false;
        }
    },
    

    Supprimez la ligne qui attribue un modèle d’élément au contrôle ListView et apportez les modifications indiquées dans les commentaires du code suivant.

    ready: function (element, options) {
        var listView = element.querySelector(".groupeditemslist").winControl;
        var listViewZoomOut = element.querySelector(".groupeditemslistZoomOut").winControl;
        var semanticZoom = element.querySelector(".sezoDiv").winControl;
    
        // Add the following line of code.
        var itemTemplate = element.querySelector(".itemtemplate");
    
        listView.groupHeaderTemplate = element.querySelector(".headerTemplate");
    
        listView.oniteminvoked = this.itemInvoked.bind(this);
        listViewZoomOut.itemTemplate = element.querySelector(".itemtemplate");
        listViewZoomOut.oniteminvoked = this.groupInvoked.bind(this)
    
        // Change the last argument of the following function to itemTemplate.
        this.initializeLayout(listView,listViewZoomOut, semanticZoom, appView.value, itemTemplate);
        listView.element.focus();
     },
    
    // This function updates the page layout in response to viewState changes.
    updateLayout: function (element, viewState, lastViewState) {
        /// <param name="element" domElement="true" />
        /// <param name="viewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
        /// <param name="lastViewState" value="Windows.UI.ViewManagement.ApplicationViewState" />
        var listView = element.querySelector(".groupeditemslist").winControl;
        var listViewZoomOut = element.querySelector(".groupeditemslistZoomOut").winControl;
        var semanticZoom = element.querySelector(".sezoDiv").winControl;
    
        // Add the following line of code.
        var itemTemplate = element.querySelector(".itemtemplate");
    
        if (lastViewState !== viewState) {
            if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) {
                var handler = function (e) {
                    listView.removeEventListener("contentanimating", handler, false);
                    e.preventDefault();
                }
                listView.addEventListener("contentanimating", handler, false);
    
                // Change this line to pass through the item template.
                this.initializeLayout(listView, listViewZoomOut, semanticZoom,viewState,itemTemplate);
            }
            if (lastViewState === appViewState.snapped) {
                semanticZoom.zoomedOut = true;
                semanticZoom.forceLayout();
            }
        }
    },
    
  5. Le code de cette zone est à présent terminé. Nous devons ensuite appliquer des styles aux éléments.

    Dans le fichier GroupedItems.css, ajoutez les styles suivants pour les modèles d’éléments de petite, moyenne et grande tailles.

    .groupeditemspage .smallitemtemplate
    {
        width: 150px;
        height: 150px;
        overflow: hidden;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 0px 1fr;
        display: -ms-grid;
    }
    .smallitemtemplate .item-overlay .item-title {
        position: absolute; 
        padding-right: 6px;
        bottom: 10px; 
        font-size: 16pt;
    }
    .smallitemtemplate .item-overlay .item-subtitle {
        display: none;
    }
    .groupeditemspage .mediumitemtemplate
    {
        width: 310px;
        height: 150px;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 60px;
        display: -ms-grid;
        overflow: hidden;
    }       
    .groupeditemspage .largeitemtemplate
    {
        width: 310px;
        height: 310px;
        overflow: hidden;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 90px;
        display: -ms-grid;
    }
    
  6. Supprimez le code CSS suivant pour éviter tout écrasement de nos styles.

    .groupeditemspage .groupeditemslistZoomOut .win-item {
        -ns-grid-columns: 1fr;
        -ms-grid-rows: 1fr 90px;
        display: -ms-grid;
        height: 250px;
        width: 250px;
    }
    

    Remarque  Si vous n’avez pas implémenté la section du zoom sémantique, supprimez simplement le code CSS au-dessus du fichier pour empêcher la substitution du code CSS que nous venons d’ajouter pour les éléments aux différentes tailles.