What’s New in the Beta Metro/JS Templates for VS11
The Consumer Preview of Windows 8 (aka the Win8 beta) is now available for download, along with the matching Visual Studio 11 beta. You can download them both from the Developer Center for Metro style Apps and at least when I did the downloading this morning, it was smooth and worked well. In case you’re interested, I downloaded the ISO, not the setup, and I am currently writing this blog entry in Windows Live Writer running inside a WMWare Workstation 8.0 virtual machine running on the Windows 7 host OS running inside Boot Camp on my MacBook Pro. As someone said to me this morning: “That’s a lot of VMs!” Maybe so, but the Win8 and VS11 betas are running surprisingly well inside of my Inception-box.
Metro/JS Templates for VS11 in BUILD
If you played around with the Metro/JS templates in VS11 from the BUILD conference in September, you’ll have noticed that the generated apps were compliant with the Windows 8 UX style guidelines, but that two of the templates — Grip and Split — generated large amounts of code. That’s because these are pretty much the biggest apps that Microsoft has ever shipped as templates. They each have multiples pages and they work hard at being simple but feature complete Metro style apps.
However, as well as the BUILD templates implemented the Win8 UX, their code wasn’t the greatest, for the following reasons:
- The templates were attempting to codify best practices for a brand-new app model. That’s always going to take time to get right.
- There was only so much time to get it right before BUILD — it was either have great code or implement a great UX, so the template team chose the latter.
- The bonehead PM in charge at the time (me) decided it was a better choice to push the data model into each page instead of centralizing it. That was a bad idea.
- The data model, spread as it was across nearly every JS file in the templates, was assumed to be static. However, the vast majority of apps get their data dynamically from some external source and the template-generated code made that very hard to implement.
- There were a number of features that the templates needed that didn’t exist directly in the platform, either in WinJS, the IE DOM or in the WinRT libraries, so the templates needed to provide those features.
All of these reasons meant that the functionality of the generate Grid and Split apps made for an instructional start to building your own Metro style apps, assuming you were willing to wade through a great deal of code. The new templates in the Win8 beta solve nearly all of these problems.
Metro/JS Templates for VS11 Beta
Out of the box, the Metro/JS templates in the VS11 beta (made available today, Feb 29, 2012), get more betterer as they get more complex.Let’s start simple and work our way up.
Blank Application
The Blank Application template is almost the smallest Metro/JS app you can build, except that it includes a reference to the Microsoft Windows Library for JavaScript (aka WinJS):
And to be clear, this is a brand new feature. Including WinJS as a reference instead of dropping the code into each project means that MS can shipped a single shared implementation instead of every app in the store duplicating the code. If you want to duplicate the code into your project, you can do so, but you’ll also have to update the URL references to the JS and CSS files from your HTML files, like this snippet from the generated default.html:
- <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>
In addition to the new syntax with the leading double-slashes, that the number of WinJS files to include is a far smaller number in the Beta. Now it’s just base.js and ui.js, which makes it easy to decide which one(s) you want and in what order to include them, fixing a common problem in the BUILD bits.
One other thing that’s new in the Blank Application template is that there are stubs for implementing tomb-stoning to easily save and restore your app’s session state in the default.js:
- var app = WinJS.Application;
- app.onactivated = function (eventObject) {
- if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
- if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.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.
- }
- WinJS.UI.processAll();
- }
- };
- app.oncheckpoint = function (eventObject) {
- // TODO: This application is about to be suspended. Save any state
- // that needs to persist across suspensions here. You might use the
- // WinJS.Application.sessionState object, which is automatically
- // saved and restored across suspension. If you need to complete an
- // asynchronous operation before your application is suspended, call
- // eventObject.setPromise().
- };
The handling of the terminated state in the onactivated event on line 3 and the checkpoint event handler on like 16 are for reactivating and saving your app state respectively.
And finally, the handy TODO comments are something you’ll find sprinkled throughout the templates based on usability feedback conducted to determine what developers really need help with as they add their own functionality to the app.
Fixed Layout Application
Moving up the complexity scale, the Fixed Layout Application template is meant to do just what it says — provide a jumping off point for apps that are logically fixed layout. The crux of this code is the use of the ViewBox control in the default.html:
- <body>
- <div data-win-control=“WinJS.UI.ViewBox”>
- <div class=“fixedlayout”>
- <p>Content goes here</p>
- </div>
- </div>
- </body>
Line 2 wraps the content of the app in a ViewBox control, which will scale everything inside of it to the size of the content, which is defined in the default.css file with the fixedLayout style:
- .fixedlayout {
- -ms-grid-columns: 1fr;
- -ms-grid-rows: 1fr;
- display: -ms-grid;
- height: 768px;
- width: 1024px;
- }
You’ll see in lines 5 and 6 that the height and width of the div contained in the ViewBox is 768x1024, which means that the content can be created using absolute positioning and sizing. The job of the ViewBox is as the app is resized, either the computer’s resolution changes or more likely the app is moved between landscape, portrait, split and full sizes, the ViewBox will scale the content to take up as much room as possible, keeping the aspect ratio constant and scaling the content such that the app itself can think of itself as logically 768x1024 (or whatever the top-level div’s size is). This is very handy for building things like casual games where you want scaling, but generally not flowing — you want to control where the Scrabble tiles are or the tic-tac-toe pieces and it’s much easier to do that with a fixed size.
And now that I’ve described it, I’ll tell you that this template is the only one that’s structurally identical between BUILD and Beta. Still, it is useful.
Navigation Application
The next one up the ladder is the Navigation Application template, which is where we get the Back button and the support for moving HTML fragments into and out of the DOM just like the user was logically navigation page-to-page. In the BUILD bits, this navigation functionality was packaged in the default.js file, but in the Beta, default.js is just the same as the simpler templates. Instead, the navigation functionality is packaged into a new file: navigator.js. The reason this file is separate is to make it clear if you’d like to implement a different navigation policy, e.g. MVC, then this is the file to start with. Further, while this functionality would seem a shoe-in to be included in WinJS, it’s not quite “baked” enough, which means that MS hasn’t yet decided that this is “the way” to do navigation.
Still, it’s “a way” to do navigation in a Metro/JS app and a pretty useful one. Essential the way it works is that there is a singleton PageControlNavigator in the default.html file that holds the pages as they’re swapped in. The default.html is also where navigator.js is included:
- <script src=“/js/navigator.js”></script>
- …
- <div id=“contenthost” data-win-control=“Application7.PageControlNavigator” data-win-options=“{home: ‘/html/homePage.html’}“></div>
The navigator.js file defines the PageControlNavigator control, which holds the logical pages as the user clicks around in the application. The home parameter is where to start the navigation. Navigation is to a Page, which is really a mapping between an HTML file and a set of events to handle over the lifetime of that Page:
- // This function is called whenever a user navigates to this page. It
- // populates the page elements with the app’s data.
- function ready(element, options) {
- // TODO: Initialize the fragment here.
- }
- WinJS.UI.Pages.define(“/html/homePage.html”, {
- ready: ready
- });
Of course, navigating to the home page is going to be rare compared to navigating between pages. The easiest way to get a new page to add to your app is to right-click on your project in the Solution Explorer and select Add | New Item:
The last item three item templates on the list are for shell contract implementations, which are beyond the scope of this blog post, but the first one is a Page Control, which gives us a triad of HTML, JS and CSS that fits exactly into the navigation model provided by the PageControlNavigator control:
- DOCTYPE html>
- <html>
- <head>
- <meta charset=“utf-8”>
- <title>page2</title>
- <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=“page2.css” rel=“stylesheet”>
- <script src=“page2.js”></script>
- </head>
- <body>
- <div class=“page2 fragment”>
- <header aria-label=“Header content” role=“banner”>
- <button class=“win-backbutton” aria-label=“Back” disabled></button>
- <h1 class=“titlearea win-type-ellipsis”>
- <span class=“pagetitle”>Welcome to page2</span>
- </h1>
- </header>
- <section aria-label=“Main content” role=“main”>
- <p>Content goes here.</p>
- </section>
- </div>
- </body>
- </html>
- .page2 p {
- margin-left: 120px;
- }
- // This function is called whenever a user navigates to this page. It
- // populates the page elements with the app’s data.
- function ready(element, options) {
- // TODO: Initialize the fragment here.
- }
- function updateLayout(element, viewState) {
- // TODO: Respond to changes in viewState.
- }
- WinJS.UI.Pages.define(“/page2.html”, {
- ready: ready,
- updateLayout: updateLayout
- });
Navigating to this new control defined by these files is a simple matter of calling the navigate method:
- <a onclick=“WinJS.Navigation.navigate(‘/page2.html’)“>Page 2</a>
As far as the user is concerned, the anchor tag shows up as a link like any other:
Clicking on “Page 2” fires the onclick event, which calls the navigate method, passing in the path to the HTML file and causes the Page control defined in page2.html, page2.js and page2.css to be loaded:
In addition to whatever content on your Page control, notice that the Back button shows up automatically. The Back button manages navigation via clicking, touching and the browser keys; Ctrl+Left Arrow and Ctrl+Right Arrow work as Back and Forward respectively.
Grid and Split Application
At this point, we’ve covered almost all of the core concepts that make up the Grid and Split applications: they bring in WinJS by reference, they use controls and they use navigation via the Page controls. In fact, even though the Grid app has three pages and the Split app has two, they’re really just the navigation template with the pages to implement the Grid and Split app patterns that MS decided were the major app patterns appropriate for Win8. However, the Grid and Split application templates do have two major features that the other templates don’t have: support for multiple view states and a centralized data model.
Multiple view state support means that as the app is moved between portrait, landscape, full and split, the app adjusts itself to look good in all states. The view state management is mostly handled with CSS styles associated with media modes, like in the Split App’s itemsPage.css:
- …
- @media screen and (-ms-view-state: snapped) {
- .itemspage .itemslist .win-vertical.win-viewport .win-surface {
- margin-bottom: 30px;
- }
- …
In Metro/JS apps, MS has provided a media query predicate called -ms-view-state, which can be one of the four view states and the styles in the media query block will be applied when the app moves to that state. In addition, if you want to handle the view state change in JS, you can do so with updateLayout event in your Page control, like this snippet from itemsPage.js:
- // This function updates the page layout in response to viewState changes.
- updateLayout: function (element, viewState) {
- var listView = element.querySelector(″.itemslist”).winControl;
- if (viewState === Windows.UI.ViewManagement.ApplicationViewState.snapped) {
- listView.layout = new ui.ListLayout();
- } else {
- listView.layout = new ui.GridLayout();
- }
- }
In this case, the updateLayout event is called when the control is initially created and as the app moves through the view states so it can change the layout style for the ListView control showing the contents of the page:
Landscape vs. Snapped view state layout for the itemsPage ListView control
The other major feature of the Grid and Split app templates — and this feature is new in the Beta bits — is the centralized data model, which is where the data for all pages comes from. This data model is defined in data.js and it contains the static group and item data as you just saw. The core of the data is exposed from data.js like so:
- WinJS.Namespace.define(“data”, {
- items: groupedItems,
- groups: groupedItems.groups,
- getItemsFromGroup: getItemsFromGroup
- });
These three members of the data object are used throughout the templates, e.g. in the itemsPage.js ready event handler:
- // 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) {
- var listView = element.querySelector(″.itemslist”).winControl;
- ui.setOptions(listView, {
- itemDataSource: data.groups.dataSource,
- itemTemplate: element.querySelector(″.itemtemplate”),
- oniteminvoked: this.itemInvoked.bind(this),
- });
- this.updateLayout(element, Windows.UI.ViewManagement.ApplicationView.value);
- },
Notice that the data.groups property is used on line 6 to perform a data binding operation. That data binding is against the dataSource property of the object returned from data.groups, which itself is created by a method on the WinJS.Binding.List object that holds the grouped item data. It’s this binding list, a new feature in WinJS for the Beta, that makes it easy to move from the static data provided by the templates and dynamic data that your app defines.
The binding list is a binding data source, which means that as you add items to it, it notifies any control that happens to be bound to it. This is especially handy when your app starts up with zero data, but you need to initialize the ListViews such that as the data is available (perhaps from an asynchronous network call), it will be shown.
If you open up the data.js, you’ll see the static sample data:
- // Each of these sample groups must have a unique key to be displayed
- // separately.
- var sampleGroups = [
- { key: “group1”, title: “Group Title: 1”, subtitle: “Group Subtitle: 1”, backgroundImage: darkGray, description: groupDescription },
- …
- ];
- // Each of these sample items should have a reference to a particular
- // group.
- var sampleItems = [
- { group: sampleGroups[0], title: “Item Title: 1”, subtitle: “Item Subtitle: 1”, description: itemDescription, content: itemContent, backgroundImage: lightGray },
- …
- ];
The group data has a unique key, a title, a subtitle, a background image and a description, which are all fields that the data templates used in the Split and Grid apps depend upon (although you can change them if you like). The item data has a reference to the group to which it belongs, a title, a subtitle, a description, a background image and the content for the item itself.
The code that populates the binding list with the sample data looks like this:
- var list = new WinJS.Binding.List();
- var groupedItems = list.createGrouped(groupKeySelector, groupDataSelector);
- // TODO: Replace the data with your real data.
- // You can add data from asynchronous sources whenever it becomes available.
- sampleItems.forEach(function (item) {
- list.push(item);
- });
As the comment makes clear, it’s this code you’re most likely to want to change. Instead of pulling in static data from the sampleItems array, we want to pull the items in asynchronously, perhaps from an RSS feed or two just like my earlier post:
- var list = new WinJS.Binding.List();
- var groupedItems = list.createGrouped(groupKeySelector, groupDataSelector);
- // RSS feeds
- var feeds = [
- { key: “feed1”, title: “Scott Hanselman”, subtitle: “a blog”, backgroundImage: darkGray, description: “a blog”, url: “http://feeds.feedburner.com/ScottHanselman” },
- { key: “feed2”, title: “Raymond Chen”, subtitle: “a blog”, backgroundImage: lightGray, description: “a blog”, url: “http://blogs.msdn.com/b/oldnewthing/rss.aspx” },
- { key: “feed3”, title: “Chris Sells”, subtitle: “a blog”, backgroundImage: mediumGray, description: “a blog”, url: “http://sellsbrothers.com/posts/?format=rss” },
- ];
- feeds.forEach(function (feed) {
- WinJS.xhr({ url: feed.url }).then(function (request) { processPosts(feed, request); });
- });
- function processPosts(feed, request) {
- // parse the RSS
- var nodes = request.responseXML.selectNodes(“//item”);
- for (var i = 0, len = nodes.length; i < len; i++) {
- var node = nodes[i];
- var item = {
- group: feed,
- title: node.selectNodes(“title”)[0].text,
- subtitle: node.selectNodes(“pubDate”)[0].text,
- description: “a post”,
- content: node.selectNodes(“description”)[0].text,
- backgroundImage: feed.backgroundImage
- };
- list.push(item);
- }
- }
In this case, our group data is a set of RSS feeds, being careful to continue to use the same group field names so I don’t have to update the data templates in the rest of the app. When the app loads, I still create a binding list, but instead of filling it directly, I start an async xhr call (the WinJS XMLHttpRequest wrapper) for each feed, creating an item for each RSS post I find upon a successful completion. Because I’ve left the data model alone and because I’m using the binding list, that’s all I have to change and now the entire app has been updated to support that data:
The items page with the set of feeds in the Split app template
The split page with the posts from the selected feed
Where Are We?
As you can see, the Metro/JS templates in the VS11 beta start simple and add features with navigation, pages with specific app pattern functionality, multiple view state support and a unified data model. The main difference is the Beta versions of this templates is that code has been simplified, beautified and pushed into WinJS as much as possible to make the inside of your app just as pretty and easy to use as the outside.
Translation
This article has been translated into Serbo-Croatian by Jovana Milutinovich. Enjoy.