Toen Microsoft aankondigde dat de applicaties die gebruik maken van de Metro interface gemaakt kunnen worden in Javascript en HTML5 was de wereld verrast. Veel developers zijn bekend met het .Net framework en kunnen op dit moment al op een snelle en goede manier ‘Metro style’ applicaties bouwen voor Windows Phone 7 doormiddel van Silverlight en de kennis die ze hebben opgebouwd over het .Net framework in andere soorten applicaties zoals Winforms, WPF of ASP.NET. Toch heeft Microsoft besloten om naast de developers die Silverlight en .Net kennis hebben ook een nieuwe groep developers de kans te geven om applicaties te gaan bouwen voor Windows 8 door gebruik te maken van Javascript en HTML5.
In dit artikel geven we je uitleg over hoe je je eerste Metro applicatie voor Windows 8 kunt bouwen in Javascript en HTML. We zullen eerst uitleggen hoe de architectuur in elkaar steekt met de verschillen tussen de diverse applicatie architecturen en zullen daarna stap voor stap een beschrijving geven hoe je komt van een nieuw Visual Studio project tot een werkende applicatie.
De splitsing tussen de nieuwe Metro stijl applicaties en de oude Windows applicaties gaat door tot op een diep niveau in de architectuur van Windows. Dit is goed zichtbaar op afbeelding 1. In plaats van de bekende Win32 APIs te gebruiken heeft Microsoft ervoor gekozen om een compleet nieuwe laag te bouwen waarop de Metro style applicaties gebouwd kunnen worden. De laag die de brug vormt tussen je applicatie en het operating system is WinRT (Windows Runtime). De WinRT is een object georiënteerde vervanging van Win32 waarmee in verschillende talen gecommuniceerd kan worden zoals C++, C#, VB en ook Javascript.

Afbeelding 1: Windows 8 applicatie architectuur
De WinRT API lijkt erg op die van het .Net framework waardoor Silverlight applicaties simpel omgezet kunnen worden van het .Net framework naar WinRT door alleen het aanpassen van enkele namespaces. Een aantal API’s die aanwezig zijn in het .Net framework zijn echter niet aanwezig in WinRT, anderen zijn verplaatst en er zijn ook een aantal nieuwe API’s bijgekomen. Een voorbeeld hiervan is dat alle synchrone calls voor het openen van files of sockets zijn vervangen door asynchrone varianten. Dit alles moet zorgen voor betere performance en een user interface die nooit blokkeert omdat er een langdurige operatie wordt uitgevoerd. De Windows Runtime zorgt er ook voor dat elke applicatie in een sandbox omgeving draait en niet bij de gegevens van andere applicaties kan.
Alle applicaties die geprogrammeerd zijn in C, C++, C# of VB gebruiken XAML in de UI, in het geval van javascript wordt er HTML gebruikt. Javascript is zoals Microsoft het noemt een "First Class Citizen" binnen de WinRT en heeft dezelfde mogelijkheden binnen het OS als bijvoorbeeld een C# of VB Metro applicatie.
Metro applicaties gemaakt in Javascript kunnen gebruik maken van veel HTML5 features zoals het canvas element, SVG, video en audio en ook een aantal CSS 3 features zoals 2D en 3D transformaties, transities en animaties. De Javascript Metro applicaties zijn echter meer dan alleen een lokale website door gebruik te maken van de WinJS Library (Windows Libraries for Javascript). De WinJS library zorgt voor een aantal herbruikbare javascript en css bestanden die het simpeler maken om een Metro stijl applicatie te bouwen en geven toegang tot bronnen die normaal gesproken vanuit Javascript niet te benaderen zijn. Deze libraries worden standaard toegevoegd aan een nieuw project in Visual Studio als je een Javascript applicatie gaat bouwen.
In dit artikel zullen we je stap voor stap verschillende onderdelen van het ontwikkelen van Javascript Metro applicaties laten zien doormiddel van het bouwen van een voorbeeldapplicatie. We beginnen met het aanmaken van een nieuw project tot uiteindelijk werkende applicatie. De applicatie wordt een Twitter applicatie waarmee het onderwerp ‘#win8nl’ gevolgd kan worden en zal de laatste berichten die gepost zijn met dat onderwerp tonen.
Met de Windows 8 wordt de Developer preview van Visual studio 11 meegeleverd met alleen de templates voor het bouwen van ‘metro style’ applicaties. Standaard worden er templates meegeleverd voor Javascript, C#, VB en C++, waarin al zaken als layout en navigatie zijn geregeld. Voor een basis applicatie zijn de templates een leuke start om met Windows 8 development te beginnen en in de templates te zien hoe je bepaalde onderdelen gebouwd zijn. Voor de twitter applicatie die we als voorbeeld in dit artikel gaan bouwen gebruiken we de JavaScript ‘blank application’ template.

Afbeelding 2: Metro style project templates voor Javascript
Als we het project aanmaken en in de solution explorer kijken, dan heeft het project veel weg van een normaal web project. Er is een default.html file aangemaakt, er zijn mappen aangemaakt voor images, css en javascript. Het enige verschil met web projecten is de map ‘winjs’. Deze map bevat alle javascript files die nodig zijn om de Windows Runtime vanuit Javascript te kunnen aanspreken. Later tijdens het bouwen van de voorbeeld applicatie zullen we hier nog op terug komen.

Afbeelding 3: Solution explorer
De files die van belang zijn voor de voorbeeld applicatie zijn de default.html in de root van de solution en de default.js in de ‘js’ folder. De file Default.html is het startpunt van onze applicatie en bevat alleen html (inclusief de referenties naar de verschillende javascript files) Default.js werkt als een soort van ‘code-behind’ file voor onze html file. Hierin wordt de applicatielogica geschreven om onze applicatie te laten functioneren.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WinWebApp5</title>
<!-- WinJS references --></span>
<link rel="stylesheet" href="/winjs/css/ui-dark.css" />
<script src="/winjs/js/base.js"></script>
<script src="/winjs/js/wwaapp.js"></script>
<!-- WinWebApp5 references --></span>
<link rel="stylesheet" href="/css/default.css" />
<script src="/js/default.js"></script>
</head>
<body>
</body>
</html>
Codevoorbeeld 1: Default.html
(function () {
'use strict';
// Uncomment the following line to enable first chance exceptions.
// Debug.enableFirstChanceException(true);
WinJS.Application.onmainwindowactivated = function (e) {
if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
// TODO: startup code here
}
}
WinJS.Application.start();
})();
Codevoorbeeld 2: Default.js
Om onze applicatie te kunnen laten werken hebben we data nodig, de data die we gaan gebruiken halen we van twitter door gebruik te maken van de search API. Aan de hand van het onderwerp ‘#win8nl’ halen we de laatste tweets over dit onderwerp op in JSON formaat en tonen die in onze applicatie. Om data op te halen via de Twitter search API gebruiken we de volgende url:
http://search.twitter.com/search.json?q=%23win8nl
Om de basis van onze twitter applicatie neer te zetten voegen we een html div stateent met het id "results" toe aan de default.html. Vervolgens maken we de methode ‘getTweets’ aan in de default.js file. In deze methode doen we een asynchroon XML HttpRequest (aan te roepen door WinJS.xhr() te gebruiken) die de url aanroept en in de promiss (de gechainde .then() methode) de methode aanroept die de data verwerkt (processResults) of in het geval van een error de foutafhandeling start. (processError)
function getTweets() {
WinJS.xhr({ url: "http://search.twitter.com/search.json?q=%23win8nl" })
.then(processResults, processError);
}
function processResults(response) {
var results = JSON.parse(response.response);
var content = document.getElementById("results");
for (var i = 0; i < results.results.length; i++) {
var tweet = results.results[i];
var h2 = document.createElement("h2");
h2.innerText = tweet.from_user;
content.appendChild(h2);
var text = document.createElement("div");
text.innerText = tweet.text;
content.appendChild(text);
}
}
function processError(error) { }
Codevoorbeeld 3: Methode getTweets, processResults en processError
De methode processResults krijgt een parameter response mee, waarin de resultaten van de twitter opvraging zich bevinden. De response parsen we doormiddel van Json.Parse(). Vervolgens zoeken we in de html de div results op, zodat we daar straks het resultaat in kunnen plaatsen zodat het resultaat zichtbaar op het scherm wordt. Vervolgens loopen we door de tweets heen, maken per bericht de html op en voegen die daarna toe aan de ‘results’ div. Het enige wat we nu nog moeten doen is de method ‘getTweets’ aanroepen in het ‘onmainwindowactivated’ event om de basisversie van de voorbeeldapplicatie te kunnen starten.
WinJS.Application.onmainwindowactivated = function (e) {
if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
getTweets();
}
}
Codevoorbeeld 4: onmainwindowactivated
Als we de applicatie nu starten krijgen we een lijst met usernames en twitter berichten te zien. Dit is natuurlijk een leuke en snel gemaakte basis maar het opbouwen van html code in javascript willen we het liefst vermijden. Hoe zorgen we ervoor dat we de logica en de markup kunnen scheiden?
Voor het scheiden van user interface markup en logica kan gebruik gemaakt worden van databinding en templates. We beginnen met de template. Als eerste voegen we een aantal referenties toe naar de files uit de ‘winjs’ folder
<script src="/winjs/js/ui.js" type="text/javascript"></script>
<script src="/winjs/js/binding.js" type="text/javascript"></script>
<script src="/winjs/js/controls.js" type="text/javascript"></script>
<script src="/winjs/js/animations.js" type="text/javascript"></script>
<script src="/winjs/js/uicollections.js" type="text/javascript"></script>
Codevoorbeeld 5: WinJs referenties
Daarna definiëren we de datatemplate in de default.html file onder de div ‘results’
<div id="tweetTemplate" data-win-control="WinJS.Binding.Template">
<div>
<h2 data-win-bind="innerText: user"></h2>
<img data-win-bind="src: image"/>
<span data-win-bind="innerText: text"></span>
</div>
</div>
Codevoorbeeld 6: WinJs datatemplate
Nu beginnen we ook echt de WinJs library in actie te zien komen. In de template komen we de attributen "data-win-control" en "data-win-bind" tegen. Met ‘data-win-control’ geven we aan dat de html tag en onderliggende markup geïnterpreteerd moet worden als een winjs control en in dit specifieke geval een template. Doormiddel van de ‘data-win-bind’ attributen geef je aan welk attribuut er aan welke property van een object gekoppeld wordt. In onze template gebruiken we de twitter username en het bericht die we aan de innerText van de resp. H2 tag en de div koppelen. Aan de image tag koppelen we twee properties, namelijk de src en de alt tag. Meerde properties kunnen gekoppeld worden door een puntkomma te plaatsen tussen de combinatie van tag en property.
In de default.js file moeten we de methode ‘getTweets’ aanpassen, zodat de markup wordt gecontroleerd op winjs controls. Dit doen we door de ‘WinJs.UI.ProcessAll’ aan te roepen, en in de promiss (Omdat dit allemaal asynchroon gebeurd willen we pas data ophalen op het moment dat alle controls zijn geïnterpreteerd) de data op te halen.
function getTweets() {
WinJS.UI.processAll().then(function (e) {
WinJS.xhr({ url: "http://search.twitter.com/search.json?q=%23win8nl" })
.then(processResults, processError);
});
}
Codevoorbeeld 7: Bijgewerkte methode getTweets
Het enige wat we nog moeten doen is de ‘processResults’ methode aanpassen, zodat we niet meer zelf de markup gaan opbouwen, maar de data koppelen aan de datatemplate. De code hiervoor vind je hieronder
function processResults(response) {
var results = JSON.parse(response.response);
var content = document.getElementById("results");
for (var i = 0; i < results.results.length; i++) {
var tweet = results.results[i];
var template = document.getElementById("tweetTemplate").winControl;
template.render({ user: tweet.from_user,
image: tweet.profile_image_url,
text: tweet.text }, content);
}
}
Codevoorbeeld 8: Bijgewerkte processResults methode
In het vorige code sample kun je zien dat we de template opzoeken doormiddel van een getElementById met het Id van de template en daarna de property winControl uitvragen, zodat we het object terug krijgen wat door WinJs te gebruiken is om data aan te binden. Na het ophalen van de datatemplate als WinJs control, zeggen we dat de template gerendered moet worden en geven daar de velden uit het tweet object aan mee + de div results waar het resultaat aan toegevoegd wordt.
Als we nu de applicatie starten, wordt er een lijst getoond met daarin de profielafbeeldingen, naam van de twitteraar en het bericht. Dit ziet er al een stuk beter uit dan de eerste variant.
Nog mooier zou zijn als we een control aan onze html pagina kunnen toevoegen, en daar een lijst met objecten aan kunnen binden i.p.v. per item een template vullen en deze aan de ‘results’ div toevoegen.De volgende stap in onze voorbeeldapplicatie wordt binden van data aan een control uit de WinJs bibliotheek namelijk de ListView. Om de ListView te kunnen gebruiken voegen we de volgende div toe aan de default.html pagina:
<div id="tweetList"
data-win-control="WinJS.UI.ListView"
data-win-options="{layout:{type : WinJS.UI.ListLayout}}"
style="height:1000px;"> </div>
Codevoorbeeld 9: WinJs ListView
De div ‘results’ wordt vanaf nu niet meer gebruikt en kan verwijderd worden uit de html pagina. De volgende stap is het aanpassen van de processResults methode zodat de tweets in een array worden geplaatsten vervolgens aan de ListView gebind wordt.
function processResults(response) {
var results = JSON.parse(response.response);
var content = document.getElementById("results");
var tweets = [];
for (var i = 0; i < results.results.length; i++) {
var tweet = results.results[i];
tweets.push({ user: tweet.from_user,
image: tweet.profile_image_url,
text: tweet.text });
}
var template = document.getElementById("tweetTemplate");
var listview = document.getElementById("tweetList").winControl;
listview.itemRenderer = template;
listview.dataSource = tweets;
}
Codevoorbeeld 10: processResults met databinding aan ListView
In de aangepaste processResults methode zie je dat de datatemplate nog steeds gebruikt wordt. Deze wordt aan de ListView meegegeven zodat de ListView weet hoe de items op het scherm gerenderd moeten worden.
Als we na de aanpassingen de applicatie starten zien we gelijk dat de lijst dynamischer is geworden. Items lichten op als je er over heen gaat met de muis en kunnen geselecteerd worden.
In veel van de Windows 8 metro style applicaties wordt er gebruik gemaakt van de appbar, de balk onderin de applicatie met daarin een aantal knoppen. De appbar wordt zichtbaar door het klikken op de rechtermuisknop. Ook in onze twitter applicatie zou het handig zijn om een appbar te hebben met bijvoorbeeld een refresh knop. Ook de appbar is een van de controls in de WinJs bibliotheek. Om de appbar met de refresh knop aan onze app toe te voegen moeten we drie stappen doorlopen. De eerste is de appbar control toevoegen aan onze html pagina.
<div id="appbar"
data-win-control="WinJS.UI.AppBar"
data-win-options="{position:'bottom', transient:true, autoHide:0, lightDismiss:false}">
<div class="win-left">
<button aria-labelledby="lbl-cmd-new" id="btn-refresh" class="win-command">
<img class="win-spritestates" alt="Refresh" src="/images/refresh.png" />
<br />
<span id="Span1" class="win-label">Refresh</span>
</button>
</div>
</div>
Codevoorbeeld 11: Appbar template
Vervolgens voegen we het plaatje refresh.png toe aan de map ‘images’. Het enige wat ons nog rest is het koppelen van het click event op de refresh knop aan het uitvoeren van de methode ‘getTweets’. Dit doen we na het op de ‘onmainwindowactivated’ event en het initieel ophalen van de twitter berichten.
WinJS.Application.onmainwindowactivated = function (e) {
if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
getTweets();
var refreshButton = document.getElementById("btn-refresh");
refreshButton.addEventListener('click', getTweets , false);
}
}
Codevoorbeeld 12: Click event koppelen aan de appbar refresh button
Voor het tonen van de appbar hoeven we verder niets te doen, omdat deze als wincontrol staat gemarkeerd en al wordt opgepakt in de ‘processall’ methode.
Start de applicatie en wacht tot de lijst geladen is, stuur een nieuw twitter bericht met hashtag #win8nl, klik met de rechtermuisknop in de applicatie om de appbar te activeren en klik vervolgens op de refresh knop en zie het resultaat verversen.
Om onze applicatie helemaal af te maken zou het leuk zijn als we net als dat we bij Windows Phone 7 kennen een live tile hebben. En dan vooral als we de gebruiker kunnen laten kiezen tussen een brede of een smalle tile. Op dit moment kunnen we niet zelf een custom tile aanmaken waarbij we zelf bepalen waar tekst en afbeeldingen terecht komen, maar alleen een keuze maken uit de voor gedefinieerde templates. Op http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.notifications.tiletemplatetype kun je alle templates bekijken.
function processResults(response) {
var results = JSON.parse(response.response);
var content = document.getElementById("results");
var tweets = [];
for (var i = 0; i < results.results.length; i++) {
var tweet = results.results[i];
tweets.push({ user: tweet.from_user,
image: tweet.profile_image_url,
text: tweet.text });
}
var template = document.getElementById("tweetTemplate");
var listview = document.getElementById("tweetList").winControl;
listview.itemRenderer = template;
listview.dataSource = tweets;
var Notifications = Windows.UI.Notifications;
Notifications.TileUpdateManager.createTileUpdaterForApplication().clear();
var tileXml = Notifications.TileUpdateManager.getTemplateContent
(Notifications.TileTemplateType.tileWideSmallImageAndText04);
var tileTexts = tileXml.getElementsByTagName("text");
tileTexts[0].appendChild(tileXml.createTextNode(tweets[0].user));
tileTexts[1].appendChild(tileXml.createTextNode(tweets[0].text));
var tileImages = tileXml.getElementsByTagName("image");
tileImages[0].setAttribute("src", tweets[0].image);
var tileNotification = new Notifications.TileNotification(tileXml);
Notifications.TileUpdateManager.createTileUpdaterForApplication().
update(tileNotification);
}
Codevoorbeeld 13: processResults inclusief aanmaken van livetile
In het bovenstaande code voorbeeld maken we gebruik van de ‘TileUpdateManager’ om in eerste instantie bestaande tiles weg te halen door het aanroepen van de ‘clear’ methode.
Na het verwijderen van de bestaande tile(s) vragen we via de methode ‘getContentTemplate’ de xml op van de template ‘TileWideSmallImageAndText04’. Als eerste gaan we de tekst velden van de template vullen. Deze tekstvelden zoeken we op door naar de tag ‘text’ te zoeken. In dit geval zijn er twee tekst velden die we gaan vullen met de naam van de twitteraar en zijn bericht. Vervolgens zoeken we het afbeelding veld op via de tag ‘image’ en vullen daar de src tag van. Nu we de xml ingevuld hebben maken we op basis van de xml een ‘TileNotification’ die we vervolgens aan de TileUpdatemanager teruggeven door middel van de ‘Update’ methode.
Het enige wat ons nog rest om de brede tiles te ondersteunen is het toevoegen van een background image die de juiste afmetingen heeft voor een wide tile. (310 x 150 pixels) Het opgeven van de wide tile image doen we door de package.appxmanifest te openen en op het eerste tabblad "Application UI" bij het veld ‘Wide Logo’ onze wide tile te selecteren.

Afbeelding 4: Application UI capabilities scherm
Als we nu de applicatie starten en deze afsluiten en naar het Windows 8 ‘home screen’ gaan, kunnen we daar met de rechtermuisknop onze applicatie selecteren. Bij het selecteren zal de Windows 8 appbar tevoorschijn komen met daarin de optie om de tile ‘Larger’ te maken.

Afbeelding 5: Windows 8 appbar met 'Larger' optie
Als we vervolgens op ‘Larger’ klikken dan krijgen we onze live tile te zien met aan de voorkant de wide tile die we in het ‘Application UI’ scherm gekozen hebben. Op de achterkant staat de livetile die we gemaakt hebben in de applicatie.

Beide kanten van de live tile
Hoewel navigatie binnen een applicatie niet helemaal in de voorbeeldapplicatie past is het toch een belangrijk onderdeel van het ontwikkelen van Javascript Metro applicaties. Navigatie binnen een Javascript Metro applicatie kun je doen via een hyperlink in je html code zoals bij websites maar dit heeft belangrijke consequenties. Waar bij je bij een website een server hebt die de state bij houd tussen de pagina’sdoormiddel van je session heb je dit lokaal in een Javascript Metro applicatie niet. Microsoft heeft daarom gekozen om extra hulpmiddelen te creëren die je helpen bij het navigeren tussen pagina’s. De manier van navigeren doormiddel van hyperlinks is een vorm van ‘multi-page navigation’. Microsoft heeft gekozen om ‘single-page navigation’ te implementeren waardoor je state altijd bewaard blijft omdat je applicatie technisch gezien altijd op dezelfde pagina blijft.
‘Single-page navigation’ binnen Javascript Metro applicaties wordt gedaan doormiddel van ‘fragments’. Een fragment is een nieuw html bestand dat aangemaakt kan worden door in Visual Studio ‘Add new item’ te kiezen en daarna new ‘HTML fragment’. Deze fragment pagina kan daarna dynamisch worden ingeladen in de html code doormiddel van functies in de WinJS.UI.Fragments namespace. De functie ‘WinJS.UI.Fragments.clone’ maakt een kopie van een fragment bestand aan in het geheugen zodat deze in de dom van de bestaande pagina geladen kan worden. Om te navigeren kun je een functie aanmaken zoals loadPage in codeblock [nummer]. De clone functie is een asynchrone functie die een promise als return waarde heeft. Als de clone functie klaar is neem je een div element en plaats je de gekregen fragment in de div doormiddel van de ‘appendChild‘ functie.
function loadPage(location, state) {
WinJS.UI.Fragments.clone(location, state).then(function (frag) {
// Set the contents of the "content" div.
var content = document.getElementById("content");
content.innerHTML = "";
content.appendChild(frag);
});
}
Codevoorbeeld 14: Laden van fragments
Nu de fragment geladen is moeten ook de controls en logica die binnen de fragment gedefinieerd zijn uitgevoerd worden. Hiervoor vuur je een speciaal event aan het einde van je loadPage functie zodat de controls op de pagina en andere logica geladen worden.
WinJS.Application.queueEvent({
type: "fragmentappended", location: location, fragment: content, state: state
});
Codevoorbeeld 15: Laden van controls en logica in een fragment
In de javascript file behorende bij de fragment kunnen we op dit event reageren en alle controls laden.
(function () {
'use strict';
WinJS.Application.addEventListener('fragmentappended', function handler(e) {
fragmentLoaded(e.fragment, e.state);
});
function fragmentLoaded(elements, options) {
WinJS.UI.processAll(elements)
.then(function () {
WinJS.UI.processAll(elements);
});
}
WinJS.Namespace.define('fragment1', {
fragmentLoaded: fragmentLoaded,
});
})();
Codevoorbeeld 16: Reageren op het fragmentappended event
Javascript Metro applicaties bieden een kans voor ontwikkelaars die niet bekend zijn met huidige Microsoft technieken om toch applicaties voor Windows 8 te ontwikkelen doormiddel van hun kennis van bestaande web technieken zoals HTML5 en Javascript. Een Javascript Metro applicatie is echter niet zomaar een lokale website dus er zal altijd een leer curve zijn voor mensen die van websites overstappen op Javascript Metro applicaties.
Voor ontwikkelaars die al bekend zijn met Silverlight en C# is het echter de vraag wat deze manier van ontwikkelen voor hen toevoegt. De ontwikkeltools voor Silverlight en C# zijn op dit moment vele malen beter voor Silverlight en C#. De kans is groot dat de meeste ontwikkelaars liever bij hun vertrouwde omgeving blijven. De toekomst zal uitwijzen welke talen het meest gebruikt gaan worden.
Links
Christiaan Veeningen en Geert van der Cruijsen zijn consultants bij Avanade Nederland waar ze zich focussen op web en mobiele technieken.
Christiaan Veeningen is te bereiken via zijn weblog op http://playingwith.net, via email christiaan.veeningen@avanade.com of twitter @bloodyairtimer.
Geert van der Cruijsen is te bereiken via zijn weblog op http://vdcruijsen.net, via email g.van.der.cruijsen@avanade.com of twitter @geertvdc.