ListView Binding With Grouping and Semantic Zoom in Windows 8 JavaScript Metro Application


Windows 8 CP and VS11 are available. Recently I wrote Setting up Visual Studio11 Beta for Metro Application Creation. In this article we do some development.

In this article we will see:

  1. Creating DataSource
  2. ListView Binding to DataSource
  3. Creating ItemTemplate
  4. Grouping Data Source
  5. Using SemanticZoomView over ListView

At the end of the article we will have output like below:

1.jpg1.1.jpg

We are going to display a list of movies in a ListView. The movies will be grouped with the first letter of their title. Later in the article we will apply SemanticZoomView on the ListView. In the above image you can see Semantic Zoom View. To switch between SemanticZoomView and detail view either you can tap on the screen or press CTRL + Arrow key on the keyboard.

Create DataSource

The very first thing we need to do is to create a DataSource. To create a DataSource, right-click on the JS folder and add a new JavaScript file. Let us call this newly added JavaScript file moviedata.js

2.jpg

Inside the newly created file, we need to create an anonymous JavaScript function and define a JavaScript array. This array contains local data to be bound to the ListView.

MovieArray contains two properties:

  1. Title to display name of the movie
  2. Picture to display thumbnail of the movie.

All the movie images are inside the images folder in the project. Let us create an array with sample data. The array is given below:

var movieArray = [
{ title:
"The Artist", picture: "images/TheArtist.jpg" },
{ title:
"A Better Life", picture: "images/abetterlife.jpg" },
{ title:
"Abduction", picture: "images/abduction.jpg" },
{ title:
"African Cats", picture: "images/africancats.jpg" },
{ title:
"Angel Crest", picture: "images/angelscrest.jpg" },
{ title:
"Arthur", picture: "images/arthur.jpg" },
{ title:
"Anonymous", picture: "images/anonymous.jpg" },
{ title:
"A Dangerous Method", picture: "images/adangerousmethod.jpg" },
{ title:
"A Good Old Fashioned Orgy ", picture: "images/agoodoldfashionedorgy.jpg" },
{ title:
"A Seperation ", picture: "images/aseparation.jpg" },
{ title:
"Another Earth ", picture: "images/anotherearth.jpg" },
{ title:
"A Heartbeat Away ", picture: "images/aheartbeataway.jpg" },
{ title:
"Bad Teacher ", picture: "images/badteacher.jpg" },
{ title:
"Begineers ", picture: "images/beginners.jpg" },
{ title:
"Brotherhood ", picture: "images/brotherhood.jpg" },
{ title:
"Bridesmaids ", picture: "images/bridesmaids.jpg" },
{ title:
"Born To Be Wild ", picture: "images/borntobewild.jpg" },
{ title:
"Blackthorn ", picture: "images/blackthorn.jpg" },
{ title:
"Bellflower ", picture: "images/bellflower.jpg" },
{ title:
"Butter ", picture: "images/butter.jpg" },
{ title:
"Bunraku ", picture: "images/bunraku.jpg" },
{ title:
"Cars 2 ", picture: "images/cars2.jpg" },
{ title:
"Cost Of A Soul", picture: "images/costofasoul.jpg" },
{ title:
"Carnage ", picture: "images/carnage.jpg" },
{ title:
"Crazy Stupid Love ", picture: "images/crazystupidlove.jpg" },
{ title:
"Cracks ", picture: "images/cracks.jpg" },
{ title:
"Colombiana ", picture: "images/colombiana.jpg" },
{ title:
"Cedar Rapids ", picture: "images/cedarrapids.jpg" },
{ title:
"Captain America ", picture: "images/captainamerica.jpg" },
];


Next we need to convert the JavaScript array into a list to be bound to the ListView.

3.jpg

Now to make this data publicly available we need to make a namespace and on the HTML page add a reference of this namespace to use the data from this file. The namespace can be created as below:

4.jpg

There are a few points worth discussing about creating the namespace:

  1. MovieData.itemList is the public member
  2. MovieData.itemList.datasource will be the datasource

At this point we have created the DataSource. And putting all the pieces of code together, the moviedata.js file will be as below:

Moviedata.js

(function () {

"use strict";
// http://www.allmoviephoto.com/photo/index_2011.html
var movieArray = [
{ title:
"The Artist", picture: "images/TheArtist.jpg" ,
{ title:
"A Better Life", picture: "images/abetterlife.jpg" },
{ title:
"Abduction", picture: "images/abduction.jpg" },
{ title:
"African Cats", picture: "images/africancats.jpg" },
{ title:
"Angel Crest", picture: "images/angelscrest.jpg" },
{ title:
"Arthur", picture: "images/arthur.jpg" },
{ title:
"Anonymous", picture: "images/anonymous.jpg" },
{ title:
"A Dangerous Method", picture: "images/adangerousmethod.jpg" },
{ title:
"A Good Old Fashioned Orgy ", picture: "images/agoodoldfashionedorgy.jpg" },
{ title:
"A Seperation ", picture: "images/aseparation.jpg" },
{ title:
"Another Earth ", picture: "images/anotherearth.jpg" },
{ title:
"A Heartbeat Away ", picture: "images/aheartbeataway.jpg" },
{ title:
"Bad Teacher ", picture: "images/badteacher.jpg" },
{ title:
"Begineers ", picture: "images/beginners.jpg" },
{ title:
"Brotherhood ", picture: "images/brotherhood.jpg" },
{ title:
"Bridesmaids ", picture: "images/bridesmaids.jpg" },
{ title:
"Born To Be Wild ", picture: "images/borntobewild.jpg" },
{ title:
"Blackthorn ", picture: "images/blackthorn.jpg" },
{ title:
"Bellflower ", picture: "images/bellflower.jpg" },
{ title:
"Butter ", picture: "images/butter.jpg" },
{ title:
"Bunraku ", picture: "images/bunraku.jpg" },
{ title:
"Cars 2 ", picture: "images/cars2.jpg" },
{ title:
"Cost Of A Soul", picture: "images/costofasoul.jpg" },
{ title:
"Carnage ", picture: "images/carnage.jpg" },
{ title:
"Crazy Stupid Love ", picture: "images/crazystupidlove.jpg" },
{ title:
"Cracks ", picture: "images/cracks.jpg" },
{ title:
"Colombiana ", picture: "images/colombiana.jpg" },
{ title:
"Cedar Rapids ", picture: "images/cedarrapids.jpg" },
{ title:
"Captain America ", picture: "images/captainamerica.jpg" },
];


var dataList = new WinJS.Binding.List(movieArray);

Creating ListView

We have created a data source and now let us go ahead and create a ListView. In the ListView we need to set the itemDataSource to MovieData.itemList.dataSource.

5.jpg

If you remember, while creating a data source we have created a public namespace with the name MovieData. In the MovieData.js file we need to add a reference as below:

6.jpg

At this point if we run the application; we will get output as below. Essentially the following is the same array we get as output when we created the data source.

7.jpg

Now we need to create a template to render the movie image and the name of the movie. An ItemTemplate for the ListView can be created as below:

8.jpg

Inside the ItemTemplate there are two controls:

  1. Image control to render the thumbnail of the movie
  2. Inside the div rendering the title of the movie

Now go ahead and set the itemTemplate of the ListView as below.

9.jpg

At this point if we run the application we should get the ListView as below.

10.jpg

Notice that WinJS has automatically added a scrollbar. At this point we should have a HTML file as below:

Default.html

<!DOCTYPE html>
<
html>
<
head>
<meta charset="utf-8">
<title>ListViewBinding</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>

<!-- ListViewBinding references -->
<link href="/css/default.css" rel="stylesheet">
<script src="/js/default.js"></script>
<script src="/js/moviedata.js"></script>

</
head>
<
body>

<div id="headerTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div class="simpleHeaderItem">
<h1 data-win-bind="innerText: title"></h1>
</div>
</
div>
<div id="moviesTemplate"
data-win-control="WinJS.Binding.Template">

<div style="width: 150px; height: 130px;">
<img src="#" style="width: 100px; height: 100px;"
data-win-bind="alt: title; src: picture" />
<div>
<h4 data-win-bind="innerText:title"></h4>
</div>
</div>

</div>

<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource :MovieData.itemList.dataSource,
itemTemplate:moviesTemplate,
layout: {type: WinJS.UI.GridLayout} }">
</div>
</
body>
</
html>

Customizing ListView

If we see the ListView, it is not that immersive. We can customize a ListView by overriding the CSS. Each HTML file has a corresponding CSS file associated withit. For example for the default.html there is a CSS file default.css. Now let us go ahead and override the ListView properties. In the following CSS:

  1. ListView height and width is set to 600 pixels and 800 pixels respectively
  2. ListView border is set to 2 Pixel
  3. Margin of each item in ListView is set to 10 Pixel
  4. On hover, each item's color will be changed to Red

11.jpg

We need to put the above CSS in default.css. After that when we run the application, we will get the ListView as below. Notice that on mouse hover the color changes to Red.

12.jpg

Grouping of ListView Items

To group the items in ListView, we need to call CreateGrouped JavaScript function from base.js. This function is defined as below.

13.jpg

We can see that it takes three parameters. An explanation of the parameters are given below.

  • groupKey : A function that accepts a single argument. The function is called with each element in the list; the function should return a string representing the group containing the element.
  • groupData : A function that accepts a single argument. The function is called on an element in the list for each group. It should return the value that should be set as the data of the .groups list element for this group.
  • groupSorter : A function that accepts two arguments. The function is called with the key of groups found in the list. It must return one of the following numeric values: negative if the first argument is less than the second, zero if the two arguments are equivalent, positive if the first argument is greater than the second. If omitted, the groups are sorted in ascending, ASCII character order.

As you see, the createGrouped function takes three functions as input parameters. So let us create these functions one by one.

The following function will compare the groups. This function will be groupSorter.

15.jpg

This function will return the group of the item.

16.jpg

The following function will return the Title of the group. In this case items would be grouped with the first letter.

17.jpg

In the last step we need to call the above three functions to create a group and make a public namespace for this; that is, it is accessible from other files.

18.jpg

By putting all codes together in the mymoviedata.js file you will have two public namespaces. You need to set MovieGroupedData as the datasource of the ListView to create grouped data.

MovieGroupedData.js

(function () {

"use strict";

// http://www.allmoviephoto.com/photo/index_2011.html

var movieArray = [
{ title:
"The Artist", picture: "images/TheArtist.jpg" },
{ title:
"A Better Life", picture: "images/abetterlife.jpg" },
{ title:
"Abduction", picture: "images/abduction.jpg" },
{ title:
"African Cats", picture: "images/africancats.jpg" },
{ title:
"Angel Crest", picture: "images/angelscrest.jpg" },
{ title:
"Arthur", picture: "images/arthur.jpg" },
{ title:
"Anonymous", picture: "images/anonymous.jpg" },
{ title:
"A Dangerous Method", picture: "images/adangerousmethod.jpg" },
{ title:
"A Good Old Fashioned Orgy ", picture: "images/agoodoldfashionedorgy.jpg" },
{ title:
"A Seperation ", picture: "images/aseparation.jpg" },
{ title:
"Another Earth ", picture: "images/anotherearth.jpg" },
{ title:
"A Heartbeat Away ", picture: "images/aheartbeataway.jpg" },
{ title:
"Bad Teacher ", picture: "images/badteacher.jpg" },
{ title:
"Begineers ", picture: "images/beginners.jpg" },
{ title:
"Brotherhood ", picture: "images/brotherhood.jpg" },
{ title:
"Bridesmaids ", picture: "images/bridesmaids.jpg" },
{ title:
"Born To Be Wild ", picture: "images/borntobewild.jpg" },
{ title:
"Blackthorn ", picture: "images/blackthorn.jpg" },
{ title:
"Bellflower ", picture: "images/bellflower.jpg" },
{ title:
"Butter ", picture: "images/butter.jpg" },
{ title:
"Bunraku ", picture: "images/bunraku.jpg" },
{ title:
"Cars 2 ", picture: "images/cars2.jpg" },
{ title:
"Cost Of A Soul", picture: "images/costofasoul.jpg" },
{ title:
"Carnage ", picture: "images/carnage.jpg" },
{ title:
"Crazy Stupid Love ", picture: "images/crazystupidlove.jpg" },
{ title:
"Cracks ", picture: "images/cracks.jpg" },
{ title:
"Colombiana ", picture: "images/colombiana.jpg" },
{ title:
"Cedar Rapids ", picture: "images/cedarrapids.jpg" },
{ title:
"Captain America ", picture: "images/captainamerica.jpg" },
];

var dataList = new WinJS.Binding.List(movieArray);

function compareGroups(left, right) {
return left.charCodeAt(0) - right.charCodeAt(0);
}

function getGroupKey(dataItem) {
return dataItem.title.toUpperCase().charAt(0);
}
function getGroupData(dataItem) {
return {
title: dataItem.title.toUpperCase().charAt(0)
};
}

var publicMembers =
{
itemList: dataList
};
WinJS.Namespace.define(
"MovieData", publicMembers);

var groupedItemsList = dataList.createGrouped(getGroupKey, getGroupData, compareGroups);
WinJS.Namespace.define(
"MovieGroupedData",
{
groupedItemsList: groupedItemsList
});
})();

At this point we have created functions to group data. Next we need to modify the ListView datasource to group ListView items. For that let us create a header template. Header template will display the Title of the group. The Header Template can be created in the same way of the Item Template. The Header template is given below:

19.jpg

And we have already created the Item Template as in the following:

20.jpg

To group items in the ListView we need to create a Header Template and Item Template. We have created them in the previous step. Next we need to set the datasource of the ListView to bind the grouped items. The ListView can be created as following for the grouped items:

21.jpg

If we put all the code together then default.html will have the following code:

Default.html

<!DOCTYPE html>
<
html>
<
head>
<meta charset="utf-8">
<title>ListViewBinding</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>

<!-- ListViewBinding references -->

<link href="/css/default.css" rel="stylesheet">
<script src="/js/default.js"></script>
<script src="/js/moviedata.js"></script>
</
head>
<
body>

<div id="headerTemplate"
data-win-control="WinJS.Binding.Template"
style="display: none">
<div class="simpleHeaderItem">
<h1 data-win-bind="innerText: title"></h1>
</div>
</div>

<div id="moviesTemplate"
data-win-control="WinJS.Binding.Template">
<div style="width: 150px; height: 130px;">
<img src="#" style="width: 100px; height: 100px;"
data-win-bind="alt: title; src: picture" />
<div>
<h4 data-win-bind="innerText:title"></h4>
</div>
</div>

</div>

<!--<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource :MovieData.itemList.dataSource,
itemTemplate:moviesTemplate,
layout: {type: WinJS.UI.GridLayout} }">
</div> -->

<h1>Rate Movies Application</h1>

<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : MovieGroupedData.groupedItemsList.dataSource,
groupDataSource: MovieGroupedData.groupedItemsList.groups.dataSource,
itemTemplate: moviesTemplate,
groupHeaderTemplate: headerTemplate,
layout: {type: WinJS.UI.GridLayout} }">

</div>
</
body>
</
html>

Let us go ahead and run the application. We should be getting the output as in the following:

22.jpg

We have grouped items in the ListView. Next let us add a Semantic View. To add a Semantic View you need to put the ListView inside a Semantic View control. In Semantic Zoom we will have two views of the same ListView. We need to create a view for the zoom out. For zoom out the Template can be created as given below:

23.jpg

After creation of the Template let us go ahead and create a ListView for the Zoom Out view.

24.jpg

The ItemDataSource of the zoomed ListView is the same as of the zoomed in ListView. Now we need to put both the ListView , Zoomed In and Zoomed out in the Semantic Zoom control. We can put the ListView controls in the Semantic Zoom control as below:

25.jpg

Now we should have the default.html as the following:

Default.html

<!DOCTYPE html>
<
html>
<
head>
<meta charset="utf-8">
<title>ListViewBinding</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>

<!-- ListViewBinding references -->

<link href="/css/default.css" rel="stylesheet">
<script src="/js/default.js"></script>
<script src="/js/moviedata.js"></script>
</
head>
<
body>

<div id="headerTemplate"
data-win-control="WinJS.Binding.Template"
style="display: none">
<div class="simpleHeaderItem">
<h1 data-win-bind="innerText: title"></h1>
</div>
</div>

<div id="semanticZoomTemplate"

data-win-control="WinJS.Binding.Template"
style="display: none">
<div class="semanticZoomItem">
<h1 class="semanticZoomItem-Text"
data-win-bind="innerText: title"></h1>
</div>
</div>

<div id="moviesTemplate"
data-win-control="WinJS.Binding.Template">
<div style="width: 150px; height: 130px;">
<img src="#" style="width: 100px; height: 100px;"
data-win-bind="alt: title; src: picture" />
<div class="a">
<h4 data-win-bind="innerText:title"></h4>
</div>
</div>
</div>

<!--<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource :MovieData.itemList.dataSource,
itemTemplate:moviesTemplate,
layout: {type: WinJS.UI.GridLayout} }">
</div> -->

<h1>Rate Movies Application</h1>

<!--<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : MovieGroupedData.groupedItemsList.dataSource,
groupDataSource: MovieGroupedData.groupedItemsList.groups.dataSource,
itemTemplate: moviesTemplate,
groupHeaderTemplate: headerTemplate,
layout: {type: WinJS.UI.GridLayout} }">

</div>-->

<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">
<div id="movieListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : MovieGroupedData.groupedItemsList.dataSource,
groupDataSource: MovieGroupedData.groupedItemsList.groups.dataSource,
itemTemplate: moviesTemplate,
groupHeaderTemplate: headerTemplate,
layout: {type: WinJS.UI.GridLayout} }">

</div>
<div id="zoomedOutListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource: MovieGroupedData.groupedItemsList.groups.dataSource,
itemTemplate: semanticZoomTemplate,
selectionMode: 'none',
tapBehavior: 'invoke',
swipeBehavior: 'none' }">

</div>
</div>
</
body>
</
html>


Next we need to add some CSS to make it more immersive. In default.css, we need to add the following CSS:

.win-listview

{
height: 600px;
width: 800px;
border: 2px solid gray;
}

.win-listview .win-container {
margin: 10px;
}
.win-listview .win-container:hover {
background-color: red;
border-color: red;
}

.semanticZoomItem

{
width: 130px;
height: 130px;
background-color: rgba(38, 160, 218, 1.0);
}

.semanticZoomItem .semanticZoomItem-Text

{

padding: 10px;
line-height: 150px;
white-space: nowrap;
color: white;
}

On running we should be getting the following output:

26.jpg

On tapping or pressing of the Ctrl and arrow key you should be getting the semantic zoom view. The Semantic Zoom View of the ListView is as in the following image:

27.jpg

In this way we can work ListView and SemanticZoom WinJS controls. In a future article we will try to pull data from a service. I hope this article is useful. Thanks for reading.

Up Next
    Ebook Download
    View all
    Learn
    View all