How to display items that are different sizes (HTML)

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

By default, the ListView allocates the same size for each item in the list. When you use a grid layout, you can modify this behavior and display items that are different sizes by making the items span multiple cells.

What you need to know

Technologies

Prerequisites

Instructions

About cells and sizing in the ListView

Before we get into the code, it's helpful to understand how the ListView handles item sizing.

By default, the ListView allocates the same size cell for each item it contains. Here's a ListView that contains items that all have the same size.

A ListView that contains items that have the same size

Here's the same ListView, with an individual cell highlighted.

A cell in a ListView

The size of the cell is determined by the size of the first item in the ListView. When the ListView contains items of different sizes, it still allocates the cell size based on the size of the first item. So, if one item is larger than the others, it will be clipped to match the sizes of the other ListView items.

A ListView that contains items of different sizes

You can change this behavior by enabling cell spanning. When you do so, an item can take up multiple cells. In this example, cell spanning is turned on so the larger item takes up 5 cells instead of one.

A ListView with cell spanning enabled

When you turn on cell spanning, you can also explicitly specify the size of the base cell. We recommend that every item in your ListView has a size that's a multiple of base cell size. In the next example, the larger item is modified so that it's twice the height of the base cell but has the same width.

A ListView with items that are multiples of the same base cell size

Here's how to create a ListView that contains items of three different sizes.

Step 1: Create your data and the ListView

First, let's create a data source and a ListView.

  1. In a JavaScript file, define a data source for the ListView. This example creates a List from an array of JSON objects and makes it publicly accessible by using WinJS.Namespace.define to expose it through a namespace named DataExamples.

    The data is similar to the examples we've shown in other topics, such as Quickstart: Adding a ListView, with one addition: a type field. It has three possible values: "smallListIconTextItem", "mediumListIconTextItem", and "largeListIconTextItem". In later steps, we use this field to assign a CSS class that determines the size of each item.

    (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
            });
    
    })();
    

    (If you're coding along and want to use the images used in this example, you can get them by downloading the ListView item templates sample.)

  2. In your HTML file, create a ListView that uses the cell-spanning layout. Set its itemDataSource property to the data source you created in the previous step.

    <div id="myListView" 
        data-win-control="WinJS.UI.ListView" 
        data-win-options="{ 
            itemDataSource: DataExamples.myCellSpanningData.dataSource, 
            layout: {  
                type: WinJS.UI.CellSpanningLayout 
            } 
        }"
    ></div>
    

Step 2: Define the base cell size and enable cell spanning

Now we need to define the size of the base cell.

To make the ListView use a cell-spanning layout, you create a CellSpanningLayout object and use it to set the ListView control's layout property. To turn on cell spanning and define the size of the base cell, you create a groupInfo function that provides this info and use it to set the CellSpanningLayout object's groupInfo property. The groupInfo function that we define must return an object that contains these properties.

  • enableCellSpanning
    Set to true to turn on cell spanning. The default value is false.

  • cellWidth
    The width of the base cell.

  • cellHeight
    The height of the base cell.

For this example, let's use a base cell size of 310×80 pixels.

JJ657974.wedge(en-us,WIN.10).gifTo define the size of the base cell and enable cell spanning

  1. In the JavaScript file where you created your data, create a groupInfo function that turns on cell spanning and defines the base cell as 310×80 pixels.

    // Enable cell spanning and specify
    // the cellWidth and cellHeight for the items
    var groupInfo = function groupInfo() {
        return {
            enableCellSpanning: true,
            cellWidth: 310,
            cellHeight: 80
        };
    };
    
  2. Use WinJS.Utilities.markSupportedForProcessing to make your function accessible in 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);
    

    (By default, functions and event handlers are inaccessible to Windows Library for JavaScript controls for security reasons. The WinJS.Utilities.markSupportedForProcessing function lets you override this default behavior. This assumes that the HTML that you provide is correctly formed and can be processed by WinJS. For more info, see Coding basic apps.)

    Calling WinJS.Utilities.markSupportedForProcessing on your function doesn't make it publicly accessible. We do that in the next step.

  3. Make your groupInfo function publicly accessible by exposing it through a namespace. This example updates the DataExamples namespace we created in Step 1.1.

    WinJS.Namespace.define("DataExamples",
        {
            groupInfo : groupInfo,
            myCellSpanningData: myCellSpanningData
        });
    
  4. Update your ListView to use your groupInfo function.

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

Step 3: Define the size of an item that spans a single cell

Now that we've defined our base cell size, we can define the sizes of our items. When we defined our data in the first step, we included a type field that contains info about how big the item can be: small, medium, or large. We can use that info to assign item sizes. The best way to assign the sizes is to use CSS classes. This approach works whether we use a templating function or a WinJS.Binding.Template.

Our base cell is 310 pixels wide and 80 pixels tall. The total size of each item must be a multiple of the base cell size. The base cell size is the size of the item plus the item's padding, margin, and border:

A cell in a ListView

Here's the formula for calculating the base cell size:

  • base cell width = item width + item horizontal padding + item horizontal margin + item border thickness
  • base cell height = item height + item vertical padding + item vertical margin + item border thickness

JJ657974.wedge(en-us,WIN.10).gifTo define the size of an item that occupies a single base cell

  1. Let's define the size for the smallest item. In your CSS file, create a Cascading Style Sheets (CSS) class named "smallListIconTextItem".

    .smallListIconTextItem
    {
    
    }     
    
  2. The smallest item takes up only one cell. Let's set the item's width to 300px, its height to 70px , and its padding to 5px.

    .smallListIconTextItem
    {
        width: 300px;
        height: 70px;
        padding: 5px;
        overflow: hidden;
        background-color: Pink;
        display: -ms-grid;
    }    
    

    Let's check those numbers against our formula to make sure they match our base cell size.

    • cell width = item width + left padding + right padding + border thickness + left margin + right margin = 300 + 5px + 5px + 0 + 0 + 0 = 310

    • cell height = item height + top padding + bottom padding + border thickness + top margin + bottom margin= 70px + 5px + 5px + 0 + 0 + 0= 80

    They do match our base cell size, so we're good to move on to the next step.

Step 4: Define the sizes of items that span 2 or more cells

When determining the size of an item that spans one or more cells, you must also take into account the win-container margin between the cells it spans. For example, if you have an item that spans one cell horizontally but two cells vertically, its total item size includes the win-container bottom margin of the first cell and the win-container top margin of the second cell, as shown here.

An item that spans two cells

Here's the formula for calculating the total item size of an item that spans multiple cells:

  • total item width = number of cells * base cell width + (number of cells - 1) * (win-container left margin + win-container right margin)

  • total item height = number of cells * base cell height + (number of cells - 1) * (win-container top margin + win-container bottom margin)

Tip  The win-container margin is 5 pixels by default.

 

JJ657974.wedge(en-us,WIN.10).gifTo define the size of an item that vertically spans two cells

  1. Use our formula to determine the total item height:

    total item height = number of cells * base cell height + (number of cells - 1) * (win-container top margin + win-container bottom margin) = 2 * 80 + (2-1) * (5 + 5) = 170

  2. Create the CSS style that specifies the size item. This example defines an item that has a height of 160 pixels and has 5 pixels of padding, so its total height is 160 + 5 + 5 = 170. Because the item spans only a single cell horizontally, give it the same width and padding as the CSS class we created in Step 3, smallListIconTextItem.

    .mediumListIconTextItem
    {
        width: 300px;
        height: 160px;
        padding: 5px;
        overflow: hidden;
        background-color: LightGreen;
        display: -ms-grid;
    }
    

JJ657974.wedge(en-us,WIN.10).gifTo define the size of an item that vertically spans three cells

  1. Use our formula to determine the total item height:

    total item height = number of cells * base cell height + (number of cells - 1) * (win-container top margin + win-container bottom margin) = 3 * 80 + (3-1) * (5 + 5) = 260

  2. Create the CSS style that specifies the size item. This example defines an item that has a height of 250 pixels and has 5 pixels of padding, so its total height is 250 + 5 + 5 = 260.

    .largeListIconTextItem
    {
        width: 300px;
        height: 250px;
        padding: 5px;
        overflow: hidden;
        background-color: LightBlue;
        display: -ms-grid;
    }
    

Step 5: Create the item-sizing function for the CellSpanningLayout

In addition to the groupInfo function, the CellSpanningLayout needs to expose an itemInfo function that determines how to size items of different 'types' in the data source. The itemInfo function needs to return a JavaScript object that contains the following properties:

  • width
    The width of the individual item in the ListView

  • height
    The height of the individual item in the ListView.

JJ657974.wedge(en-us,WIN.10).gifTo define the size of individual items in the ListView

  1. In the JavaScript file where you created your data, create a itemInfo function that turns retrieves an item from the data source and returns the corresponding size and height for that item.

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

    The itemInfo is wrapped by a call toWinJS.Utilities.markSupportedForProcessing to make the function accessible in HTML.

  2. Make your itemInfo function publicly accessible by exposing it through a namespace. This example updates the DataExamples namespace we created in Step 1.1.

    WinJS.Namespace.define("DataExamples",
        {
            myCellSpanningData: myCellSpanningData,
            groupInfo: groupInfo,
            itemInfo: itemInfo
    });
    
  3. Update your ListView to use your itemInfo function.

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

Step 6: Create the template

The final step is to create a template or templating function that uses the CSS classes that we just defined. We'll show you how to create both a WinJS.Binding.Template and a templating function.

JJ657974.wedge(en-us,WIN.10).gifOption A: Use a WinJS.Binding.Template

  1. In your HTML, define a 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>
    
  2. Remember how, when we defined our data in Step 1.1, we included a type property that specified which CSS class is assigned to each item? Now we can use that data. On the item's root element, bind the class name to the value of our data's type field.

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

    Note  The example binds to className, not class. That's because, even though you use "class" in HTML, the backing JavaScript property is named "className". When your app processes the data-win-bind attribute, it assigns the bound values via JavaScript calls.

    That means that whenever the HTML attribute name and the backing JavaScript property name are different, you use the JavaScript property name when you set data-win-bind.

     

  3. Update your ListView to use the template by setting its itemTemplate property to ID of your template.

    <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
    

If you want, you can use a templating function instead of a WinJS.Binding.Template. Using a templating function can give you more flexibility in how you generate your HTML and assign sizes.

JJ657974.wedge(en-us,WIN.10).gifOption B: Use a templating function

  1. In a JavaScript file, define your templating function. You can add this code to the same file that contains your data or you can add it to a different file. Just make sure that the page that contains your ListView references this file.

    This example uses the type data for each item to assign it the CSS class that determines its size.

    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;
        });
    };
    
  2. Call markSupportedForProcessing on the function so that it can be accessed through markup.

    WinJS.Utilities.markSupportedForProcessing(myCellSpanningJSTemplate);
    
  3. Use WinJS.Namespace.define to make the function publicly accessible.

    WinJS.Namespace.define("Templates",
        {
            myCellSpanningJSTemplate: myCellSpanningJSTemplate
        });
    
  4. In your HTML, update your ListView to use the templating function by setting its itemTemplate property to the name of your templating function.

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

Regardless of which templating approach you used, when you run the app, the ListView displays multi-sized items.

A ListView that contains multi-sized items

Remarks

Editing items

When you change items in a ListView that has cell spanning enabled, call ListView.recalculateItemPosition whenever you make a change.

ListView item templates sample