Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

PageControl objects and navigation for your Windows Store app

, 13 Feb 2013
Rate this:
Please Sign up or sign in to vote.
PageControl objects and navigation for your Windows Store app

Develop a Windows 8 app in 30 days

The "Hello, world" app that you created in the previous tutorials only contains a single page of content. Most real-world apps contain multiple pages. In this tutorial, you copy the code from your "Hello, world" app into a new app that uses the Navigation App template, and then you add an additional page.

Learn how to:

  • Use the Navigation App project template to create an app that contains multiple pages of content.
  • Use PageControl objects to separate your code into modular units.
  • Use the single-page navigation model to navigate between pages.
  • Use an AppBar to provide navigation commands.

Before you start...

About Navigation in Windows Store Apps

Nearly every website provides some form of navigation, usually in the form of hyperlinks that you click to go to a different page. Each page has its own set of JavaScript functions and data, a new set of HTML to display, style information, and so on. This navigation model is known as multi-page navigation. This design is fine for most websites, but it can pose problems for an app because it makes it difficult to maintain state across the different pages.

Another navigation model is single-page navigation, where you use a single page for your app and load additional data into that page as needed. You still split your application into multiple files, but instead of moving from page to page, your app loads other documents into the main page. Because your app's main page is never unloaded, your scripts are never unloaded, which makes it easier to manage state, transitions, or animations. We recommend that Windows Store apps use the single-page navigation model.

To help you create apps that use the single-page navigation model, the Windows Library for JavaScript provides the WinJS.UI.Pages.PageControl object. There's also the Navigation App project template, which provides some additional navigation infrastructure. In the next step, you use this template to create a new project.

Step 1: Create a new Navigation App in Visual Studio

Let's create a new app named HelloWorldWithPages that uses the Navigation App template. Here's how:

  1. Launch Microsoft Visual Studio Express 2012 for Windows 8.
  2. From the File menu select New Project.

The New Project dialog appears. The left pane of the dialog lets you pick the type of templates to display.

  1. In the left pane, expand Installed, then expand Templates, then expand JavaScript and select the Windows Store template type. The dialog's center pane displays a list of project templates for JavaScript.

For this tutorial, we use the Navigation App template.

  1. In the center pane, select the Navigation App template.
  2. In the Name text box, enter "HelloWorldWithPages".
  3. Uncheck the Create directory for solution checkbox.

  1. Click OK to create the project.

Visual Studio creates your project and displays it in the Solution Explorer.

Notice that your new Navigation App contains a few more files than your "Hello, world" app does. Let's take a look at these new files:

  • /pages/home/home.css, /pages/home/home.html, and /pages/home/home.js

These three pages define a PageControl for the app's home page. A PageControl is made up of an HTML file, a JavaScript file, and a CSS file. A PageControl is a modular unit of HTML, CSS, and JavaScript that can be navigated to (like an HTML page) or used as a custom control. You can use PageControl objects to split a large app into smaller, more manageable portions.

PageControl objects support several methods that makes using them in your app easier than using a collection of loose HTML, CSS, and JavaScript pages. You'll learn more about these methods in a later step.

  • /js/navigator.js

This file provides the PageControlNavigator helper class that you can use to display PageControl objects and navigate between them. You don't need it to display a PageControl, but it can make using them easier.

Let's take a look at the default.html page of your new app:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>HelloWorldWithPages</title>
 
    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <!-- HelloWorldWithPages references -->
    <link href="http://www.codeproject.com/css/default.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/js/default.js"></script>
    <script src="http://www.codeproject.com/js/navigator.js"></script>
</head>
<body>
 
    <div id="contenthost" data-win-control="Application.PageControlNavigator" 
        data-win-options="{home: '/pages/home/home.html'}"></div>
    <!-- <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand" 
            data-win-options="{id:'cmd', label:'Command',
                icon:'placeholder'}" 
            type="button"></button>
    </div> -->
</body>
</html>

The body of the file contains two elements: A div element for the PageControlNavigator and a commented-out div for an AppBar. Let's ignore the app bar for now and take a closer look at the first div element.

    <div id="contenthost" data-win-control="Application.PageControlNavigator" 
        data-win-options="{home: '/pages/home/home.html'}"></div>

This div element creates a PageControlNavigator control. The PageControlNavigator loads and displays our home page for us. You use the data-win-options attribute to tell it which page to load (/pages/home/home.html).

Go ahead and run the app.

Although it's not obvious, the app is actually showing both default.html and home.html. It's similar to using an iframe to display a HTML page inside another HTML page.

Step 2: Copy your HTML and CSS content from your "Hello, world" app

Our new app has two HTML pages: default.html and home.html. Where do you put your content?

  • Use default.html for UI that should always be present, no matter which page the app is displaying. For example, you can use default.html to host a navigation bar.
  • Use pages, such as home.html, for content that makes up an individual screen in the app.

Let's open home.html and take a look at some of the markup it contains.

  • It has a head element that contains references to the Windows Library for JavaScript code and style sheets. It also contains references to the app's default style sheet (default.css) and to the other files that make up the home page (home.css and home.js).
<head>
    <meta charset="utf-8" />
    <title>homePage</title>
 
    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <link href="http://www.codeproject.com/css/default.css" rel="stylesheet" />
    <link href="http://www.codeproject.com/pages/home/home.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/pages/home/home.js"></script>
</head>
  • It has a page header area that includes a back button for backwards navigation and a title area. The template contains code that automatically enables the back button when you can navigate backwards. The button won't be visible until we add a second page and navigate there.
 
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
            </h1>
        </header>
  • It has a section for your main content.
        <section aria-label="Main content" role="main">
            <p>Content goes here.</p>
        </section>

Let's add the content from our "Hello, world" app to the home page (home.html) of our new HelloWorldWithPages project.

To add the HTML and CSS content from your "Hello, world" app

  1. Copy your final HTML content from the default.html file of your "Hello, world" app into the main content section of the /pages/home/home.html in your new project.
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <!-- Copied from "Hello, world" -->
            <h1 class="headerClass">Hello, world!</h1>
            <div class="mainContent">
                <p>What's your name?</p>
                <input id="nameInput" type="text" />
                <button id="helloButton">Say "Hello"</button>
                <div id="greetingOutput"></div>
                <label for="ratingControlDiv">
                    Rate this greeting: 
                </label>
                <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
                </div>
                <div id="ratingOutput"></div>
            </div>
        </section>
    </div>
</body>
  1. Move the heading content that you copied to the h1 element that home.html provides for you. Since home.html already contains a main content section, remove the "mainContent" div element that you copied (but leave its contents).
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Hello, world!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <p>What's your name?</p>
            <input id="nameInput" type="text" />
            <button id="helloButton">Say "Hello"</button>
            <div id="greetingOutput"></div>
            <label for="ratingControlDiv">
                Rate this greeting: 
            </label>
            <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
            </div>
            <div id="ratingOutput"></div>
        </section>
    </div>
</body>
 
  1. Switch to the light style sheet. Replace the reference to the dark style sheet:
    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

With this one:

    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
 
  1. Each PageControl has its own Cascading Style Sheets (CSS) file.

Copy the greetingOutput style from the default.css file you created in Part 1: Create a "Hello, world!" app to home.css.

.homepage section[role=main] {
    margin-left: 120px;
}
 
#greetingOutput {
    height: 20px; 
    margin-bottom: 40px;
}
 
@media screen and (-ms-view-state: snapped) {
    .homepage section[role=main] {
        margin-left: 20px;
    }
}
 
@media screen and (-ms-view-state: portrait) {
    .homepage section[role=main] {
        margin-left: 100px;
    }
}
  1. Run the app.

You've recreated the content from your original "Hello, world" app. Next, we add interactivity by copying your "Hello, world" event handlers.

Step 3: Copy your event handlers

Each PageControl has its own JavaScript file. Let's take a look at the JavaScript file that Visual Studio created for our "home" PageControl, home.js:

(function () {
    "use strict";
 
    WinJS.UI.Pages.define("/pages/home/home.html", {
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
        }
    });
})();

This file looks quite a bit different than your default.js file. For one thing, it's much shorter. That's because default.js already handles activation and core app logic. Each PageControl only needs to contain logic for the page itself.

One of the first lines of code, a call to the WinJS.UI.Page.define function, creates the PageControl object. This function takes two parameters: the URI of the page ("/pages/home/home.html" in this example), and an object that defines the members of the PageControl. You can add any type of member you want. You can also implement a set of special members, described by the IPageControlMembers interface, that automatically get called by the app when you use the PageControl.

The home.js file created by the template defines one of these special members, the ready function. The ready function is called after your page is initialized and rendered. This is a good place to attach event handlers.

You might notice that the code doesn't include a call to WinJS.UI.processAll. That's because the PageControl calls it for you, automatically. By the time the ready function is called, WinJS.UI.processAll has already been called and has completed its processing.

To add your event handlers

  1. In Part 1: Create a "Hello, world!" app and Part 2: Manage app lifecycle and state, you defined three event handlers: buttonClickHandler, ratingChanged, and nameInputChanged. Copy those event handlers to your home.js file and make them members of your PageControl. Add them after the ready function that the template created for you.
    WinJS.UI.Pages.define("/pages/home/home.html", {
 
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
        },
 
        buttonClickHandler: function (eventInfo) {
 
            var userName = document.getElementById("nameInput").value;
            var greetingString = "Hello, " + userName + "!";
            document.getElementById("greetingOutput").innerText = greetingString;
 
            // Save the session data. 
            WinJS.Application.sessionState.greetingOutput = greetingString;
        },
 
        ratingChanged: function (eventInfo) {
 
            var ratingOutput = document.getElementById("ratingOutput");
            ratingOutput.innerText = eventInfo.detail.tentativeRating;
 
            // Store the rating for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["greetingRating"] = eventInfo.detail.tentativeRating;
        },
 
        nameInputChanged: function (eventInfo) {
            var nameInput = eventInfo.srcElement;
 
            // Store the user's name for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["userName"] = nameInput.value;
        }
 
 
    });
  1. Now we need to attach our event handlers. In Parts 1 and 2, we created a then function for the Promise returned by WinJS.UI.processAll. Things are a bit simpler now, because we can use the ready function to attach our event handlers. The ready function is called after the PageControl has automatically called WinJS.UI.processAll for us.

Copy the code that attaches your event handlers to the ready function in home.js.

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
        }
  1. Run the app. When you enter a name and click the button, it displays a greeting. When you rate the greeting, it displays the numeric rating value.

Step 4: Restore the app's state

We've almost recreated the functionality we had in our "Hello, world" app. The only thing we need to do now is restore the app's state when the user launches it.

You might remember that we had two types of app state to restore:

  • The user name and rating. We restore this state regardless of how the app was shut down.
  • The personalized greeting. We only restore this state if the app was successfully terminated the last time it ran.

To restore the app's state

  1. Copy the code from the "Hello, world" app that restores the user name and rating. Add the code to the ready function in home.js.
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
            // Restore app data. 
            var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
 
            // Restore the user name.
            var userName =
               Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
            if (userName) {
                nameInput.value = userName;
            }
 
            // Restore the rating. 
            var greetingRating = roamingSettings.values["greetingRating"];
            if (greetingRating) {
                ratingControl.userRating = greetingRating;
                var ratingOutput = document.getElementById("ratingOutput");
                ratingOutput.innerText = greetingRating;
            }
 
        },
  1. We only want to restore the personalized greeting if the app was successfully terminated the last time it ran. Unfortunately, our PageControl doesn't provide a built-in way for us to check the app's previous execution state: that info is provided to the onactivated event handler in our default.js file. But there's an easy solution to this problem: we just need to save the app's previous execution state in the sessionState object so that our PageControl can access it.
    1. In your default.js file, add code to your onactivated handler to save the previous execution state. Save the state by adding a property to the sessionState object named previousExecutionState.
    app.addEventListener("activated", 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.
            }
 
            // Save the previous execution state. 
            WinJS.Application.sessionState.previousExecutionState = 
                args.detail.previousExecutionState;
 
            if (app.sessionState.history) {
                nav.history = app.sessionState.history;
            }
            args.setPromise(WinJS.UI.processAll().then(function () {
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }
            }));
        }
    });
    1. In your home.js file, add code to your ready method that checks the previousExecutionState data. If the previous execution state is terminated, restore the personalized greeting (you can copy your code that does this from the default.js file in your "Hello, world" app.)
            // If the app was terminated last time it ran, restore the personalized
            // greeting. 
            if (               
                WinJS.Application.sessionState.previousExecutionState
                === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                var outputValue = WinJS.Application.sessionState.greetingOutput;
                if (outputValue) { 
                    var greetingOutput = document.getElementById("greetingOutput");
                    greetingOutput.innerText = outputValue;
                }
 
            }

Here's the complete ready method.

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
            // Restore app data. 
            var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
 
            // Restore the user name.
            var userName = 
                Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
            if (userName) {
                nameInput.value = userName;
            }
 
            // Restore the rating. 
            var greetingRating = roamingSettings.values["greetingRating"];
            if (greetingRating) {
                ratingControl.userRating = greetingRating;
                var ratingOutput = document.getElementById("ratingOutput");
                ratingOutput.innerText = greetingRating;
            }
 
            // If the app was terminated last time it ran, restore the personalized
            // greeting. 
            if (
               WinJS.Application.sessionState.previousExecutionState
               === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
               var outputValue = WinJS.Application.sessionState.greetingOutput;
                if (outputValue) {
                    var greetingOutput =
                    document.getElementById("greetingOutput");
                    greetingOutput.innerText = outputValue;
                }
 
            }
 
        }
  1. Run the app. We've now duplicated the functionality we had in our original "Hello, world" app.

Step 5: Add another page

Most apps contain several pages. Let's add another page to our app. Because we're using the Navigation App template, it's easy to add additional pages.

To add another page

  1. In the Solution Explorer, right-click the pages folder and select Add > New Folder. A new folder appears in the project.
  2. Rename the folder to "page2".
  3. Right-click the page2 folder and select add Add > New Item.... The Add New Item dialog appears.
  4. Select Page Control from the list. In the Name text box, enter "page2.html".

  1. Click Add to add the PageControl. The new PageControl appears in the Solution Explorer.

The new PageControl has three files: page2.css, page2.html, and page2.js.

You've created a new page. In the next step, you earn how to navigate to it.

Step 6: Use the navigate function to move between pages

Right now, we have a second page but no way for the user to get to it. Let's update our home.html page by adding a link to page2.html.

To navigate between pages

  1. Open your home.html and add a link to page2.html.
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Hello, world!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <p>What's your name?</p>
            <input id="nameInput" type="text" />
            <button id="helloButton">Say "Hello"</button>
            <div id="greetingOutput"></div>
            <label for="ratingControlDiv">
                Rate this greeting:
            </label>
            <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
            </div>
            <div id="ratingOutput"></div>
 
            <!-- A hyperlink to page2.html. -->
            <p><a href="http://www.codeproject.com/pages/page2/page2.html">Go to page 2.</a></p>
        </section>
    </div>
</body>
  1. Run the app and click the link. It seems to work: the app displays page2.html.

However, there's a problem: the app performed a top-level navigation. Instead of navigating from home.html to page2.html, it navigates from default.html to page2.html.

What you want instead is to replace the content of home.html with page2.html.

Fortunately, the PageControlNavigator control makes performing this type of navigation fairly easy. The PageControlNavigator code (in your app's navigator.js file) handles the WinJS.Navigation.navigated event for you. When the event occurs, the PageControlNavigator loads the page specified by the event.

The WinJS.Navigation.navigated event occurs when you use the WinJS.Navigation.navigate, WinJS.Navigation.back, or WinJS.Navigation.forward functions to navigate.

You need to call WinJS.Navigation.navigate yourself rather than using the hyperlink's default behavior. You could replace the link with a button and use the button's click event handler to call WinJS.Navigation.navigate. Or you could change the default behavior of the hyperlink so that when the user clicks on a link, the app uses WinJS.Navigation.navigate to navigate to the link target. To do this, handle the hyperlink's click event and use the event to stop the hyperlink's default navigation behavior, and then call the WinJS.Navigation.navigate function and pass it the link target.

To override the default hyperlink behavior

  1. In your home.js file, define a click event handler for your hyperlinks and make it a member of your PageControl. Add it after the nameInputChanged handler.
        linkClickEventHandler: function (eventInfo) {
 
        }
  1. Call the preventDefault method to prevent the default link behavior (navigating directly to the specified page).
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
 
        }
  1. Retrieve the hyperlink that triggered the event.
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
            var link = eventInfo.target;
 
        }
 
  1. Call the WinJS.Navigation.navigate function and pass it the link target. (Optionally, you could also pass a state object that describes the state for that page. For more information, see the WinJS.Navigation.navigate page.)
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
            var link = eventInfo.target;
            WinJS.Navigation.navigate(link.href);
        }
  1. In the home.js file's ready function, attach the event handler to your hyperlinks.

The Windows Library for JavaScript provides a WinJS.Utilities.query function that makes it easy to retrieve a number of elements on the page. The WinJS.Utilities.query function returns a QueryCollection, which provides additional methods for attaching and removing event handlers. Let's use the WinJS.Utilities.query collection and the listen method to attach our linkClickEventHandler.

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
            
            WinJS.Utilities.query("a").listen("click", this.linkClickEventHandler, false);

The nice thing about this approach is that it will work for any number of links on the page. We only have one link right now, but, with this approach, we could add more links and we wouldn't have to change our code.

  1. Run the app and click the link for page2.html.

Now the page displays using the proper navigation pattern.

Step 7: Add an app bar for additional navigation

The WinJS.UI.AppBar control provides users with easy access to commands when they need them. The user can swipe the bottom edge of the screen to make app bars appear. They cover the content of the application and can be dismissed by the user with an edge swipe, or by interacting with the application. Use app bars to present navigation, commands, and tools to users.

The app bar is a Windows Library for JavaScript control. To declare one in your HTML, you use this syntax:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
</div>

An app bar is designed to contain app bar command buttons. To create an app bar command button, you use a button element and use the data-win-control attribute to make it a command button.

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"></button>
</div>

You also need to use the data-win-options attribute to specify a few options for the command button:

  • id: The ID of the command.
  • label: The label to display for the command.
  • icon: The icon to display for the command, or the path to a custom PNG file. (For a list of icon values, see AppBarIcon.)
  • section: The section to which the command belongs. The possible values are "selection" and "global".
  • tooltip: The tooltip to display for the command.
  • type: The type of command. The possible values are "button", "toggle", "flyout", and "separator".

Here's an example of what the syntax looks like:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmdID', label:'Command', icon:'placeholder', section='global', tooltip: 'Command', type='button'}">
        </button>
</div>

You can also add hr elements as separators, like this:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <hr 
            data-win-control="WinJS.UI.AppBarCommand" 
            data-win-options="{type:'separator', section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmdID', label:'Command', icon:'placeholder', tooltip: 'Command'}">
        </button>
</div>

By default, the only elements the app bar can contain are command buttons and hr elements, both of which must have the data-win-control="WinJS.UI.AppBarCommand" attribute set on them. (To learn how to create custom app bars that can contain other elements, see Quickstart: Adding a custom app bar.)

After you create your app bar, you need to attach event handlers to your commands. You can do this like you would any other control, or you can use the app bar's getCommandById method to retrieve the command:

var appbar = document.getElementById("appbar").winControl;
appbar.getCommandById("cmdID").addEventListener("click", myEventHandler, false);

Our app contains several pages. Where should we add our app bar?

  • If your app bar contains commands that should be available to every page, add it to your default.html file.
  • If your app bar is different from page to page, you can define a different app bar in each of your PageControl objects.
  • You can also define a central app bar in default.html and then modify as you load different PageControl objects.

Let's create a simple app bar that lets the user navigate between home.html and page2.html. We'll define our app bar in our default.html file.

To add an app bar

  1. Open your default.html file. The template created an app bar for us, but it's commented out. Uncomment it.
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>HelloWorldWithPages</title>
 
    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <!-- HelloWorldWithPages references -->
    <link href="http://www.codeproject.com/css/default.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/js/default.js"></script>
    <script src="http://www.codeproject.com/js/navigator.js"></script>
</head>
<body>
    <div id="contenthost" data-win-control="Application.PageControlNavigator"
        data-win-options="{home: '/pages/home/home.html'}"></div>
    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}"
            type="button"></button>
    </div>
</body>
</html>
  1. Run the app. To show the bar, make a top or bottom edge swipe, press Win + Z, or click the right mouse button.

Right now the app bar doesn't do anything interesting.

  1. Remove the app bar's button and replace it with two of your own button elements: one for navigating to home.html and one for navigating to page2.html. Give the home button an ID of "homeButton" and the button for navigating to page2.html an ID of "page2Button". Let's add a separator, too.
    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <hr
            data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{type:'separator',section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'homeButton',label:'Home',icon:'home',section:'global',
            tooltip:'Go to the home page', type: 'button'}">
        </button>
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'page2Button',label:'Page 2',icon:'page',section:'global',
            tooltip:'Go to page 2', type: 'button'}">
        </button>
    </div>
  1. According to the Guidelines and checklist for app bars, app bars used for navigation should appear at the top of the screen. You can control where an app bar appears by setting its placement property. Set the app bar's placement property to "top" to make it appear at the top of the screen.
    <div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{placement:'top'}" >
        <hr
            data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{type:'separator',section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'homeButton',label:'Home',icon:'home',section:'global',
            tooltip:'Go to the home page', type: 'button'}">
        </button>
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'page2Button',label:'Page 2',icon:'page',section:'global',
            tooltip:'Go to page 2', type: 'button'}">
        </button>
    </div>

You've declared an app bar. Next, let's style it.

  1. Open your default.css file and, at the beginning of the file, create a CSS style for the app bar that changes its background color.
#appbar {
    background-color: #03abe2;
}

Now let's attach our event handlers so that the buttons do something.

  1. In default.js, after the activated event handler, create two event handlers named goToHomePage and goToPage2.
    function goToHomePage(eventInfo) {
        WinJS.Navigation.navigate("/pages/home/home.html");
    }
    
    function goToPage2(eventInfo) {
        WinJS.Navigation.navigate("/pages/page2/page2.html");       
    }
  1. Create two variables named homeButton and page2Button. Define them before the activated event handler. We'll use these buttons to access our navigation commands later.
(function () {
    "use strict";
 
    WinJS.Binding.optimizeBindingReferences = true;
 
    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;
    var nav = WinJS.Navigation;
 
    // Create variables for the navigation buttons.
    var homeButton, page2Button;
  1. In the activated event handler, retrieve the app bar and use its getCommandByID method to retrieve the navigation buttons. Store them in your homeButton and page2Button variables and attach your event handlers.
    app.addEventListener("activated", 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.
            }
 
            // Save the previous execution state.
            WinJS.Application.sessionState.previousExecutionState =
                args.detail.previousExecutionState;
 
            if (app.sessionState.history) {
                nav.history = app.sessionState.history;
            }
            args.setPromise(WinJS.UI.processAll().then(function () {
 
                // Retrieve the app bar.
                var appbar = document.getElementById("appbar").winControl;
 
                // Attach event handlers to the command buttons.
                homeButton = appbar.getCommandById("homeButton");
                homeButton.addEventListener("click", goToHomePage, false);
                page2Button = appbar.getCommandById("page2Button");
                page2Button.addEventListener("click", goToPage2, false);
 
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }            
               
            }));
        }
    });
  1. Run the app. Now you can use the app bar to navigate between the two pages.
  2. We're almost done. Notice that you can click the home page button when you're viewing home.html. Let's add some code to disable the navigation buttons when they don't do anything useful. One easy way to update the buttons is to use the navigated event.

Define a navigated event handler named navigatedHandler. In your handler, use the detail.location property to find out which page the app navigated to. When the app navigates to home.html, disable the home button and enable the page 2 button. When the app navigates to page2.html, enable the home button and disable the page 2 button.

(Because we created variables for the home and page 2 buttons in an earlier step, we don't have to retrieve them every time we handle the event.)

    function navigatedHandler(eventInfo) {
        if (eventInfo.detail.location === "/pages/home/home.html") {
            homeButton.disabled = true;
            page2Button.disabled = false;
        }
        else if (eventInfo.detail.location === "/pages/page2/page2.html") {
            homeButton.disabled = false;
            page2Button.disabled = true;
        }
    }
  1. In your activated event handler, use WinJS.Navigation.addEventListener function to register the navigatedHandler function you just created. Add this code after you attach your event handlers to your app bar buttons.
            args.setPromise(WinJS.UI.processAll().then(function () {

                // Retrieve the app bar.
                var appbar = document.getElementById("appbar").winControl;
 
                // Attach event handlers to the command buttons.
                homeButton = appbar.getCommandById("homeButton");
                homeButton.addEventListener("click", goToHomePage, false);
                page2Button = appbar.getCommandById("page2Button");
                page2Button.addEventListener("click", goToPage2, false);
 
                WinJS.Navigation.addEventListener("navigated", navigatedHandler, false);
 
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }            
               
            }));
  1. Run the app again. Now the app bar only shows valid navigation commands.

Summary

Congratulations, you're done with the third tutorial! You learned how to create a project that uses the Navigation App template and how to use PageControl objects. You also learned how to create an app bar.

See the complete code

Did you get stuck, or do you want to check your work? If so, see complete code.

This tutorial is brought to you by the team at MSDN. To learn more about coding for Windows Store apps, please visit http://dev.windows.com

HTML5 Video Resources

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

HTML5 Partners

United States United States
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 13 Feb 2013
Article Copyright 2013 by HTML5 Partners
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid