Tuesday, May 22, 2012

Metro: Grouping Items in a ListView Control

Metro: Grouping Items in a ListView Control:
The purpose of this blog entry is to explain how you can group list items when displaying the items in a WinJS ListView control. In particular, you learn how to group a list of products by product category.

image

Displaying a grouped list of items in a ListView control requires completing the following steps:

  1. Create a Grouped data source from a List data source
  2. Create a Grouped Header Template
  3. Declare the ListView control so it groups the list items

Creating the Grouped Data Source

Normally, you bind a ListView control to a WinJS.Binding.List object. If you want to render list items in groups, then you need to bind the ListView to a grouped data source instead.

The following code – contained in a file named products.js — illustrates how you can create a standard WinJS.Binding.List object from a JavaScript array and then return a grouped data source from the WinJS.Binding.List object by calling its createGrouped() method:

(function () {
    "use strict";

    // Create List data source
    var products = new WinJS.Binding.List([
            { name: "Milk", price: 2.44, category: "Beverages" },
            { name: "Oranges", price: 1.99, category: "Fruit" },
            { name: "Wine", price: 8.55, category: "Beverages" },
            { name: "Apples", price: 2.44, category: "Fruit" },
            { name: "Steak", price: 1.99, category: "Other" },
            { name: "Eggs", price: 2.44, category: "Other" },
            { name: "Mushrooms", price: 1.99, category: "Other" },
            { name: "Yogurt", price: 2.44, category: "Other" },
            { name: "Soup", price: 1.99, category: "Other" },
            { name: "Cereal", price: 2.44, category: "Other" },
            { name: "Pepsi", price: 1.99, category: "Beverages" }
        ]);

    // Create grouped data source
    var groupedProducts = products.createGrouped(
        function (dataItem) {
            return dataItem.category;
        },
        function (dataItem) {
            return { title: dataItem.category };
        },
        function (group1, group2) {
            return group1.charCodeAt(0) - group2.charCodeAt(0);
        }
    );

    // Expose the grouped data source
    WinJS.Namespace.define("ListViewDemos", {
        products: groupedProducts
    });
})();
Notice that the createGrouped() method requires three functions as arguments:
  1. groupKey – This function associates each list item with a group. The function accepts a data item and returns a key which represents a group. In the code above, we return the value of the category property for each product.
  2. groupData – This function returns the data item displayed by the group header template. For example, in the code above, the function returns a title for the group which is displayed in the group header template.
  3. groupSorter – This function determines the order in which the groups are displayed. The code above displays the groups in alphabetical order: Beverages, Fruit, Other.

Creating the Group Header Template

Whenever you create a ListView control, you need to create an item template which you use to control how each list item is rendered. When grouping items in a ListView control, you also need to create a group header template. The group header template is used to render the header for each group of list items.
Here’s the markup for both the item template and the group header template:

<div id="productTemplate" data-win-control="WinJS.Binding.Template">
    <div class="product">
        <span data-win-bind="innerText:name"></span>
        <span data-win-bind="innerText:price"></span>
    </div>
</div>

<div id="productGroupHeaderTemplate" data-win-control="WinJS.Binding.Template">
    <div class="productGroupHeader">
        <h1 data-win-bind="innerText: title"></h1>
    </div>
</div>
You should declare the two templates in the same file as you declare the ListView control – for example, the default.html file.

Declaring the ListView Control

The final step is to declare the ListView control. Here’s the required markup:

<div data-win-control="WinJS.UI.ListView"
    data-win-options="{
        itemDataSource:ListViewDemos.products.dataSource,
        itemTemplate:select('#productTemplate'),
        groupDataSource:ListViewDemos.products.groups.dataSource,
        groupHeaderTemplate:select('#productGroupHeaderTemplate'),
        layout: {type: WinJS.UI.GridLayout}
    }">
</div>
In the markup above, six properties of the ListView control are set when the control is declared. First the itemDataSource and itemTemplate are specified. Nothing new here.
Next, the group data source and group header template are specified. Notice that the group data source is represented by the ListViewDemos.products.groups.dataSource property of the grouped data source.
Finally, notice that the layout of the ListView is changed to Grid Layout. You are required to use Grid Layout (instead of the default List Layout) when displaying grouped items in a ListView.
Here’s the entire contents of the default.html page:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ListViewDemos</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ListViewDemos references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
    <script src="/js/products.js" type="text/javascript"></script>

    <style type="text/css">

        .product {
            width:  200px;
            height:  100px;
            border:  white solid 1px;
            font-size:  x-large;
        }
    </style>
</head>
<body>
    <div id="productTemplate" data-win-control="WinJS.Binding.Template">
        <div class="product">
            <span data-win-bind="innerText:name"></span>
            <span data-win-bind="innerText:price"></span>
        </div>
    </div>

    <div id="productGroupHeaderTemplate" data-win-control="WinJS.Binding.Template">
        <div class="productGroupHeader">
            <h1 data-win-bind="innerText: title"></h1>
        </div>
    </div>
    <div data-win-control="WinJS.UI.ListView"
        data-win-options="{
            itemDataSource:ListViewDemos.products.dataSource,
            itemTemplate:select('#productTemplate'),
            groupDataSource:ListViewDemos.products.groups.dataSource,
            groupHeaderTemplate:select('#productGroupHeaderTemplate'),
            layout: {type: WinJS.UI.GridLayout}
        }">
    </div>
</body>
</html>
Notice that the default.html page includes a reference to the products.js file:
<script src=”/js/products.js” type=”text/javascript”></script>
The default.html page also contains the declarations of the item template, group header template, and ListView control.

Summary

The goal of this blog entry was to explain how you can group items in a ListView control. You learned how to create a grouped data source, a group header template, and declare a ListView so that it groups its list items.

    Metro: Introduction to the WinJS ListView Control

    Metro: Introduction to the WinJS ListView Control:
    The goal of this blog entry is to provide a quick introduction to the ListView control – just the bare minimum that you need to know to start using the control. When building Metro style applications using JavaScript, the ListView control is the primary control that you use for displaying lists of items. For example, if you are building a product catalog app, then you can use the ListView control to display the list of products.

    The ListView control supports several advanced features that I plan to discuss in future blog entries. For example, you can group the items in a ListView, you can create master/details views with a ListView, and you can efficiently work with large sets of items with a ListView. In this blog entry, we’ll keep things simple and focus on displaying a list of products.

    image

    There are three things that you need to do in order to display a list of items with a ListView:

    1. Create a data source
    2. Create an Item Template
    3. Declare the ListView

    Creating the ListView Data Source

    The first step is to create (or retrieve) the data that you want to display with the ListView. In most scenarios, you will want to bind a ListView to a WinJS.Binding.List object.

    The nice thing about the WinJS.Binding.List object is that it enables you to take a standard JavaScript array and convert the array into something that can be bound to the ListView. It doesn’t matter where the JavaScript array comes from. It could be a static array that you declare or you could retrieve the array as the result of an Ajax call to a remote server.

    The following JavaScript file – named products.js – contains a list of products which can be bound to a ListView.

    (function () {
        "use strict";
    
        var products = new WinJS.Binding.List([
                { name: "Milk", price: 2.44 },
                { name: "Oranges", price: 1.99 },
                { name: "Wine", price: 8.55 },
                { name: "Apples", price: 2.44 },
                { name: "Steak", price: 1.99 },
                { name: "Eggs", price: 2.44 },
                { name: "Mushrooms", price: 1.99 },
                { name: "Yogurt", price: 2.44 },
                { name: "Soup", price: 1.99 },
                { name: "Cereal", price: 2.44 },
                { name: "Pepsi", price: 1.99 }
            ]);
    
        WinJS.Namespace.define("ListViewDemos", {
            products: products
        });
    
    })();
    
    The products variable represents a WinJS.Binding.List object. This object is initialized with a plain-old JavaScript array which represents an array of products.
    To avoid polluting the global namespace, the code above uses the module pattern and exposes the products using a namespace. The list of products is exposed to the world as ListViewDemos.products.
    To learn more about the module pattern and namespaces in WinJS, see my earlier blog entry:
    http://stephenwalther.com/blog/archive/2012/02/22/metro-namespaces-and-modules.aspx

    Creating the ListView Item Template

    The ListView control does not know how to render anything. It doesn’t know how you want each list item to appear. To get the ListView control to render something useful, you must create an Item Template.
    Here’s what our template for rendering an individual product looks like:

    <div id="productTemplate" data-win-control="WinJS.Binding.Template">
        <div class="product">
            <span data-win-bind="innerText:name"></span>
            <span data-win-bind="innerText:price"></span>
        </div>
    </div>
    
    This template displays the product name and price from the data source.
    image
    Normally, you will declare your template in the same file as you declare the ListView control. In our case, both the template and ListView are declared in the default.html file.
    To learn more about templates, see my earlier blog entry:
    http://stephenwalther.com/blog/archive/2012/02/27/metro-using-templates.aspx

    Declaring the ListView

    The final step is to declare the ListView control in a page. Here’s the markup for declaring a ListView:

    <div data-win-control="WinJS.UI.ListView"
         data-win-options="{
            itemDataSource:ListViewDemos.products.dataSource,
            itemTemplate:select('#productTemplate')
         }">
    </div>
    
    You declare a ListView by adding the data-win-control to an HTML DIV tag. The data-win-options attribute is used to set two properties of the ListView.
    The ListView is associated with its data source with the itemDataSource property. Notice that the data source is ListViewDemos.products.dataSource and not just ListViewDemos.products. You need to associate the ListView with the dataSoure property.
    The ListView is associated with its item template with the help of the itemTemplate property. The ID of the item template — #productTemplate – is used to select the template from the page.
    Here’s what the complete version of the default.html page looks like:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>ListViewDemos</title>
    
        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
        <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
        <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>
    
        <!-- ListViewDemos references -->
        <link href="/css/default.css" rel="stylesheet">
        <script src="/js/default.js"></script>
        <script src="/js/products.js" type="text/javascript"></script>
    
        <style type="text/css">
    
            .product {
                width:  200px;
                height:  100px;
                border:  white solid 1px;
            }
        </style>
    
    </head>
    <body>
        <div id="productTemplate" data-win-control="WinJS.Binding.Template">
            <div class="product">
                <span data-win-bind="innerText:name"></span>
                <span data-win-bind="innerText:price"></span>
            </div>
        </div>
    
        <div data-win-control="WinJS.UI.ListView"
                data-win-options="{
                itemDataSource:ListViewDemos.products.dataSource,
                itemTemplate:select('#productTemplate')
                }">
        </div>
    
    </body>
    </html>
    
    Notice that the page above includes a reference to the products.js file:
    <script src=”/js/products.js” type=”text/javascript”></script>
    The page above also contains a Template control which contains the ListView item template. Finally, the page includes the declaration of the ListView control.

    Summary

    The goal of this blog entry was to describe the minimal set of steps which you must complete to use the WinJS ListView control to display a simple list of items. You learned how to create a data source, declare an item template, and declare a ListView control.

    Could not find a part of the path ... bin\roslyn\csc.exe

    I am trying to run an ASP.NET MVC (model-view-controller) project retrieved from TFS (Team Foundation Server) source control. I have added a...