Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

Windows 8 JavaScript Metro Application–Getting Started

4.98/5 (24 votes)
12 Mar 2012Ms-PL12 min read 126K   6.5K  
Getting Started with Windows 8 Metro App Development in JavaScript

Introduction

Couple of mounths ago I wrote about My First Windows 8 Application – Metro Puzzle when using Windows 8 Developer Preview, since than things has changed.

338916/Windows_8.png

In this article I'll show the new Windows 8 JavaScript development process and couple of features for Metro apps.

  • Getting Started
  • Settings
  • MessageBox
  • Splash
  • Asynchronous Programming & App Storage 

Related Links 

Background

Over the past couple of months I’ve built Applications and Games for Windows 8.

This was an amazing experience especially when I built everything in JavaScript, as you know Windows 8 allow you to build metro application in:

  • C++ and XAML
  • C# and XAML
  • JavaScript and HTML5

The first step is focusing on the main structure and basics of JavaScript Grid Application and then I’ll drill down to more features in Windows 8.

338916/windows_8_-_platform_and_tools.png

Getting Started

Step 1: Basics

When writing Windows 8 JavaScript Style App you might want to learn a little bit on WinJS and basic actions already available in Windows 8 JavaScript App, I saw posts about integrating JQuery to Windows 8 JavaScript applications, this is not necessary, WinJS offers many of those:

Selectors:

  • document.querySelector(".headerTemplate")
  • document.querySelectorAll("div")

Text

  • document.querySelector(“#Title”).textContent;

Animation

  • WinJS.UI.Animation.fadeIn(document.querySelector(“div”));

And more…

Step 2: Application Styles

When you open a new JavaScript metro app in Visual Studio 11 you can choose from the following:

  • Blank Application – A single-page project for windows metro style app that has no predefined controls or layout.
338916/1.png
  • Split Application - A two-page project for windows metro style app that navigates among grouped items. The first page allows group selection while the second display an item list alongside details for the selected item.
338916/2.png
  • Fixed Layout Application – A project for windows metro style app that scales a fixed aspect ratio layout.
338916/4.png
  • Navigation Application – A project for a windows metro style app that has predefined controls for navigation.
338916/5.png
  • Grid Application – A multi-page project for windows metro style app that navigates among groups of items. Dedicated pages display group and item details.
338916/6.png

for this demo I’ve created new Grid Application:

338916/9.png

Step 3: Project Structure

In previous versions of Visual Studio 11 and Windows 8 there were a JS folder with all WinJS files, in the new version all necessary files under the References in two main files:

  • base.js
  • ui.js
Beside that when creating Grid Application you will have three pages:
  • groupDetailsPage
  • groupedItemsPage
  • itemDetailsPage
Notice that each Html page have its own Css and JavaScript file, there is no naming convention that automatically takes those and combine but in order to have some order in or application this is the best practice on how to build pages in Windows 8 JavaScript application.

338916/10.png

Step 4: Application Flow

Everything starts from default.html, this page loaded all necessary js files and css files and using the PageControlNavigator it navigate the application to groupedItemsPage.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application1</title>
 
    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="http://www.codeproject.com/Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="http://www.codeproject.com/Microsoft.WinJS.0.6/js/ui.js"></script>
 
    <!-- Application1 references -->
    <link href="http://www.codeproject.com/css/default.css" rel="stylesheet">
    <script src="http://www.codeproject.com/js/data.js"></script>
    <script src="http://www.codeproject.com/js/navigator.js"></script>
    <script src="http://www.codeproject.com/js/default.js"></script>
</head>
<body>
    <div id="contenthost" 
         data-win-control="Application1.PageControlNavigator" 
        data-win-options="{home: '/html/groupedItemsPage.html'}"></div>
</body>
</html> 

The groupedItemsPage loaded the relevant JS/CSS files.

<title>groupedItemsPage</title>

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

  <link href="http://www.codeproject.com/css/default.css" rel="stylesheet">
  <link href="http://www.codeproject.com/css/groupedItemsPage.css" rel="stylesheet">
  <script src="http://www.codeproject.com/js/data.js"></script>
  <script src="http://www.codeproject.com/js/groupedItemsPage.js"></script>

This flow apply to each page you load.

Step 5: Page Definition

Now, after I’ve navigate to my page, how I can define what to display?

Instead register to Navigation event – navigated in groupedItemsPage.js and write this condition in each page:

(We only wants to do some actions to our page if it’s the right one…

if (e.location === '/html/groupedItemsPage.html')

For each page you load you need to define what should happened when you navigate to him, using WinJS.UI.Pages.define method you can register to each page events.

use strict:

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a "strict" operating context. This strict context prevents certain actions from being taken and throws more exceptions.

Strict mode helps out in a couple ways:

  • It catches some common coding bloopers, throwing exceptions.
  • It prevents, or throws errors, when relatively "unsafe" actions are taken (such as gaining access to the global object).
  • It disables features that are confusing or poorly thought out.
 (function () {
    "use strict";
 
    var appView = Windows.UI.ViewManagement.ApplicationView;
    var appViewState = Windows.UI.ViewManagement.ApplicationViewState;
    var nav = WinJS.Navigation;
    var ui = WinJS.UI;
    var utils = WinJS.Utilities;
 
    ui.Pages.define("/html/groupedItemsPage.html", {
        itemInvoked: function (eventObject) {
            //User Click
        },
        ready: function (element, options) {
            // Page Loaded
        },
        updateLayout: function (element, viewState) {
            //Layout Changed
        }
    });
})(); 

Another syntax to perform Page definition and use global methods ( right now each method or variable you define in Ready or any other method is only part of that function and it’s not visible to others) inside that strict mode is like that:

 (function () {
    "use strict";
 
    var appView = Windows.UI.ViewManagement.ApplicationView;
    var appViewState = Windows.UI.ViewManagement.ApplicationViewState;
    var nav = WinJS.Navigation;
    var ui = WinJS.UI;
    var utils = WinJS.Utilities;
 
    function ready(element, options) {
    }
 
    function itemInvoked(eventObject) {
    }
 
    function updateLayout(element, viewState) {
    }
 
    ui.Pages.define("/html/groupedItemsPage.html", {
        itemInvoked: itemInvoked,
        ready: ready,
        updateLayout: updateLayout
    });
})(); 

Step 6: Namespaces & Classes

Some of you might think this is a new feature in JavaScript but No, even without WinJS you can write Namespace in JavaScirpt, using WinJS.Namespace and WinJS.Class you can define Namespaces and Classes very easily.

WinJS.Namespace.define("data", {
    web: WinJS.Class.define({
        load: loadRoamingData,
        save: saveRoamingData
    }),
    local: WinJS.Class.define({
        load: loadLocalData,
        save: saveLocalData
    }),
    items:groupedItem
}); 
Now, from everywhere in my code I can call it like that:

data.web.load() or getting items –> data.items

Step 7: WinJS.UI.ListView

ListView is just one of the controls coming with WinJS, ListView Displays data items in a customization list or grid.

Define ListView in your Html page is easy, you define WinJS.UI.ListView value in data-win-control attribute inside a div element.

<div class="groupeditemslist" aria-label="List of groups" 
data-win-control="WinJS.UI.ListView" 
data-win-options="{ selectionMode: 'none' }"></div> 

Populate the data inside is also a simple task, in groupedItemsPage.js define under ready

event to take the groupeditemslist wincontrol, and using our previous namespace takes

data.items as datasource to our list.

 ready: function (element, options) {
     var listView = element.querySelector(".groupeditemslist").winControl;
     //Also Possible –> listView.itemDataSource = data.items.dataSource
     ui.setOptions(listView, {
         itemDataSource: data.items.dataSource
     });
}, 

Step 8: Binding and Templates

Now after you define your listview datasource, how to define the display of each item?

Think our data.items contains a list of item object that has title, subtitle and a backgroundImage.

In our groupedItemsPage.html page we define another WinJS control called - WinJS.Binding.Template, inside that control you need to add additional attribute for each child element called - data-win-bind and define the path for binding.

 <!-- These templates are used to display each item in the ListView declared
 below. -->
<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
    <img class="item-image" src="#" data-win-bind="src: backgroundImage;
  alt: title" />
    <div class="item-overlay">
        <h4 class="item-title" data-win-bind="textContent: title"></h4>
        <h6 class="item-subtitle win-type-ellipsis" 
                            data-win-bind="textContent: subtitle"></h6>
    </div>
</div> 

One more thing, you need to set this template as itemTemplate for our listview:

 ready: function (element, options) {
     var listView = element.querySelector(".groupeditemslist").winControl;
            
     ui.setOptions(listView, {
         itemDataSource: data.groups.dataSource,
         itemTemplate: element.querySelector(".itemtemplate")                
     });           
},  

Add Settings

338916/settings.png

In order to help you get inside Windows 8 Metro Apps I’ll start writing on specific features in Metro Apps, starting from Settings.

As you build your Metro App you probably need to let the user to change some settings in the application, you don’t need to write any things special for that because Win8 comes with integrated Settings Pane allow you to add your own settings.

This is a very simple task, just define your settings as Pages, for example:

  • Help Page
  • About
  • Dummy 1
  • etc…

(Remember – Each page should have it’s own CSS and JS file, don’t be lazy!)

before we can get started you need to build new JavaScript application, the second part is getting inside the default.js file and register to onsettings event:

 app.onactivated = function (eventObject) {
    if (eventObject.detail.kind === Windows.ApplicationModel.
                             Activation.ActivationKind.launch) {
      WinJS.UI.processAll();
      // app.addEventListener("settings", function (e) { Load Settings });
      // OR       
      app.onsettings = loadSettings;
    }
}; 

I’ve created a new folder in my project called – “Settings”, inside I’ve create 2 pages – Help and About.

338916/settings2.png

Now, you need to register those pages to the application onsettings event, and make sure to use the flyout control to populate those setting.

 function loadSettings(e) {
    e.detail.applicationcommands =
    {
        "Help":
            {
                title: "Help",
                href: "/Settings/Help.html"
            },
        "About":
            {
                title: "About Me",
                href: "/Settings/About.html"
            }
    };
    WinJS.UI.SettingsFlyout.populateSettings(e);
} 

Now, how can I see my settings? There are three ways to see them:

User perform a proper gesture to open the setting page.

  1. Call the Setting Pane using SettingsPane Windows.UI.ApplicationSettings.SettingsPane.show();
  2. Call specific page using SettingsFlyout (using id and path)
  3. WinJS.UI.SettingsFlyout.showSettings("Help", "/Settings/Help.html");
 document.querySelector("#btnShowSettings").addEventListener
("click", function (e) {
    Windows.UI.ApplicationSettings.SettingsPane.show();
});
 
document.querySelector("#btnHelp").addEventListener("click", function () {
    WinJS.UI.SettingsFlyout.showSettings("Help", "/Settings/Help.html");
});
 
document.querySelector("#btnAbout").addEventListener("click", function () {
    WinJS.UI.SettingsFlyout.showSettings("About", "/Settings/About.html");
}); 

338916/settings3.png

Message Dialog

I’ve already saw people who wrote overlay div to show messages, just because they didn’t find how to add more buttons for the Message Dialog, so let me show you how:

Using the WinRT you can use the MessageDialog to popup a message to the user:

var msg = new Windows.UI.Popups.MessageDialog("Message Content",                                                   "Your Message Title");msg.showAsync(); 

338916/msg1.png

For more complex dialog message that contains more then one button and can perform a different action based on user choice, you need to append new UICommand inside the MessageDialog object.

var msg = new Windows.UI.Popups.MessageDialog("Message Content", "Your Message Title");
//Add buttons and set their callback functions
msg.commands.append(new Windows.UI.Popups.UICommand("OK",
    function (command) {
        writeMsg("You Clicked Ok");
    }));
msg.commands.append(new Windows.UI.Popups.UICommand("Cancel",
    function (command) {
        writeMsg("You Clicked Cancel");
    }));
msg.showAsync(); 

Now, there are some options for the message box, such as what should happened when the user click “ESC” or what should by the focus button?

beside that you probably want to create a function that will handle the user action instead of writing the function inside the button (the example above).

function showMsg() {
    var msg = new Windows.UI.Popups.MessageDialog("Message Content",
        "Your Message Title");
    //Add buttons and set their callback functions
    msg.commands.append(new Windows.UI.Popups.UICommand("OK", actionHandler, 0));
    msg.commands.append(new Windows.UI.Popups.UICommand("Cancel", actionHandler, 1));
    msg.commands.append(new Windows.UI.Popups.UICommand("Ignore", actionHandler, 2));
    //Set the command to be invoked when a user presses ESC
    msg.cancelCommandIndex = 1; //Set the command that will be invoked by default
    msg.defaultCommandIndex = 1; msg.showAsync();
} function actionHandler(command) {
    writeMsg(command.label);
    //Create action for each button.
    switch (command.id) {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
    }
}  

338916/msg2.png

Splash Screen

When creating an Win 8 Metro App you define you splash screen with a single image, what if you want to do more… For example show some animations, load your resources before the application loads and more.

In my demo I’ll display some canvas animation and a timer (from 10s) until going to the main page.

Live Video

338916/s4.png

Define Startup Page

When you start your application the first thing the user will see is this:

338916/s1.png

There are two ways to remove it before we can set our custom splash screen:

338916/s2.png

  1. Because you can’t remove the splash image (it’s required), you can replace the image with your own empty image.
  2. The second option is to define the background color to white – this will show an white page with not clock.

After we remove the default splash image we want to define our Splash.html page as startup page as follow:

338916/s3.png

Splash.html

 <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Splash</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>
    <link href="/css/default.css" rel="stylesheet">
    <link href="/css/splash.css" rel="stylesheet">
    <script src="/js/splash.js"></script>
    <script src="/js/shadebob.js"></script>
</head>
<body onload="resize()">
    <div id="screen" style="display: none">
        <canvas id="shadebob">
        </canvas>
    </div>
    <div id="holder">
        This is my Custom Splash...
        <h1 id="timer">
        </h1>
    </div>
</body>
</html> 

Splash.css

The first thing we want is to place our custom splash in the center of the screen, so I’ve defined our holder div to absolute position, I’ve also centered the timer div.

 #body
{
    position: absolute;
    top: 0%;
    left: 0%;
    height: 100%;
    width: 100%;
    margin: 0px;
    padding: 0px;
}
#holder
{
    position: absolute;
}
h1
{
    font-size: 180px;
    font-weight: 600;
}
#timer
{
    text-align: center;
} 

Splash.js

Because we changed the startup page we now need to set the splash.js to start the application, this allows us to hook the onactivated event from he application.

You can see the onactivated is fired you get the eventObject that has the splashScreen object inside detail.

From the splash object we take the imageLocation to get the x,y width and height of the original splash screen, the reason for doing that is to set our canvas and timer display in the center exactly where the splash should be, you don’t have to do that you can also calculate based on the document width and height.

After we obtain those values we set our holder div at the same position and size as the original splash screen, and we start the timer by calling setInterval to call countDown function.

The countDown function reduce 1 from the waitFor object until is reached to 0 and then he will redirect the page to homePage.

Now to the important part the dismissed event, as you can see I’ve register to the dismissed event from the splash screen so I can know when the splash has dismissed, in my demo this event is not relevant because my splash is dismissed the second I place the image on the screen but for you it can be loading event are something else.

 (function () {
    "use strict";
    var waitFor = 10;
    var app = WinJS.Application;
    // This function responds to all application activations.
    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            // Retrieve splash screen object
            var splash = eventObject.detail.splashScreen;
            // Retrieve the window coordinates of the splash screen image.
            var coordinates = splash.imageLocation;
            // Position the extended splash screen image in the same location as the splash screen image.
            var holder = document.querySelector("#holder");
            holder.style.left = coordinates.x + "px";
            holder.style.top = coordinates.y + "px";
            holder.style.height = coordinates.height + "px";
            holder.style.width = coordinates.width + "px";
            countDown();
            setInterval(countDown, 1000);
            // Register an event handler to be executed when the splash screen has been dismissed.
            splash.addEventListener("dismissed", onSplashScreenDismissed, false);
            WinJS.UI.processAll();
        }
    };
    app.start();
    function countDown() {
        waitFor = waitFor - 1;
        if (waitFor <= 0) {
            location.href = "/html/homePage.html";
        }
        else
            document.querySelector("#timer").innerHTML = waitFor;
    }
    function onSplashScreenDismissed() {
        // Include code to be executed when the system has transitioned
        // from the splash screen to the application's first view.
    }
})(); 

Asynchronous Programming & App Storage

In this demo I’m going to talk about WinJS.xhr that makes an XMLHttpRequest as a Promise, and how to save and load images and from your local storage.

What is a Promise?

Promise is a way for Asynchronous Programming in JavaScript, Avoiding synchronous execution in single-threaded languages like JavaScript is necessary in order to create apps that are responsive and high performing. Windows Library for JavaScript provides a consistent and predictable mechanism called a Promise that simplifies asynchronous programming.

A promise implements a method for registering callbacks for state change notifications, named then.

Instead of writing a single get action that force your code to wait for response.

var result = myWebService.get(http://www.contoso.com); 

Or if you think about writing more code like this:

myWebService.addEventListener('completed', 
function(result)  { /* do something */});
myWebService.get(http://www.contoso.com); 

You should use WinJS Promise to create Asynchronous action using then method.

 myWebService.get("http://www.contoso.com")
    .then(
       function (result) { /* do something */ },
       function (error) { /* handle error */ },
       function (progress) { /* report progress */ }
); 

For this demo I wrote a simple JavaScript Metro App that downloads web images and saves them to local storage and a gallery that display all images under App local storage.

338916/a1.png

Asynchronous Image Download

We need to download the image asynchronously, then save the stream we get to a local file.

The first thing is the folder location, Each application has three available folders under - Windows.Storage.ApplicationData.current - to save user data:

  • Local
  • Temp
  • Roaming

Now using WinJS.xhr we enter the image url and define the response type as “Blob”, again this is an Asynchronous method and we can use THEN in order to register a callback that will called when the request is finished.

Once we get the result from Xhr we use the folder object to create new file with the name the user passed, again once the new file was created we open this file for editing and get the stream.

Code Flow:

  1. Using Ajax get the image = WinJS.xhr({ url: imgUrl, responseType: "blob" }).then
  2. After we received the image we create a new file = folder.createFileAsync(imgName,..).then
  3. Open our file for edit = file.openAsync(Windows.Storage.FileAccessMode.readWrite).then
  4. Copy the image content = copyAsync(blob.msDetachStream(), stream).then
  5. Close stream = stream.flushAsync().then
 function download(imgUrl, imgName) {
return WinJS.xhr({ url: imgUrl, responseType: "blob" }).then(function (result) {
    var blob = result.response;
    return folder.createFileAsync(imgName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) {
        // Open the returned file in order to copy the data
        return file.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (stream) {
            return Windows.Storage.Streams.RandomAccessStream.copyAsync(blob.msDetachStream(), stream).then(function () {
                // Copy the stream from the blob to the File stream
                return stream.flushAsync().then(function () {
                    stream.close();
                });
            });
        });
    });
}, function (e) {
    var msg = new Windows.UI.Popups.MessageDialog(e.message);
    msg.showAsync();
});
} 

Locate Local File

After the download has completed we want to locate the local file we just saved and return the file object, using the file object we can get the file type ,creation date and more.

Using Windows.Storage.ApplicationData.current.local.getFileAsync (or Temp, Roaming") , we can search for a specific file under that folder, if the file is found in the local folder we return the file, else return null. (File Not Found)

 function fileExists(fileName) {
    return folder.getFileAsync(fileName).then(function (file) {
        return file;
    }, function (err) {
        return null;
    });
} 

Add imgDownloader Namespace

In order to call these methods from Default.js we need to add the namespace using the following methods:

 WinJS.Namespace.define('imgDownloader', {
    download: download,
    fileExists: fileExists
}); 

Add Page Functionality

Now, when the user writes the image Uri and clicks the “Get Image” button we’ll call the getImage function.

 app.onactivated = function (eventObject) {
if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
    // TODO: Initialize your application here.
    WinJS.UI.processAll();
    document.querySelector("#btnDownloadImg").addEventListener("click", function () {
        getImage();
    });
    getImage();
}; 

Using querySelector we’ll take the Uri and File Name values, using the imgDownloader namespace and call Download function using the user supplied values. Because the download function has a callback value we can use the then method.

After the download operation has completed we call the fileExists function to get the local file object.

After getting the Image we demonstrate two options for displaying the image:

  1. ms-appdata:// protocol – Path To Local Folder
  2. URL.createObjectURL – Converting the file to blob, you can choose to create permanent blob so you can use it again.
function getImage() {
var imgUrl = document.querySelector("#txtUrl").value;
var fileName = document.querySelector("#txtFileName").value;
imgDownloader.download(imgUrl, fileName).then(function () {
    imgDownloader.fileExists(fileName).then(function (file) {
 document.querySelector("#mainImg").src = URL.createObjectURL(file, false);
 // using the ms-appdata:// protocol.
 document.querySelector("#mainImg2").src = "ms-appdata:///Local/" + fileName;
 document.querySelector("#filePath").textContent = "Path: " + file.path;
 document.querySelector("#fileType").textContent = "Display Type: " + file.displayType;
 document.querySelector("#dateCreated").textContent = "Date Created: " + file.dateCreated;
       drawGallery();
    }, function (err) {
        var msg = new Windows.UI.Popups.MessageDialog("Picture Not Found");
        msg.showAsync();
    });
});
} 

The last thing I want to do is locate all files under my local folder and display all image files.

Again we’ll use Windows.Storage.ApplicationData.current.localFolder but now let’s call “getItemsAsync” to get all files, then forEach over these items and make sure to handle only images, convert each file to a blob using createObjectURL and add the image to our gallery div.

function drawGallery() {
Windows.Storage.ApplicationData.current.localFolder.getItemsAsync().then(function (items) {
    var div = document.querySelector("#existingFiles");
    div.textContent = "";
    items.forEach(function (storageItem) {
        if (storageItem.fileType === ".png" || storageItem.fileType === ".jpg" || 
            storageItem.fileType === ".jpeg") {
            var image = document.createElement("img");
            image.style.width = "100px"
            image.style.height = "100px"
            image.src = URL.createObjectURL(storageItem);
            image.alt = image.src;
            div.appendChild(image);
        }
    }, function (e) {
        var msg = new Windows.UI.Popups.MessageDialog(e);
        msg.showAsync();
    });
});
} 

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)