Cómo agregar funcionalidad común a la plantilla de cuadrícula (aplicaciones de la Tienda Windows con JavaScript y HTML)

Windows 8: Este tema solo es válido para Windows 8. Para obtener más información acerca del uso de un modelo de navegación jerárquica en Windows 8.1, consulta Navegación jerárquica, de principio a fin (HTML).

Puedes ampliar la plantilla Aplicación de cuadrícula de Microsoft Visual Studio para incluir funcionalidades importantes adicionales que aporten valor a tu aplicación. Este tutorial está diseñado para usarse con Microsoft Visual Studio 2012 en Windows 8. Encontrarás el paquete de la aplicación para el tutorial completo aquí.

Esta es la página de aterrizaje de la plantilla Aplicación de cuadrícula predeterminada.

Captura de pantalla de la página de aterrizaje de la plantilla Aplicación de cuadrícula predeterminada

Después de completar el tutorial, tu aplicación contará con elementos de varios tamaños que usan distintas plantillas.

Captura de pantalla de una aplicación de cuadrícula con elementos de varios tamaños con distintas plantillas

También tendrá una barra de la aplicación que muestra los comandos contextuales cuando se seleccionan elementos.

Captura de pantalla de la aplicación con la barra de la aplicación y los comandos contextuales cuando se seleccionan elementos

Implementará el control SemanticZoom.

Captura de pantalla de una aplicación que implementa el control de zoom semántico

Se animará de manera fluida en todas las páginas de la aplicación.

La plantilla Aplicación de cuadrícula

Usar una plantilla de aplicación de Visual Studio es una buena forma de iniciarse rápidamente en una aplicación con estilo de diseño Microsoft "casi listo". Debido a que no todas las necesidades de los desarrolladores son iguales, no se incluyen todas las funcionalidades en las plantillas. En este tutorial se muestra cómo personalizar una aplicación basada en plantillas y agregarle valor, a la vez que se respetan las instrucciones para un estilo de diseño Microsoft. Concretamente, se muestra cómo personalizar una aplicación en función de la plantilla Aplicación de cuadrícula.

Cada sección de este tutorial se centra en cómo agregar una característica específica a la plantilla Aplicación de cuadrícula. Agregamos animaciones de transición de páginas, una barra de la aplicación con comandos globales y contextuales, el control SemanticZoom y elementos de múltiples tamaños en la cuadrícula. Explicamos la motivación detrás de cada adición y proporcionamos una guía paso a paso sobre cómo agregar la característica a la plantilla. Aunque nos centraremos en la plantilla Aplicación de cuadrícula, también puedes aplicar muchas de las nociones que aprendas aquí a la plantilla Aplicación dividida.

Tareas iniciales

Antes de comenzar con este tutorial, sigue estos pasos.

  1. Inicia Visual Studio 2012 y selecciona Archivo > Nuevo proyecto.
  2. Crea un nuevo proyecto de JavaScript con la plantilla Aplicación de cuadrícula.

Agregar animaciones de transición de páginas

Las aplicaciones con varias páginas deben ofrecer transiciones de página animadas cuando el usuario navegue por ellas. En esta sección, agregamos animaciones de transición de páginas a páginas en la plantilla Aplicación de cuadrícula.

Motivación

La animación de la página de entrada garantiza que la navegación entre las páginas sea rápida y fluida. Debes incluir estas animaciones cada vez que el usuario pase de una página a otra.

Implementación

En cada página de la plantilla, agregamos una función que devuelve los elementos de la página que queremos animar, en el orden en el que deben animarse. Después, agregamos una función a Navigator.js, que nos asegura que el sistema llamará a las animaciones cada vez que el usuario navegue por una página.

Para obtener más información sobre las animaciones de transición de páginas, consulta la Muestra de HTML de la biblioteca de animaciones.

  1. En GroupedItems.js, agrega una función a ui.Pages.define que devuelva una matriz de elementos HTML. Esta función proporciona el encabezado y la sección de la página que contiene la vista de cuadrícula.

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

    Nota  Tal como se muestra en el ejemplo anterior, debes agregar una coma al final de esta función salvo que sea la última función de la lista. Si es la última función, debes agregar una coma antes de la función. La misma regla se aplica en los pasos siguientes.

  2. En GroupDetail.js, agrega una función a ui.Pages.define que devuelva una matriz de elementos HTML para animar.

    getAnimationElements: function () {
        return [[this.element.querySelector("header")], [this.element.querySelector("section")]];
    },
    
  3. En ItemDetail.js, agrega una función a ui.Pages.define que devuelva una matriz de elementos HTML para animar.

    getAnimationElements: function () {
        return [[this.element.querySelector("header")], [this.element.querySelector(".content")]];
    },
    
  4. En Navigator.js, agrega una función a WinJS.Namespace.define que intente recuperar los elementos de una página para animarlos. Si la página no proporciona una función que devuelva una matriz de elementos, animamos toda la página.

    _getAnimationElements: function () {
        if (this.pageControl && this.pageControl.getAnimationElements) {
            return this.pageControl.getAnimationElements();
        }
        return this.pageElement;
    },
    
  5. Para establecer el foco correcto de la interfaz de usuario después de completar la animación, debemos agregar una función única a cada página. Llamamos a esta función una vez que se haya completado la animación de entrada.

    En GroupedItems.js, agrega una función a ui.Pages.define que establezca el foco de la interfaz de usuario en un control de la página.

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

    Nota  Tal como se muestra en el ejemplo anterior, debes agregar una coma al final de esta función salvo que sea la última función de la lista. Si es la última función, debes agregar una coma antes de la función. La misma regla se aplica en los pasos siguientes.

    En GroupDetail.js, agrega una función a ui.Pages.define que establezca el foco de la interfaz de usuario en un control de la página.

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

    En ItemDetail.js, agrega una función a ui.Pages.define que establezca el foco de la interfaz de usuario en un control de la página.

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

    Actualiza la función lista en cada página (ItemDetail.js, GroupDetail.js y GroupedItems.js) para quitar las siguientes líneas:

    • GroupedItems.js: listView.element.focus();
    • GroupDetail.js: listView.element.focus();
    • ItemDetail.js: element.querySelector(".content").focus();
  6. Por último, agrega la animación de la página de entrada. En Navigator.js, agrega el código siguiente a la parte superior de la función navigated (no _navigated) para establecer las animaciones en movimiento.

    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");
    
        ...
    

Agregar una barra de la aplicación

Aquí, agregamos una barra de la aplicación que contiene comandos globales o contextuales que quizás el usuario quiera ejecutar.

Motivación

Uno de los principios del estilo de diseño Microsoft es "anteponer el contenido al contenedor visual". Esta idea consiste en que menos es más y que solo los elementos más importantes deben aparecer en pantalla. Conforme a este principio, las aplicaciones de Windows 8 pueden usar la barra de la aplicación, un control común que permite que las aplicaciones coloquen comandos contextuales y relevantes en una ubicación de fácil acceso fuera de la pantalla. Los comandos que se usan con más frecuencia se encuentran cerca de los bordes derecho e izquierdo para que estén a mano. La barra de la aplicación permite que la superficie de la aplicación se centre en el contenido en vez de en los controles. Debido a que la barra de la aplicación se usa en todo el sistema, los usuarios estarán familiarizados con las interacciones de la barra de la aplicación. Esto aumenta la facilidad de uso de tu aplicación y hace que todo el sistema sea más cohesivo.

En práctica

El caso práctico del sitio web para una aplicación de la Tienda Windows muestra de qué manera los desarrolladores pueden replantearse cómo admitir comandos mediante la barra de la aplicación. A continuación, te mostramos un tutorial sobre cómo cargar una foto. En la parte izquierda hay un tutorial paso a paso de cómo se haría esto en web. A la derecha se presenta el escenario equivalente que usa el estilo de diseño Microsoft.

Capturas de pantalla de los pasos para cargar una foto

En el tutorial anterior, el sitio web muestra el comando para cargar tus fotos en la página. Por el contrario, la aplicación de la Tienda Windows usa la barra de la aplicación para mostrar la acción para cargar una foto. Después, el comando de la barra de la aplicación abre el selector de archivos. Este es solo un ejemplo de cómo las aplicaciones pueden tomar acciones y colocarlas en la barra de la aplicación para hacer más con menos y elegir el contenido sobre el contenedor visual.

Guía

Los comandos de la barra de la aplicación pueden variar según la página que está viendo el usuario o el contexto de la aplicación. Los comandos globales aparecen cuando el usuario navega por una página, mientras que los comandoscontextuales (o de selección) aparecen cuando están seleccionados determinados elementos. Analizaremos los comandos contextuales en el tema sobre cómo hacer que la cuadrícula sea seleccionable y cómo mostrar los comandos contextuales de la barra de la aplicación.

Nota  Los comandos que el usuario necesita para completar un flujo de trabajo, como por ejemplo enviar un correo electrónico o comprar un producto, representan una excepción y pueden colocarse en el Canvas.

Descripción general del ejemplo

En esta parte del tutorial, agregamos una barra de la aplicación a la página de elementos agrupados y a la página de detalles de elemento de la plantilla Aplicación de cuadrícula. Agregamos comandos que permiten al usuario cambiar el tamaño de las etiquetas en la página de elementos agrupados y el tamaño del texto de cuerpo en la página de detalles de elemento. Solo mostramos comandos que son relevantes a la página que se está visualizando y no mostramos la barra de la aplicación en la página de detalles de grupo.

En la captura de pantalla siguiente, la barra de la aplicación de la página de elementos agrupados muestra el comando para aumentar el tamaño de la etiqueta:

Captura de pantalla de la página de elementos agrupados que muestra el comando para aumentar el tamaño de la etiqueta en la barra de la aplicación

Esta captura de pantalla muestra la página de detalles de elemento con la barra de la aplicación que muestra el comando para aumentar el tamaño de texto:

Captura de pantalla de la página de detalles de elemento que muestra el comando para aumentar el tamaño de texto en la barra de la aplicación

Implementación

En los pasos siguientes, agregamos una barra de la aplicación global con botones globales de la barra de la aplicación. Mostramos y ocultamos los botones en las funciones listas de cada página. Enlazamos los comandos con las escuchas de eventos que llaman a los controladores de eventos para llevar a cabo la funcionalidad descrita anteriormente. Deshabilitamos la barra de la aplicación en la página de detalles de grupo porque la página no contiene comandos para mostrar.

  1. En Default.html, encontrarás una barra de la aplicación en la plantilla, pero con comentarios. Quita los comentarios para que la barra de la aplicación forme parte del HTML.

  2. El elemento div de la barra de la aplicación contiene un botón de marcador de posición. Reemplázalo con los siguientes botones.

    <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. En Default.js, agrega las siguientes escuchas de eventos.

    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. En Default.js, agrega las siguientes funciones junto a las otras funciones (con ámbito del archivo).

    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. Asegúrate de que mostremos solo los comandos relevantes en cada página.

    En GroupedItems.js, agrega el siguiente código a la función ready:

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

    En GroupDetail.js, agrega la siguiente línea de código a la función ready.

    appbar.winControl.disabled = true;
    

    En ItemDetail.js, agrega el siguiente código a la función ready.

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

Hacer que la cuadrícula se pueda seleccionar y mostrar los comandos contextuales de la barra de la aplicación

Aquí agregamos un control ListView para permitir que el usuario seleccione los elementos sobre los que quiera actuar. Para obtener más información sobre el control ListView, consulta el tema sobre el paquete de ejemplos de ListView en HTML.

Motivación

Es posible que el usuario quiera realizar distintas acciones en un elemento en ListView. Dividimos las acciones en una acción primaria y una serie de acciones secundarias. Una acción primaria puede llevarse a cabo al pulsar o hacer clic en un elemento. Por lo general, esto hace que el usuario navegue hasta el elemento.

El usuario realiza una acción secundaria al seleccionar un elemento y usar la barra de la aplicación para ejecutar un comando sobre el elemento. El usuario puede realizar diversas acciones secundarias en un elemento de la cuadrícula, como eliminar un elemento, agregar un elemento a Inicio, marcar un elemento como favorito, marcar un elemento como leído, cambiar el nombre de un elemento, etc. El usuario puede seleccionar elementos deslizándose perpendicularmente hacia la dirección del movimiento panorámico, haciendo clic con el botón secundario o presionando la barra espaciadora en el teclado.

En práctica

En el caso práctico de la aplicación de la Tienda Windows de iPad para Windows 8 se muestra de qué manera las aplicaciones pueden usar una cuadrícula seleccionable y una barra de la aplicación para replantear los comandos contextuales. A continuación, incluimos un tutorial sobre cómo eliminar fotos de una aplicación de diario de fotos. En la parte izquierda, hay un tutorial paso a paso de cómo se haría esto en el iPad. A la derecha, se presenta el escenario equivalente que usa el estilo de diseño Microsoft.

Capturas de pantalla de los pasos para eliminar fotos en un iPad y en una aplicación de la Tienda Windows

En la aplicación para iPad, el usuario primero especifica el modo de selección y después los comandos contextuales aparecen en la parte superior de la pantalla. En la aplicación de la Tienda Windows, los comandos contextuales se ocultan fuera de la pantalla en la barra de la aplicación. En el paso A, el usuario invoca la barra de la aplicación. Cuando se invoca por primera vez, no hay elementos seleccionados en la cuadrícula, por lo que solo se muestra el comando global. En el paso B, el usuario empieza a seleccionar elementos en la cuadrícula, que muestra los comandos contextuales en la parte izquierda de la barra de la aplicación.

Descripción

Cuando se selecciona un elemento, la barra de la aplicación debe invocarse para mostrar los comandos que son contextuales al elemento seleccionado. Cuando se seleccionan varios elementos, la barra de la aplicación debe permanecer visible y mostrar los comandos que son contextuales a todos los elementos seleccionados. Si el usuario anula la selección de elementos, cuando se anule la selección del último elemento, la barra de la aplicación debe ocultarse.

Cuando se muestra la barra de la aplicación con comandos contextuales, los comandos globales deben seguir mostrándose siempre. Los comandos globales deben estar alineados a la derecha de la barra de la aplicación y los comandos contextuales deben estar alineados a la izquierda.

Puedes elegir permitir que se seleccione solo un elemento o varios elementos a la vez.

Cuando una aplicación se acopla, la aplicación debe mantener el mayor estado posible. Cuando la barra de la aplicación se encuentra visible, si la aplicación está acoplada, la barra de la aplicación debe permanecer visible. Si se selecciona un elemento y se acopla la aplicación, el elemento debe permanecer seleccionado, si es posible.

Sin embargo, cuando se acopla la página de elementos agrupados, la vista acoplada muestra la vista alejada de los grupos. Como consecuencia, no es posible mostrar la selección de elementos individuales mientras se está en estado acoplado. Dado que los elementos individuales no se pueden mostrar, deben ocultarse los comandos contextuales de los elementos seleccionados anteriormente.

Descripción general del ejemplo

En esta sección del tutorial, hacemos que los elementos de la cuadrícula sean seleccionables. Esto permite seleccionar varios elementos a la vez. Una vez que se selecciona un elemento, la barra de la aplicación se invoca de inmediato y aparece el comando contextual para marcar el elemento como leído. Esto hace que el título de todos los elementos seleccionados aparezca en color gris. Al anular la selección del elemento, se oculta la barra de la aplicación. Al optar por marcar los elementos como leídos se realiza la acción, se anula la selección de elementos y se oculta la barra de la aplicación.

Esta captura de pantalla muestra la página de elementos agrupados con varios elementos seleccionados y la barra de la aplicación que muestra el comando contextual para marcar como leído y el comando global para aumentar el tamaño de la etiqueta:

Captura de pantalla que muestra la página de elementos agrupados con varios elementos seleccionados y la barra de la aplicación que muestra un comando contextual y un comando global

Implementación

En los pasos siguientes habilitamos la selección de elementos de cuadrícula. También agregamos una escucha de eventos que muestra la barra de la aplicación que contiene comandos contextuales para el elemento seleccionado.

  1. En GroupedItems.html, haz que los elementos de GridView se puedan seleccionar al cambiar el modo de selección de la ListView de "none" a "multi".

    data-win-options="{ selectionMode: 'multi' }"
    
  2. En Default.html, agrega el siguiente comando contextual de la barra de la aplicación junto a otros comandos de la barra de la aplicación.

    <button id="markItem" data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'markItem', section:'selection', label:'Mark as read', icon:'accept'}"></button>
    
  3. En Default.js, agrega las siguientes escuchas de eventos junto a otras escuchas de eventos de la barra de la aplicación en el controlador app.onactivated.

    document.getElementById("markItem").onclick = markItem;
    
  4. En Default.js, agrega la función markItem junto a las demás funciones de comandos de la barra de la aplicación.

    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. En ItemDetail.js, modifica la llamada a la función hideCommands para ocultar también el botón para marcar el elemento.

    appbar.winControl.hideCommands([labelSize, markItem]);
    
  6. En GroupedItems.js, modifica la llamada a la función hideCommands para ocultar también el botón para marcar el elemento.

    appbar.winControl.hideCommands([textSize, markItem]);
    
  7. En GroupedItems.js, agrega las funciones siguientes antes de la función ui.Pages.define. Estas funciones muestran y ocultan, mediante programación, la barra de la aplicación.

    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. En GroupedItems.js, agrega las funciones siguientes junto a las funciones precedentes. Las siguientes funciones muestran y ocultan los comandos de la barra de la aplicación.

    function showItemCommands() {
        appbar.winControl.showCommands([markItem]);
    }
    
    function hideItemCommands() {
        appbar.winControl.hideCommands([markItem]);
    }
    
  9. En GroupedItems.js, agrega código para abrir y cerrar, mediante programación, la barra de la aplicación. En la función ready, agrega un controlador de eventos de cambio de selección para el control ListView de la página.

    listView.onselectionchanged = this.itemSelected.bind(this);
    
  10. En GroupedItems.js, agrega las siguientes funciones para administrar la selección desde la 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. En GroupedItems.js, agrega una llamada a la función hideItemCommands desde updateLayout. Esta llamada oculta el comando contextual en la barra de la aplicación cuando la aplicación se acopla. El comando contextual no debe aparecer cuando la aplicación se acopla porque el elemento no se muestra como seleccionado en el estado acoplado.

    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";
            }
        }
    }
    

Agregar el control SemanticZoom

En esta sección, agregamos un control SemanticZoom para proporcionar una vista semántica de los elementos en la cuadrícula de ListView. Cada elemento de la vista alejada corresponde a un grupo de elementos de la cuadrícula.

Captura de pantalla que muestra elementos en la cuadrícula de ListView

Para obtener más información sobre el zoom semántico, consulta el ejemplo de SemanticZoom y la agrupación de ListView en HTML.

Motivación

La plantilla Aplicación de cuadrícula usa un patrón de navegación jerárquico. Esto significa que el contenido interesante de cada sección principal de una aplicación puede promocionarse en la página de aterrizaje. Esta página de aterrizaje puede crecer hasta tener un gran número de grupos o una gran cantidad de elementos. Aunque el usuario puede obtener una panorámica para examinar el contenido, considera la posibilidad de agregar el zoom semántico para permitir que el usuario pueda saltar rápidamente a distintas secciones de la página.

El control SemanticZoom permite al usuario alejar la cuadrícula y obtener una vista semántica, en vez de óptica, de los grupos. Desde esta vista, el usuario puede seleccionar un grupo para acercarlo de nuevo. El zoom semántico permite al usuario ver grupos grandes de elementos sin movimiento panorámico, además de proporcionarle una vista de los grupos de la cuadrícula.

La vista alejada también proporciona metainformación sobre los grupos para guiar al usuario a secciones interesantes de la página. Por ejemplo, la vista alejada puede mostrar la cantidad de elementos en el grupo que tienen una superposición de números.

En práctica

El caso práctico de aplicación de la Tienda Windows de iPad para Windows 8 muestra de qué manera las aplicaciones pueden usar el zoom semántico para permitirle al usuario saltar rápidamente entre grupos de elementos. A continuación ofrecemos un tutorial sobre cómo usar el zoom semántico para obtener una vista semántica de los grupos. En la parte izquierda hay un tutorial paso a paso de cómo se haría esto en el iPad. A la derecha se presenta el escenario equivalente que usa el estilo de diseño Microsoft.

Captura de pantalla que muestra de qué manera el zoom semántico se compara con iPad para cambiar entre grupos de elementos

En la aplicación para iPad, el usuario puede pulsar el botón Años en la barra de navegación superior para que aparezca un elemento emergente con una lista de años. En la aplicación de la Tienda Windows, el usuario puede usar un gesto de reducir para usar el zoom semántico para alejarse de los grupos. En este ejemplo, esta vista semántica se ha configurado para mostrar el número de comentarios en el grupo de comentarios y para mostrar qué meses contienen fotos.

Implementación

En los pasos siguientes, agregamos el contenedor de zoom semántico a la página de elementos agrupados y agregamos una plantilla para los grupos de zoom semántico.

Enlazamos los grupos semánticos con el origen de datos del grupo y después aplicamos hojas de estilos en cascada (CSS) para aplicarles estilo a los grupos de manera adecuada.

  1. En GroupedItems.html, busca el control ListView en la sección de contenido principal:

    <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>
    

    En su forma más básica, el control SemanticZoom es un contenedor que muestra uno de sus elementos secundarios, según el factor de escala actual. Aquí agregaremos un control SemanticZoom como la "raíz" de la sección y colocaremos dos controles ListView en el control SemanticZoom.

    Primero, cambia la sección de contenido principal de la siguiente manera:

    <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>
    
    • Ahora que hemos configurado los controles en la página, agregaremos código para asegurarnos de que se muestre el contenido correcto.

      En GroupedItems.js, agrega y cambia lo siguiente a la función ready. En esencia, simplemente duplicamos lo que ya existe para la primera vista de lista y lo aplicamos a nuestra nueva ListView alejada.

      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. En GroupedItems.js, actualiza la función inializeLayout para aceptar la segunda vista de lista. La función también administra el control de zoom semántico en vista acoplada.

    // 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. En GroupedItems.js, actualiza la función updateLayout para configurar el nuevo ListView para que muestre la lista de grupos que usan el origen de datos agrupado.

    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. En GroupedItems.js, agrega la siguiente función para controlar cuándo el usuario pulsa un elemento en la vista alejada o hace clic en él.

    groupInvoked: function (args) {
        var group = Data.groups.getAt(args.detail.itemIndex);
        nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: group.key });
    },
    
  5. Ahora es el momento de agregar CSS para lograr que los elementos se vean bien.

    Nota  Para implementar las instrucciones de experiencia del usuario para el zoom semántico, usamos una cuadrícula de CSS con solo una fila y una columna. Colocamos el encabezado y el contenido de la sección en esta cuadrícula para que puedan superponerse. Esto significa que el control SemanticZoom puede abarcar toda la pantalla, incluso el encabezado de página.

    En GroupedItems.css, agrega los siguientes estilos para que el control SemanticZoom use todo el espacio disponible en pantalla. De esta forma se modifica el diseño de todo el fragmento, de modo que el contenido del encabezado de página se posiciona por debajo del contenido de la sección, que contiene el sezo div.

    .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. Después, debemos resolver algunos problemas que se generan porque el control SemanticZoom está posicionado con un margen negativo.

    Para ver el encabezado de página, reduce la vista de lista a 133 píxeles.

    .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. Ahora, debemos aplicarle estilo al elemento para mostrarlo en la vista alejada. Agrega el siguiente código CSS y asegúrate de declararlo después del estilo groupeditemslist.win-item, que ya forma parte del archivo:

        .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. Ahora tenemos que arreglar las vistas acoplada y vertical porque hicimos cambios que afectaron a las vistas. Agrega todos los estilos siguientes a la sección @media screen and (-ms-view-state: snapped) de GroupedItems.css.

    Agrega el estilo siguiente para volver a alinear la sección de encabezado.

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

    Agrega un margen a la parte superior de la superficie de modo que la lista no cubra el título.

    .groupeditemspage .groupeditemslist .win-vertical.win-viewport .win-surface {
        margin-bottom: 30px;
        /* Add the following line. */
        margin-top: 133px;
        margin-left: 6px;
    }
    
  9. Los problemas de la vista acoplada ahora ya están resueltos. Después, debemos actualizar la vista vertical.

    En GroupedItems.css, agrega los siguientes estilos a la sección @media screen and (-ms-view-state: fullscreen-portrait).

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

Presentar elementos de múltiples tamaños en la cuadrícula

Presentar una cuadrícula con elementos de varios tamaños es una buena forma de personalizar la cuadrícula y diferenciar a la aplicación. Con elementos de múltiples tamaños, una aplicación puede resaltar algunos elementos y quitarle importancia a otros al elegir los tamaños más grandes para los elementos más importantes.

Los elementos de distinto tamaño pueden usar también distintas plantillas. Por ejemplo, quizás quieras mostrar imágenes con un texto superpuesto para los elementos más grandes y solo texto para los elementos más pequeños.

En práctica

El caso práctico de aplicación de la Tienda Windows de iPad para Windows 8 muestra de qué manera las aplicaciones pueden usar elementos de varios tamaños y distintas plantillas para personalizar la plantilla de cuadrícula y personalizar la marca de la aplicación. En los ejemplos que siguen se compara la página de aterrizaje de una aplicación para iPad y la página de aterrizaje de una aplicación de la Tienda Windows en las que se ha personalizado la plantilla de cuadrícula.

Capturas de pantalla que comparan las páginas de aterrizaje de la aplicación para iPad y la aplicación de la Tienda Windows

En la aplicación para iPad, todo el contenido se presenta en una cuadrícula uniforme de imágenes. En la aplicación de la Tienda Windows, los elementos se presentan en varios tamaños y con plantillas diferentes. El primer grupo contiene elementos con una imagen pequeña y texto descriptivo en paralelo. El resto de los grupos constan de imágenes grandes y pequeñas superpuestas con texto de varios tamaños.

Descripción general del ejemplo

En el ejemplo de esta sección se personaliza la cuadrícula en la página de elementos agrupados para usar elementos con tres tamaños distintos. La cuadrícula predeterminada usa elementos de 200 x 200 píxeles. Usaremos elementos que tienen 310 x 310 píxeles, 310 x 150 píxeles y 150 x 150 píxeles. Los elementos más grandes usarán la imagen predeterminada más el diseño de texto superpuesto y los elementos más pequeños contendrán solo texto.

Aquí te mostramos la página de elementos agrupados con los tres tamaños de elementos diferentes:

Captura de pantalla de la página de elementos agrupados con los tres tamaños de elementos diferentes:

Implementación

Para lograr la experiencia de usuario deseada para este escenario, necesitamos definir nuestra propia función de representación para la vista de lista. También debemos agregar CSS para los elementos con diferentes tamaños.

Para la función de representación, leemos en una plantilla HTML predefinida en lugar de crear una manualmente en la función, pero ambas alternativas son correctas.

Cuando se eligen varios tamaños para los elementos, ten en cuenta que los tamaños deben cumplir con la fórmula siguiente itemSize = ((slotSize + margin) x multiplier) – margin.

  1. En GroupedItems.html, agrega la siguiente plantilla de elemento.

    <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. En GroupedItems.js, agrega la función siguiente antes de la definición de página (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. En GroupedItems.js, agrega la función groupInfo debajo, también fuera de la definición de la página. Esta función le indica a ListView que use elementos de varios tamaños en la vista e indica el "tamaño base" de los elementos. El tamaño base es el elemento más pequeño que se muestra en la lista. Los demás elementos deben ser múltiplos de este tamaño para que los diseños funcionen correctamente.

    function groupInfo() {
        return {
            enableCellSpanning: true,
            cellWidth: 150,
            cellHeight: 150
        };
    }
    
  4. Ahora debemos enlazar estas funciones nuevas con los controles de ListView en la página de elementos agrupados. Ya que no queremos usar imágenes con tamaños variables en la vista acoplada, debemos cambiar la forma en la que se aplican las plantillas al código de vista de lista tanto en la función ready como en la función updateLayout.

    En GroupedItems.js, cambia la función initializeLayout de modo que cuando la página esté en vista acoplada, mostremos una lista sin formato de los grupos. Sin embargo, en la vista horizontal, usamos elementos de múltiples tamaños.

    // 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;
        }
    },
    

    Quita la línea que asigna una plantilla de elemento a la ListView y realiza los cambios indicados en los comentarios en el código siguiente.

    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. Ya hemos terminado el código para esta área. A continuación debemos aplicar estilo a los elementos.

    En GroupedItems.css, agrega los siguientes estilos para las plantillas de elementos pequeños, medianos y grandes.

    .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. Quita este código CSS para evitar sobrescribirlo con nuestra aplicación de estilos.

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

    Nota  Si no implementaste la sección de zoom semántico, solo debes eliminar la CSS anterior del archivo para impedir que sobrescriba la CSS que acabamos de agregar para los elementos de varios tamaños.