Metro: Creating a Master/Detail View with a WinJS ListView Control

Posted by Stephen.Walther on Stephen Walter See other posts from Stephen Walter or by Stephen.Walther
Published on Mon, 21 May 2012 15:12:09 +0000 Indexed on 2012/06/07 4:46 UTC
Read the original article Hit count: 460

Filed under:

The goal of this blog entry is to explain how you can create a simple master/detail view by using the WinJS ListView and Template controls. In particular, I explain how you can use a ListView control to display a list of movies and how you can use a Template control to display the details of the selected movie.

clip_image002

Creating a master/detail view requires completing the following four steps:

  1. Create the data source – The data source contains the list of movies.
  2. Declare the ListView control – The ListView control displays the entire list of movies. It is the master part of the master/detail view.
  3. Declare the Details Template control – The Details Template control displays the details for the selected movie. It is the details part of the master/detail view.
  4. Handle the selectionchanged event – You handle the selectionchanged event to display the details for a movie when a new movie is selected.

Creating the Data Source

There is nothing special about our data source. We initialize a WinJS.Binding.List object to represent a list of movies:

(function () {
    "use strict";

    var movies = new WinJS.Binding.List([
        { title: "Star Wars", director: "Lucas"},
        { title: "Shrek", director: "Adamson" },
        { title: "Star Trek", director: "Abrams" },
        { title: "Spiderman", director: "Raimi" },
        { title: "Memento", director: "Nolan" },
        { title: "Minority Report", director: "Spielberg" }
    ]);

    // Expose the data source
    WinJS.Namespace.define("ListViewDemos", {
        movies: movies
    });

})();

The data source is exposed to the rest of our application with the name ListViewDemos.movies.

Declaring the ListView Control

The ListView control is declared with the following markup:

<div id="movieList"
    data-win-control="WinJS.UI.ListView"
    data-win-options="{
        itemDataSource: ListViewDemos.movies.dataSource,
        itemTemplate: select('#masterItemTemplate'),
        tapBehavior: 'directSelect',
        selectionMode: 'single',
        layout: { type: WinJS.UI.ListLayout }
    }">
</div>

The data-win-options attribute is used to set the following properties of the ListView control:

  • itemDataSource – The ListView is bound to the list of movies which we created in the previous section. Notice that the ListView is bound to ListViewDemos.movies.dataSource and not just ListViewDemos.movies.
  • itemTemplate – The item template contains the template used for rendering each item in the ListView. The markup for this template is included below.
  • tabBehavior – This enumeration determines what happens when you tap or click on an item in the ListView. The possible values are directSelect, toggleSelect, invokeOnly, none. Because we want to handle the selectionchanged event, we set tapBehavior to the value directSelect.
  • selectionMode – This enumeration determines whether you can select multiple items or only a single item. The possible values are none, single, multi. In the code above, this property is set to the value single.
  • layout – You can use ListLayout or GridLayout with a ListView. If you want to display a vertical ListView, then you should select ListLayout.

You must associate a ListView with an item template if you want to render anything interesting. The ListView above is associated with an item template named #masterItemTemplate. Here’s the markup for the masterItemTemplate:

<div id="masterItemTemplate" data-win-control="WinJS.Binding.Template">
    <div class="movie">
        <span data-win-bind="innerText:title"></span>
    </div>
</div>

This template simply renders the title of each movie.

Declaring the Details Template Control

The details part of the master/detail view is created with the help of a Template control. Here’s the markup used to declare the Details Template control:

<div id="detailsTemplate" data-win-control="WinJS.Binding.Template">
    <div>
        <div>
            Title:
            <span data-win-bind="innerText:title"></span>
        </div>
        <div>
            Director:
            <span data-win-bind="innerText:director"></span>
        </div>
    </div>
</div>

The Details Template control displays the movie title and director.

 image

Handling the selectionchanged Event

The ListView control can raise two types of events: the iteminvoked and selectionchanged events. The iteminvoked event is raised when you click on a ListView item. The selectionchanged event is raised when one or more ListView items are selected.

When you set the tapBehavior property of the ListView control to the value “directSelect” then tapping or clicking a list item raised both the iteminvoked and selectionchanged event. Tapping a list item causes the item to be selected and the item appears with a checkmark.

In our code, we handle the selectionchanged event to update the movie details Template when you select a new movie.

Here’s the code from the default.js file used to handle the selectionchanged event:

var movieList = document.getElementById("movieList");
var detailsTemplate = document.getElementById("detailsTemplate");
var movieDetails = document.getElementById("movieDetails");

// Setup selectionchanged handler
movieList.winControl.addEventListener("selectionchanged", function (evt) {
    if (movieList.winControl.selection.count() > 0) {
        movieList.winControl.selection.getItems().then(function (items) {
            // Clear the template container
            movieDetails.innerHTML = "";
            // Render the template
            detailsTemplate.winControl.render(items[0].data, movieDetails);
        });
    }
});

The code above sets up an event handler (listener) for the selectionchanged event. The event handler first verifies that an item has been selected in the ListView (selection.count() > 0). Next, the details for the movie are rendered using the movie details Template (we created this Template in the previous section).

The Complete Code

For the sake of completeness, I’ve included the complete code for the master/detail view below. I’ve included both the default.html, default.js, and movies.js files.

Here is the final code for the default.html file:

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

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

    <style type="text/css">

        body {
            font-size:  xx-large;
        }

        .movie {
            padding: 5px;
        }

        #masterDetail {
            display: -ms-box;
        }

        #movieList {
            width:  300px;
            margin:  20px;
        }

        #movieDetails {
            margin:  20px;
        }

    </style>

</head>
<body>
    <!-- Templates -->
    <div id="masterItemTemplate" data-win-control="WinJS.Binding.Template">
        <div class="movie">
            <span data-win-bind="innerText:title"></span>
        </div>
    </div>

    <div id="detailsTemplate" data-win-control="WinJS.Binding.Template">
        <div>
            <div>
                Title:
                <span data-win-bind="innerText:title"></span>
            </div>
            <div>
                Director:
                <span data-win-bind="innerText:director"></span>
            </div>
        </div>
    </div>

    <!-- Master/Detail -->
    <div id="masterDetail">
        <!-- Master -->
        <div id="movieList"
            data-win-control="WinJS.UI.ListView"
            data-win-options="{
                itemDataSource: ListViewDemos.movies.dataSource,
                itemTemplate: select('#masterItemTemplate'),
                tapBehavior: 'directSelect',
                selectionMode: 'single',
                layout: { type: WinJS.UI.ListLayout }
            }">
        </div>

        <!-- Detail -->
        <div id="movieDetails"></div>
    </div>

</body>
</html>

Here is the default.js file:

(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            WinJS.UI.processAll();

            var movieList = document.getElementById("movieList");
            var detailsTemplate = document.getElementById("detailsTemplate");
            var movieDetails = document.getElementById("movieDetails");

            // Setup selectionchanged handler
            movieList.winControl.addEventListener("selectionchanged", function (evt) {
                if (movieList.winControl.selection.count() > 0) {
                    movieList.winControl.selection.getItems().then(function (items) {
                        // Clear the template container
                        movieDetails.innerHTML = "";
                        // Render the template
                        detailsTemplate.winControl.render(items[0].data, movieDetails);
                    });
                }
            });

        }
    };

    app.start();
})();

 

Here is the movies.js file:

(function () {
    "use strict";

    var movies = new WinJS.Binding.List([
        { title: "Star Wars", director: "Lucas"},
        { title: "Shrek", director: "Adamson" },
        { title: "Star Trek", director: "Abrams" },
        { title: "Spiderman", director: "Raimi" },
        { title: "Memento", director: "Nolan" },
        { title: "Minority Report", director: "Spielberg" }
    ]);

    // Expose the data source
    WinJS.Namespace.define("ListViewDemos", {
        movies: movies
    });

})();

 

Summary

The purpose of this blog entry was to describe how to create a simple master/detail view by taking advantage of the WinJS ListView control. We handled the selectionchanged event of the ListView control to display movie details when you select a movie in the ListView.

© Stephen Walter or respective owner

Related posts about metro