Cómo mostrar elementos de diferentes tamaños (HTML)
[ Este artículo está destinado a desarrolladores de Windows 8.x y Windows Phone 8.x que escriben aplicaciones de Windows Runtime. Si estás desarrollando para Windows 10, consulta la documentación más reciente ]
De manera predeterminada, ListView asigna el mismo tamaño para cada elemento de la lista. Cuando uses un diseño de cuadrícula, podrás modificar este comportamiento y mostrar elementos que tengan distintos tamaños haciendo que estos elementos se expandan por varias celdas.
Lo que debes saber
Tecnologías
Requisitos previos
- Damos por supuesto que puedes crear y usar un objeto ListView básico. Para ver una introducción al control ListView, consulta Inicio rápido: Agregar ListView.
Instrucciones
Acerca de las celdas y los tamaños en ListView
Antes de entrar en el código, sería útil comprender cómo ListView controla el tamano de los elementos.
De manera predeterminada, ListView asigna celdas del mismo tamaño para cada elemento que contiene. Aquí vemos un ListView que contiene elementos que tienen todos el mismo tamaño.
Aquí vemos el mismo ListView con una celda individual resaltada.
El tamaño de la celda viene determinado por el tamaño del primer elemento del ListView. Cuando el ListView contiene elementos de distintos tamaños, todavía puede asignar el tamaño de la celda en función del tamaño del primer elemento. Por lo tanto, si un elemento es mayor que los otros, se recortará para que coincida con los tamaños de los otros elementos del ListView.
Puedes cambiar este comportamiento habilitando la expansión de celdas. Al hacerlo, un elemento puede ocupar varias celdas. En este ejemplo, la expansión de celdas está activada, de modo que el elemento de mayor tamaño ocupa 5 celdas en vez de una.
Cuando actives la expansión de celdas, también podrás especificar explícitamente el tamaño de la celda base. Recomendamos que cada elemento del ListView tenga un tamaño que sea un múltiplo del tamaño de la celda base. En el siguiente ejemplo, se ha modificado el elemento de mayor tamaño para que tenga el doble de altura que la celda base, pero el mismo ancho.
Aquí se muestra cómo crear un ListView que contenga elementos de tres tamaños diferentes.
Paso 1: Crea tus datos en el ListView
Primero vamos a crear un origen de datos y un ListView.
En un archivo JavaScript, define un origen de datos para el ListView. Este ejemplo crea un List a partir de una matriz de objetos JSON y los hace accesibles públicamente mediante WinJS.Namespace.define para exponerlo en un espacio de nombres denominado
DataExamples
.Los datos son similares a los ejemplos que hemos mostrado en otros temas, como Inicio rápido: agregar ListView, pero con un elemento adicional: un campo
type
. Tiene tres valores posibles de menor a mayor tamaño: "smallListIconTextItem", "mediumListIconTextItem" y "largeListIconTextItem". En los siguientes pasos usaremos este campo para asignar una clase CSS que determine el tamaño de cada elemento.(function () { "use strict"; var myCellSpanningData = new WinJS.Binding.List([ { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "smallListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "mediumListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "largeListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "mediumListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "smallListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "smallListIconTextItem" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "mediumListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "mediumListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "smallListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "smallListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "smallListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "smallListIconTextItem" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "smallListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "smallListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "mediumListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "smallListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "largeListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "mediumListIconTextItem" } ]); WinJS.Namespace.define("DataExamples", { myCellSpanningData: myCellSpanningData }); })();
(Si vas a probar este código y quieres usar las imágenes que se usan en este ejemplo, descarga la Muestra de plantillas del elemento ListView).
En el archivo HTML, crea un ListView que use el diseño de expansión de celdas. Establece su propiedad itemDataSource en el origen de datos que creaste en el paso anterior.
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { type: WinJS.UI.CellSpanningLayout } }" ></div>
Paso 2: Define el tamaño de la celda base y habilita la expansión de celdas
Ahora tenemos que definir el tamaño de la celda base.
Para hacer que el ListView use un diseño de expansión de celdas, deberás crear un objeto CellSpanningLayout y usarlo para establecer la propiedad layout del control ListView. Para activar la expansión de celdas y definir el tamaño de la celda base, crea una función groupInfo que proporcione esta información y úsala para establecer la propiedad groupInfo del objeto CellSpanningLayout. La función groupInfo que definamos debe devolver un objeto que contenga las siguientes propiedades.
enableCellSpanning
Establecida como true para activar la expansión de celdas. El valor predeterminado es false.cellWidth
El ancho de la celda base.cellHeight
La altura de la celda base.
Para este ejemplo usaremos un tamaño de celda base de 310 × 80 píxeles.
Para definir el tamaño de la celda base y habilitar la expansión de celdas
En el archivo JavaScript donde creaste los datos, crea una función groupInfo que active la expansión de celdas y defina la celda base en 310 × 80 píxeles.
// Enable cell spanning and specify // the cellWidth and cellHeight for the items var groupInfo = function groupInfo() { return { enableCellSpanning: true, cellWidth: 310, cellHeight: 80 }; };
Usa WinJS.Utilities.markSupportedForProcessing para que la función sea accesible en HTML.
// Enable cell spanning and specify // the cellWidth and cellHeight for the items var groupInfo = function groupInfo() { return { enableCellSpanning: true, cellWidth: 310, cellHeight: 80 }; }; WinJS.Utilities.markSupportedForProcessing(groupInfo);
(De manera predeterminada, las funciones y los controladores de eventos no son accesibles en los controles de la Biblioteca de Windows para JavaScript por razones de seguridad. La función WinJS.Utilities.markSupportedForProcessing te permite invalidar este comportamiento predeterminado. Con esto se supone que el HTML que proporciones tiene el formato correcto y puede ser procesado por WinJS. Para obtener más información, consulta Codificar aplicaciones básicas).
La llamada a WinJS.Utilities.markSupportedForProcessing en tu función no hace que sea accesible públicamente. Eso lo haremos en el siguiente paso.
Haz que tu función groupInfo sea accesible públicamente exponiéndola a través de un espacio de nombres. Este ejemplo actualiza el espacio de nombres
DataExamples
que hemos creado en el paso 1.1.WinJS.Namespace.define("DataExamples", { groupInfo : groupInfo, myCellSpanningData: myCellSpanningData });
Actualiza tu ListView para que use la función groupInfo.
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { groupInfo: DataExamples.groupInfo, type: WinJS.UI.GridLayout } }" ></div>
Paso 3: Define el tamaño de un elemento que se expande una sola celda
Ahora que hemos definido el tamaño de la celda base, podremos definir los tamaños de nuestros elementos. Cuando definimos nuestros datos en el primer paso, incluimos un campo type
que contiene información sobre el tamaño que puede tener el elemento: pequeño, mediano o grande. Podemos usar esa información para asignar los tamaños de los elementos. La mejor manera de asignar los tamaños es mediante el uso de clases CSS. Este enfoque funciona tanto si usamos una función de plantillas o un WinJS.Binding.Template.
Nuestra celda base es de 310 píxeles de ancho y 80 píxeles de altura. El tamaño total de cada elemento debe ser un múltiplo del tamaño de la celda base. El tamaño de la celda base es el tamaño del elemento más su espaciado interno, márgenes y bordes:
Esta es la fórmula para calcular el tamaño de la celda base:
- ancho de la celda base = ancho del elemento + espaciado interno horizontal del elemento + margen horizontal del elemento + grosor del borde del elemento
- altura de la celda base = altura del elemento + espaciado interno vertical del elemento + margen vertical del elemento + grosor del borde del elemento
Para definir el tamaño de un elemento que ocupa una sola celda base
Definamos el tamaño del elemento más pequeño. En el archivo CSS, crea una clase de hojas de estilo CSS llamada "smallListIconTextItem".
.smallListIconTextItem { }
El elemento más pequeño solo ocupará una celda. Establezcamos el ancho del elemento en 300px, su altura en 70px y su espaciado interno en 5px.
.smallListIconTextItem { width: 300px; height: 70px; padding: 5px; overflow: hidden; background-color: Pink; display: -ms-grid; }
Ahora comprobemos esos números con nuestra fórmula para asegurarnos de que coinciden con nuestro tamaño de celda base.
ancho de la celda = ancho del elemento + espaciado interno izquierdo + espaciado interno derecho + grosor del borde + margen izquierdo + margen derecho = 300 + 5px + 5px + 0 + 0 + 0 = 310
altura de la celda = altura del elemento + espaciado interno superior + espaciado interno inferior + grosor del borde + margen superior + margen inferior = 70px + 5px + 5px + 0 + 0 + 0= 80
Coinciden con nuestro tamaño de celda base, así que podemos ir al siguiente paso.
Paso 4: Define los tamaños de los elementos que se expanden 2 o más celdas
Al determinar el tamaño de un elemento que se expande una o más celdas, también deberás tener en cuenta el margen win-container
entre las celdas en las que se expande. Por ejemplo, si tienes un elemento que se expande una celda horizontalmente pero dos celdas verticalmente, el tamaño total del elemento incluirá el margen inferior win-container
de la primera celda y el margen superior win-container
de la segunda celda, como se muestra aquí:
Esta es la fórmula para calcular el tamaño total de un elemento que se expande en varias celdas:
ancho total del elemento = number of cells * ancho de celda base + (number of cells - 1) * (
win-container
margen izquierdo +win-container
margen derecho)altura total del elemento = number of cells * altura de celda base + (number of cells - 1) * (
win-container
margen superior +win-container
margen inferior)
Sugerencia El margen win-container
es de 5 píxeles de manera predeterminada.
Para definir el tamaño de un elemento que se expande verticalmente en dos celdas
Usa nuestra fórmula para determinar la altura total del elemento:
altura total del elemento = number of cells * altura de la celda base + (number of cells - 1) * (
win-container
margen superior +win-container
margen inferior) = 2 * 80 + (2-1) * (5 + 5) = 170Crea el estilo CSS que especifique el tamaño del elemento. En este ejemplo se define un elemento que tiene una altura de 160 píxeles y un espaciado interno de 5 píxeles, de modo que su altura es 160 + 5 + 5 = 170. Como el elemento solo se expande en una celda horizontalmente, dale el mismo ancho y el mismo espaciado interno que la clase CSS que hemos creado en el paso 3,
smallListIconTextItem
..mediumListIconTextItem { width: 300px; height: 160px; padding: 5px; overflow: hidden; background-color: LightGreen; display: -ms-grid; }
Para definir el tamaño de un elemento que se expande verticalmente en tres celdas
Usa nuestra fórmula para determinar la altura total del elemento:
altura total del elemento = number of cells * altura de la celda base + (number of cells - 1) * (
win-container
margen superior +win-container
margen inferior) = 3 * 80 + (3-1) * (5 + 5) = 260Crea el estilo CSS que especifique el tamaño del elemento. En este ejemplo se define un elemento que tiene una altura de 250 píxeles y un espaciado interno de 5 píxeles, de modo que su altura es 250 + 5 + 5 = 260.
.largeListIconTextItem { width: 300px; height: 250px; padding: 5px; overflow: hidden; background-color: LightBlue; display: -ms-grid; }
Paso 5: Crea la función de cambio de tamaño para CellSpanningLayout
Además de la función groupInfo, CellSpanningLayout necesita exponer una función itemInfo que determine cómo cambiar el tamaño de varios 'tipos' en el origen de datos. La función itemInfo debe devolver un objeto de JavaScript que contenga las siguientes propiedades:
width
El ancho del elemento individual en el ListView.height
La altura del elemento individual en el ListView.
Para definir el tamaño de los elementos individuales en el ListView
En el archivo de JavaScript donde has creado los datos, crea una función itemInfo que recupere un elemento del origen de datos y devuelva el tamaño y la altura correspondientes de ese elemento.
// Item info function that returns the size of a cell spanning item var itemInfo = WinJS.Utilities.markSupportedForProcessing(function itemInfo(itemIndex) { var size = { width: 310, height: 80 }; // Get the item from the data source var item = DataExamples.myCellSpanningData.getAt(itemIndex); if (item) { // Get the size based on the item type switch (item.type) { case "smallListIconTextItem": size = { width: 310, height: 80 }; break; case "mediumListIconTextItem": size = { width: 310, height: 170 }; break; case "largeListIconTextItem": size = { width: 310, height: 260 }; break; default: } } return size; });
La itemInfo está ajustada por una llamada a WinJS.Utilities.markSupportedForProcessing para hacer que se pueda acceder a la función en HTML.
Haz que tu función itemInfo sea accesible públicamente exponiéndola a través de un espacio de nombres. Este ejemplo actualiza el espacio de nombres
DataExamples
que hemos creado en el paso 1.1.WinJS.Namespace.define("DataExamples", { myCellSpanningData: myCellSpanningData, groupInfo: groupInfo, itemInfo: itemInfo });
Actualiza tu ListView para que use la función itemInfo.
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }" ></div>
Paso 6: Crea la plantilla
El paso final consiste en crear una plantilla o una función de plantillas que use las clases CSS que acabamos de definir. Mostraremos cómo crear tanto un WinJS.Binding.Template como una función de plantillas.
Opción A: Usa un WinJS.Binding.Template
En el HTML, define un WinJS.Binding.Template.
<div id="myItemTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div> <img src="#" class="regularListIconTextItem-Image" data-win-bind="src: picture" /> <div class="regularListIconTextItem-Detail"> <h4 data-win-bind="innerText: title"></h4> <h6 data-win-bind="innerText: text"></h6> </div> </div> </div>
¿Recuerdas cómo, cuando definimos nuestros datos en el Paso 1.1, incluimos una propiedad
type
que especificaba qué clase CSS se asigna a cada elemento? Ahora podemos usar estos datos. En el elemento raíz del elemento, enlaza el nombre de la clase al valor del campotype
de nuestros datos.<div id="myItemTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div data-win-bind="className: type"> <img src="#" class="regularListIconTextItem-Image" data-win-bind="src: picture" /> <div class="regularListIconTextItem-Detail"> <h4 data-win-bind="innerText: title"></h4> <h6 data-win-bind="innerText: text"></h6> </div> </div> </div>
Nota El ejemplo se enlaza con className, no con class. Eso es porque, aunque uses la "clase" en HTML, la propiedad JavaScript de soporte se llama "className". Cuando la aplicación procesa el atributo data-win-bind, asigna los valores enlazados mediante llamadas a JavaScript.
Esto significa que el nombre del atributo HTML y el nombre de la propiedad JavaScript de soporte sean diferentes, usarás el nombre de la propiedad JavaScript cuando establezcas data-win-bind.
Actualiza tu ListView para que use la plantilla; para ello, configura la propiedad itemTemplate como el identificador de tu plantilla.
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, itemTemplate: select(#'myItemTemplate'), layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }"></div
Si quieres, puedes usar una función de plantillas en vez de un WinJS.Binding.Template. El uso de una función de plantillas puede darte más flexibilidad para generar el HTML y asignar tamaños.
Opción B: Usa una función de plantillas
Define tu función de plantillas en un archivo JavaScript. Puedes guardar este código en el mismo archivo que contiene los datos o puedes agregarlo en un archivo distinto. Asegúrate de que la página que contiene tu ListView hace referencia a este archivo.
Este ejemplo usa los datos de
type
de cada elemento para asignarlos a la clase CSS que determina su tamaño.var myCellSpanningJSTemplate = function myCellSpanningJSTemplate(itemPromise) { return itemPromise.then(function (currentItem) { var result = document.createElement("div"); // Use source data to decide what size to make the // ListView item result.className = currentItem.data.type; result.style.overflow = "hidden"; // Display image var image = document.createElement("img"); image.className = "regularListIconTextItem-Image"; image.src = currentItem.data.picture; result.appendChild(image); var body = document.createElement("div"); body.className = "regularListIconTextItem-Detail"; body.style.overflow = "hidden"; result.appendChild(body); // Display title var title = document.createElement("h4"); title.innerText = currentItem.data.title; body.appendChild(title); // Display text var fulltext = document.createElement("h6"); fulltext.innerText = currentItem.data.text; body.appendChild(fulltext); return result; }); };
Realiza una llamada a markSupportedForProcessing en la función para que se pueda acceder a ella mediante marcado.
WinJS.Utilities.markSupportedForProcessing(myCellSpanningJSTemplate);
Usa WinJS.Namespace.define para que la función sea accesible públicamente.
WinJS.Namespace.define("Templates", { myCellSpanningJSTemplate: myCellSpanningJSTemplate });
En el HTML, actualiza tu ListView para usar la función de plantillas estableciendo la propiedad itemTemplate como el nombre de la función de plantillas.
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, itemTemplate: Templates.myCellSpanningJSTemplate layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }" ></div>
Independientemente de qué enfoque de plantillas hayas usado, cuando ejecutes la aplicación, el ListView mostrará elementos de varios tamaños.
Observaciones
Edición de elementos
Cuando cambies los elementos de un ListView que tiene habilitada la expansión de celdas, realiza una llamada a ListView.recalculateItemPosition siempre que hagas un cambio.
- Si el origen de datos es una llamada a WinJS.Binding.List, llama a recalculateItemPosition justo después de realizar la edición (por ejemplo, después de llamar a List.push o List.splice).
- Si el origen de datos es un VirtualizedDataSource personalizado, haz una llamada a beginEdits, realiza las ediciones, llama a recalculateItemPosition y después llama a endEdits.