WinJS appbar – tabbed control with listview

Hi,

As the title for the post suggest I am going through building a sample for WinJS appbar that acts as a tabbed control. Typically what I am going to do here is similar to what the default Weather app or News app has in app bar.

So here is the overall scenario
1. Create a app bar with three buttons (One each for “Birds”, “Animals” and “Reptiles). When the app bar opens it will show these three buttons.
2. On clicking any button, the app bar will extended showing a list of items. The items will be a collection of names for the button selected. For example if “Birds” is selected, the list will show “Ostriches”, “Penguins” etc. , if “Animals” is selected then the list would show “Tiger”, “Lion” etc. and so on.
3. On clicking the current button again, the list should hide itself.
4. On hide of app bar the app bar should go to default state, i.e. all items should be collapsed.

Simple requirement. Lets start 🙂

Since we are only demonstrating the app bar, I would create a new project (Blank application). In Visual Studio –> New Project –> JavaScript –> Windows Store Apps –> Blank App. Give it some name.

A project gets created with a default.html file. I usually prefer the light shade. So I would change the reference to ping to ui-light.css.

<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />

We need to have some data for the collections. So we will add a javascript file under js folder. I have named it data.js (You can name it otherwise also). First we would add the link in default.html to this new created file.

<script src="/js/data.js"></script>

In the data.js we will declare a namespace for the App bar data. In this sample I use static sample data. You may choose to get that from any service and create the data.
For this sample I need three lists 9one each for three categories. I also have a method for populating the data. I declare all of them in this namespace. this helps me to call the Properties or methods from anywhere in the app.

In below code :

      BirdData : Property for Binding List containing list of Bird names.

 

      AnimalData: Property for Binding List containing list of Animal names.

 

      ReptileData: Property for Binding List containing list of Reptile names.

 

    CreateAppBarData : Method for creating the initial data and assigning to above properties.
WinJS.Namespace.define("AppBarData",
        {
            BirdData: null,
            AnimalData: null,
            ReptileData: null,
            CreateAppBarData: initializeData
        });

Now we will have the implementation for the “initializeData” method. As I mentioned I use static data. So it is a simple implementation for me.

function initializeData() {
        var birdsdata = [{ title: "Ostriches" }, { title: "Kiwis" }, { title: "Penguins"}, { title: "Parrots"}, { title: "Storks"}];
        var animaldata = [{ title: "Tiger"}, { title: "Lion"}, { title: "Giraffe"}, { title: "Whale"}, { title: "Penguins"}];
        var reptilesData = [{ title: "Aligators"}, { title: "Crocodiles"}, { title: "Turtle"}, { title: "Lizard"}, { title: "Snakes"}];

        AppBarData.AnimalData = new WinJS.Binding.List(animaldata);
        AppBarData.BirdData = new WinJS.Binding.List(birdsdata);
        AppBarData.ReptileData = new WinJS.Binding.List(reptilesData);
    }

We are done with data.js. We have created the properties and methods for creating the data for our app bar.

Now lets go and complete the HTML. For the app bar to be two layered we need to divide it into two rows. First row would contain the parent buttons (One each for “Birds”, “Animals” and “Reptiles”) and for the second row we would use a control. Things to watch out here is that the first row would be always be visible and second row would hide and show based on the toggle state of the category in first row. We will handle most of them in CSS.

In HTML we would add a div for app bar and make it align to top. In the app bar we would add three divs (one for each category) and add a listview also.

For each category button we would have a for the title and and two divs (one for up arrow and other for down arrow).

<div class="appBarSample" data-win-control="WinJS.UI.AppBar" aria-label="Navigation Bar"
        data-win-options="{layout:'custom',placement:'top'}">
        <header class="navbar">
            <div class="itembirds item">
                <h3 class="overlay">Birds</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="itemanimals item">
                <h3 class="overlay">Animals</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="itemreptiles item">
                <h3 class="overlay">Reptiles</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="navbarlist hidden win-selectionstylefilled" data-win-control="WinJS.UI.ListView"
                data-win-options="{ selectionMode: 'none' }"></div>
        </header>
    </div>

We also need to add a template for the list view item. It is a simple template just displaying the title.

<div class="itemtemplatelist" data-win-control="WinJS.Binding.Template">
        <div class="item">
           <h4 class="title" data-win-bind="textContent: title"></h4>
        </div>
    </div>

We are done with the HTML. Now lets turn to the CSS. where bulk of the work is handled.
In CSS we will first define the layout for the appbar. As described we would need two rows. I will go for a grid layout for this sample.

In default.css, define the “navbar” to a grid layout with two rows and 4 columns. I would set the first row to a fixed size and the second row to a “auto” size. This is because when I want to hide the contents, the height should go to 0 and when I show the contents it should take the height of the elements in it. For the columns I would have one column for each category and the last one taking the rest space.

.navbar {
    -ms-grid-rows: 100px auto;
    -ms-grid-columns: 150px 150px 150px 1fr;
    display:-ms-grid;
    background-color: gray;
}

Now I would place the category divs in their location as per my grid layout.

.navbar .itembirds {
        -ms-grid-column:1;
        -ms-grid-row:1;

    }

    .navbar .itemanimals {
        -ms-grid-column:2;
        -ms-grid-row:1;
    }

    .navbar .itemreptiles {
        -ms-grid-column:3;
        -ms-grid-row:1;
    }

I would put the list view in the second row and make it streatch to all available width and height. I would give it a height because the second row was set to auto. This is because when the list view is shown it will show as height of 70px and when hidden the height would be 0px.

.navbar .navbarlist {
        -ms-grid-row:2;
        -ms-grid-column:1;
        -ms-grid-column-span:7;
        background-color: lightgray;
        height: 70px;
    }

For hiding and showing elements I use a class approach. I add a “hidden” class name to elements that need to be hidden and remove this when the elements need to be shown. By using so, I don’t need to handle them in JavaScript. All I need to do is add/remove this class and rest is taken care of. So I would define the hidden class in CSS.

.navbar .hidden {
        display:none;
    }

I would do the styling and layout for the category items and the list view items. I am not going through all of them. You can get the styles from the full code shared at the end of this article.

Now we jump on to the code part. We would do all the code in default.js.

First we need to create the data. For this in app activated event after processing the UI we will call the method we created in data.js. Then we will call a method to initialize the app bar for us.

args.setPromise(WinJS.UI.processAll().done(function(x)
            {
                AppBarData.CreateAppBarData();// this method populates the data
                initializeAppBar(); // Start initializing the app bar
            }));

We need to have a variable to track which button is selected currently. We would add a string variable for the same.

var currentSelected = "";

We will add a small helper method for hiding/showing elements. This code just adds/removes the hidden class based on whether the element is to be shown or hidden. Rest as discussed earlier is taken care by CSS.

function showHideElement(element, show) {
        if (show) {
            if (WinJS.Utilities.hasClass(element, "hidden")) {
                WinJS.Utilities.removeClass(element, "hidden")
            }
        }
        else {
            if (!WinJS.Utilities.hasClass(element, "hidden")) {
                WinJS.Utilities.addClass(element, "hidden")
            }
        }
    }

In the initialize function we will add event listeners for the category div “click” event. In this event implementation we will do these steps:
Check the current selection. If current selection is nothing show the items. If current selection is the one that is clicked, then we need to hide the items. If current selection is different from the one clicked then hide the current and show this one.

We will also add a event listener for the app bar “onafterhide” event. This is to ensure that when the app bar is hidden the app bar is cleaned up and reverts to default state.

function initializeAppBar() {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;
        listViewCtrl.itemTemplate = document.body.querySelector(".itemtemplatelist");
        listViewCtrl.oniteminvoked = itemInvokedList;

        appbarCtrl.onafterhide = function (e) {
            hideCurrentLowerList();
        };

        var birdsButton = appbar.querySelector(".itembirds");
        var animalsButton = appbar.querySelector(".itemanimals");
        var reptilesButton = appbar.querySelector(".itemreptiles");    

        birdsButton.addEventListener("click", function (args) {
            var show = currentSelected === "Birds" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList();
            }
            if (show) {
                showLowerList("Birds");
            }
        }, false);

        animalsButton.addEventListener("click", function (args) {
            var show = currentSelected === "Animals" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList()
            }
            if (show) {
                showLowerList("Animals");
            }
        }, false);

        reptilesButton.addEventListener("click", function (args) {
            var show = currentSelected === "Reptiles" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList();
            }
            if (show) {
                showLowerList("Reptiles");
            }
        }, false);
    }

In above code we have used two helper functions : “hideCurrentLowerList” and “showLowerList”.

The “hideCurrentLowerList” function hides the current shown list based on the currentSelected state. In this function we hide the list, toggle the visibility of arrow divs to show the up arrow instead of the down arrow. We also reset the currentSelected variable to initial state.

function hideCurrentLowerList() {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listView = appbar.querySelector(".navbarlist");
        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;

        // First check if any other is open and close that
        switch (currentSelected) {
            case "Birds":
                {
                    var birdsButton = appbar.querySelector(".itembirds");
                    var upArrow = birdsButton.querySelector(".upArrow");
                    var downArrow = birdsButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            case "Animals":
                {
                    var animalsButton = appbar.querySelector(".itemanimals");
                    var upArrow = animalsButton.querySelector(".upArrow");
                    var downArrow = animalsButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            case "Reptiles":
                {
                    var reptilesButton = appbar.querySelector(".itemreptiles");
                    var upArrow = reptilesButton.querySelector(".upArrow");
                    var downArrow = reptilesButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

In the “showLowerList” based on the selection we would initialize the list with those items (for the category we have selected) and show the list and toggle the visibility of arrow divs to show the down arrow instead of the up arrow. We also reset the currentSelected variable to the current selected.

function showLowerList(parentname) {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listView = appbar.querySelector(".navbarlist");
        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;

        // Now show for current
        switch (parentname) {
            case "Birds":
                {
                    var birdsButton = appbar.querySelector(".itembirds");
                    var upArrow = birdsButton.querySelector(".upArrow");
                    var downArrow = birdsButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    listViewCtrl.itemDataSource = AppBarData.BirdData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    showHideElement(listView, true);
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            case "Animals":
                {
                    var animalsButton = appbar.querySelector(".itemanimals");
                    var upArrow = animalsButton.querySelector(".upArrow");
                    var downArrow = animalsButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    listViewCtrl.itemDataSource = AppBarData.AnimalData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    showHideElement(listView, true);
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            case "Reptiles":
                {
                    var reptilesButton = appbar.querySelector(".itemreptiles");
                    var upArrow = reptilesButton.querySelector(".upArrow");
                    var downArrow = reptilesButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    showHideElement(listView, true);
                    listViewCtrl.itemDataSource = AppBarData.ReptileData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

Note : In the above code you would see that we are making the appbar sticky true and then false after assigning the item source and making it visible. this is because the app bar if not made sticky would collapse itself when we try to show the list.

The last part is to implement the item invoked for the list item.

function itemInvokedList(args) {
        var index = args.detail.itemIndex;

        switch (currentSelected) {
            case "Birds":
                {
                    var item = AppBarData.BirdData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            case "Animals":
                {
                    var item = AppBarData.AnimalData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            case "Reptiles":
                {
                    var item = AppBarData.ReptileData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

We are done. You can add styles and modify this to look better. I have just demonstrated how you should approach such a requirement. I am not sure if this is the best solution, but it works certainly. I would welcome feedback in case you have any.

Below is the full source code for your reference :

data.js

//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
(function () {
     WinJS.Namespace.define("AppBarData",
        {
            BirdData: null,
            AnimalData: null,
            ReptileData: null,
            CreateAppBarData: initializeData
        });

    function initializeData() {
        var birdsdata = [{ title: "Ostriches" }, { title: "Kiwis" }, { title: "Penguins"}, { title: "Parrots"}, { title: "Storks"}];
        var animaldata = [{ title: "Tiger"}, { title: "Lion"}, { title: "Giraffe"}, { title: "Whale"}, { title: "Penguins"}];
        var reptilesData = [{ title: "Aligators"}, { title: "Crocodiles"}, { title: "Turtle"}, { title: "Lizard"}, { title: "Snakes"}];

        AppBarData.AnimalData = new WinJS.Binding.List(animaldata);
        AppBarData.BirdData = new WinJS.Binding.List(birdsdata);
        AppBarData.ReptileData = new WinJS.Binding.List(reptilesData);
    }
})();

default.html

<!--THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.-->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>AppBarListView Sample</title>

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

    <!-- AppBarListView references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/default.js"></script>
    <script src="/js/data.js"></script>
</head>
<body>
    <div class="itemtemplatelist" data-win-control="WinJS.Binding.Template">
        <div class="item">
           <h4 class="title" data-win-bind="textContent: title"></h4>
        </div>
    </div>

    <p>Content goes here</p>
    <div class="appBarSample" data-win-control="WinJS.UI.AppBar" aria-label="Navigation Bar"
        data-win-options="{layout:'custom',placement:'top'}">
        <header class="navbar">
            <div class="itembirds item">
                <h3 class="overlay">Birds</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="itemanimals item">
                <h3 class="overlay">Animals</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="itemreptiles item">
                <h3 class="overlay">Reptiles</h3>
                <div class="upArrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->
                <div class="downArrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->
            </div>
            <div class="navbarlist hidden win-selectionstylefilled" data-win-control="WinJS.UI.ListView"
                data-win-options="{ selectionMode: 'none' }"></div>
        </header>
    </div>
</body>
</html>

default.js

//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    WinJS.Binding.optimizeBindingReferences = true;

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;

    var currentSelected = "";

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
            args.setPromise(WinJS.UI.processAll().done(function(x)
            {
                AppBarData.CreateAppBarData();// this method populates the data
                initializeAppBar(); // Start initializing the app bar
            }));
        }
    };

    app.oncheckpoint = function (args) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // args.setPromise().
    };

    function initializeAppBar() {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;
        listViewCtrl.itemTemplate = document.body.querySelector(".itemtemplatelist");
        listViewCtrl.oniteminvoked = itemInvokedList;

        appbarCtrl.onafterhide = function (e) {
            hideCurrentLowerList();
        };

        var birdsButton = appbar.querySelector(".itembirds");
        var animalsButton = appbar.querySelector(".itemanimals");
        var reptilesButton = appbar.querySelector(".itemreptiles");    

        birdsButton.addEventListener("click", function (args) {
            var show = currentSelected === "Birds" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList();
            }
            if (show) {
                showLowerList("Birds");
            }
        }, false);

        animalsButton.addEventListener("click", function (args) {
            var show = currentSelected === "Animals" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList()
            }
            if (show) {
                showLowerList("Animals");
            }
        }, false);

        reptilesButton.addEventListener("click", function (args) {
            var show = currentSelected === "Reptiles" ? false : true;
            if (currentSelected != "") {
                hideCurrentLowerList();
            }
            if (show) {
                showLowerList("Reptiles");
            }
        }, false);
    }

    function hideCurrentLowerList() {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listView = appbar.querySelector(".navbarlist");
        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;

        // First check if any other is open and close that
        switch (currentSelected) {
            case "Birds":
                {
                    var birdsButton = appbar.querySelector(".itembirds");
                    var upArrow = birdsButton.querySelector(".upArrow");
                    var downArrow = birdsButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            case "Animals":
                {
                    var animalsButton = appbar.querySelector(".itemanimals");
                    var upArrow = animalsButton.querySelector(".upArrow");
                    var downArrow = animalsButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            case "Reptiles":
                {
                    var reptilesButton = appbar.querySelector(".itemreptiles");
                    var upArrow = reptilesButton.querySelector(".upArrow");
                    var downArrow = reptilesButton.querySelector(".downArrow");

                    showHideElement(downArrow, false);
                    showHideElement(upArrow, true);
                    listViewCtrl.itemdatasource = null;
                    showHideElement(listView, false);
                    currentSelected = "";
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

    function showLowerList(parentname) {
        var appbar = document.body.querySelector(".appBarSample");
        var appbarCtrl = document.body.querySelector(".appBarSample").winControl;

        var listView = appbar.querySelector(".navbarlist");
        var listViewCtrl = appbar.querySelector(".navbarlist").winControl;

        // Now show for current
        switch (parentname) {
            case "Birds":
                {
                    var birdsButton = appbar.querySelector(".itembirds");
                    var upArrow = birdsButton.querySelector(".upArrow");
                    var downArrow = birdsButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    listViewCtrl.itemDataSource = AppBarData.BirdData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    showHideElement(listView, true);
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            case "Animals":
                {
                    var animalsButton = appbar.querySelector(".itemanimals");
                    var upArrow = animalsButton.querySelector(".upArrow");
                    var downArrow = animalsButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    listViewCtrl.itemDataSource = AppBarData.AnimalData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    showHideElement(listView, true);
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            case "Reptiles":
                {
                    var reptilesButton = appbar.querySelector(".itemreptiles");
                    var upArrow = reptilesButton.querySelector(".upArrow");
                    var downArrow = reptilesButton.querySelector(".downArrow");

                    appbarCtrl.sticky = true;
                    showHideElement(downArrow, true);
                    showHideElement(upArrow, false);
                    showHideElement(listView, true);
                    listViewCtrl.itemDataSource = AppBarData.ReptileData.dataSource;
                    listViewCtrl.layout = new WinJS.UI.GridLayout({ maxRows: 1 });
                    appbarCtrl.sticky = false;
                    currentSelected = parentname;
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

    function showHideElement(element, show) {
        if (show) {
            if (WinJS.Utilities.hasClass(element, "hidden")) {
                WinJS.Utilities.removeClass(element, "hidden")
            }
        }
        else {
            if (!WinJS.Utilities.hasClass(element, "hidden")) {
                WinJS.Utilities.addClass(element, "hidden")
            }
        }
    }

    function itemInvokedList(args) {
        var index = args.detail.itemIndex;

        switch (currentSelected) {
            case "Birds":
                {
                    var item = AppBarData.BirdData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            case "Animals":
                {
                    var item = AppBarData.AnimalData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            case "Reptiles":
                {
                    var item = AppBarData.ReptileData.getAt(index);
                    // Do some action as navigate to soem page passing the data or something else
                    break;
                }

            default:
                {
                    break;
                }
        }
    }

    app.start();
})();

default.css

/*THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.*/

.navbar {
    -ms-grid-rows: 100px auto;
    -ms-grid-columns: 150px 150px 150px 1fr;
    display:-ms-grid;
    background-color: gray;
}

    .navbar .item {
        height:90px;
        width:140px;
        background-color:whitesmoke;
        border:1px solid gray;
        margin-top:3px;
        margin-bottom:3px;
        margin-left: 5px;
        margin-right:5px;
        -ms-grid-rows:1fr;
        -ms-grid-columns:1fr 20px;
        display:-ms-grid;
        font-family: 'Segoe UI SemiBold'
    }

        .navbar .item .overlay {
            -ms-grid-column:1;
            -ms-grid-row:1;
            margin-top:40px;
            margin-left:5px;
        }

        .navbar .item .upArrow {
            -ms-grid-column:2;
            -ms-grid-row:1;
            margin-top:40px;
        }

        .navbar .item .downArrow {
            -ms-grid-column:2;
            -ms-grid-row:1;
            margin-top:40px;
        }

    .navbar .item:hover {
        border:2px solid gray;
    }

    .navbar .itembirds {
        -ms-grid-column:1;
        -ms-grid-row:1;

    }

    .navbar .itemanimals {
        -ms-grid-column:2;
        -ms-grid-row:1;
    }

    .navbar .itemreptiles {
        -ms-grid-column:3;
        -ms-grid-row:1;
    }

    .navbar .navbarlist {
        -ms-grid-row:2;
        -ms-grid-column:1;
        -ms-grid-column-span:7;
        background-color: lightgray;
        height: 70px;
    }

        .navbar .navbarlist .item {
            height: 50px;
            width : 90px;
            background-color:indigo;
        }

        .navbar .navbarlist .item .title{
            margin-top:20px;
            text-align:center;
            color:white;
            font-family: 'Segoe UI SemiLight';
        }

    .navbar .hidden {
        display:none;
    }

I hope this is useful.. Let me know if you have any question(s)..

Here are some links

List view

Cheers….

 

 

Disclaimer

Published by

Girija Shankar Beuria

A software developer by profession with 13+ years of experience in the following technologies : Data Warehousing, Business Intelligence applications using SQL Server BI Stack, Product Frameworks and Test automation framework, MOSS , C# .Net, .NET, POWERSHELL, AMO, HTML 5, JavaScript, Reporting Service Web service, Dynamics AX, Dynamics AX 2012 BI Cubes, Dynamics AX 2012 SSRS Reports, SQL Azure, Windows Azure Web Services, ASP .NET MVC 4 Web API, WCF, Entity Framework, WPF, Excel Object Model, Windows 8 Apps, Windows Phone Apps

3 thoughts on “WinJS appbar – tabbed control with listview”

  1. Thank You for blog post. It is really really helpful 🙂

    I need to add one more feature here, that is when I have Birds, there should be two buttons. One is Birds itself clickable and second is the arrow button. And if I clicked on Birds then some action to be invoked and if I click in arrow then listview should be displayed.

    1. That should be easy. In the item template have two div (one for label) another for arrows something like this

      
      <div class="itembirds item">                
      <div class="overlay">Birds</h3>                
      <div class="upArrow arrow">▲</div> <!-- Data is amphashx25B2; (convert the amp and hash to symbols) -->                
      <div class="downArrow arrow hidden">▼</div> <!-- Data is amphashx25BC; (convert the amp and hash to symbols) -->            
      </div>
      
      

      In css define the divs to be 100%:

      .navbar .item .overlay { -ms-grid-column:1; -ms-grid-row:1;   margin-top:40px;            margin-left:5px; width:100%; height:100%;       }
      
      .navbar .item .upArrow {            -ms-grid-column:2;            -ms-grid-row:1;            margin-top:40px; width:100%; height:100%;        }         
      .navbar .item .downArrow {            -ms-grid-column:2;            -ms-grid-row:1;            margin-top:40px; width:100%; height:100%;        }
      

      in javscript now you can attach individual click event handler. Something like this:

      var birdsButton = appbar.querySelector(".itembirds");
      var birdOnly = birdsButton.querySelector(".overlay");
      var buttonOnly = birdsButton.querySelector(".arrow"); 
      
      birdOnly.addEventListner("click", function(args)
      {
      // Do stuff when the bird only is clicked
      },false);
      
      butonOnly.addEventListner("click", function(args)
      {
      // Do stuff when the bird arrow button is clicked
      },false);
      
      
      

      Hope this helps..

      – Girija

      1. I tried the above reply but on applying width and height 100% to all three div tags it takes up all the horizontal space and ispite of viewing them first tab layer in horizontal way, I am viewing it in vertical way.

        buttonOnly is only called for up arrow and do not know, it is not reflecting for down arrow. I just re-factored the above code and ispite of birdsbutton, I did was buttonOnly to have up and down arrow function. But it is only clicking once, to close the second layer of tabs, it is not working.

Leave a reply to Varun Gupta Cancel reply