Background
The new Windows 8 Developer environment is seriously under documented at this point in it’s product life. Microsoft released a “Developer Preview” at the Build Conference in September, than has not done any noticeable updates or improvement on those bits. The Video’s online from build are very helpful because you can go through them in slow motion and see how to make things work. In this article, I’m going to go through the steps necessary in a lot of detail to build a simple list read-only list viewer of US Congress legislators in California. At the end, we’ll have something like the following working:
(actual tablet from build running the app)
and the completed solution from Visual Studio 2011 (it’s always nice to have source)
Getting Visual Studio 2011 Going To Create The Project
First, launch Visual Studio 2011 and give the standard File/New Project choice and chose JavaScript / Windows Metro Style / Fixed Layout Application as follows:
Notice, there are two files we will primary work with, default.js and default.html.
The default.html is a very classic html page with just the standard html doctype, some JavaScript includes, a style sheet and a simple div container. The default.js is actually what gives this page life.
One thing you’ll quickly discover if you have done much web development before (particularly JavaScript) is that unlike XAML in Visual Studio, there is no design view. This shouldn’t be an entire surprise because in Microsoft’s ASP.NET MVC, there also is know design view for html files. However, there is a really big difference here that I don’t see people pointing out clearly enough. I’m going to say it twice for emphasis. When you run your Metro App, there is no IE Debug, Firebug or Chrome Developer Tools to look at what are running (OK, I’ll spare saying it again, but this is really critical). The way most of us do web development that depends on a lot of JavaScript is to launch the application in the browser, then dive into the browser’s debugger and do all there debugging from there (that is by running the Visual Studio project outside the Visual Studio debugger.
Blend to the rescue! (more like fire, and without Expression Blend, the house would burn down). When you right click on the HTML file (as shown below) and open in blend, you will now see the page as if it were being run. I’ve noticed that if I load my JavaScript data stores from local arrays, those actually render, but if I try to pull data from a live data source they do not. I’m guessing that’s my misunderstanding for how to do it, rather than a product short coming.
Putting Down the ListView in HTML
Let’s get down to some real work here. First, let’s open the default.html and put down a listview. Before though, let’s look at what the template gives us (it’s very simple and boring).
<body> <div data-win-control="WinJS.UI.ViewBox"> <div class="fixed-layout"> <p>Content goes here</p> </div> </div> </body>
We’ll add all our stuff to the “Content goes here”. Fist, let’s add the listview control as follows:
<div id="listview" class="flipView" data-win-control="WinJS.UI.ListView" data-win-options="{dataSource: itemDataSource, itemRenderer: personTemplate, layout: {type: WinJS.UI.GridLayout, maxRows:2}}" style="margin: 15px 15px 15px 15px; left: -3px; top: 10px; height: 100%;width: 100%"> </div>
and the template itself:
<div id="personTemplate" data-win-control="WinJS.Binding.Template"> <div style="width: 100%;height: 100%"> <div style="margin: 15px 15px 15px 15px"> <img data-win-bind="src: govtrack_id convertGovTrackIdToImgTag" alt="Databound image" /> <h3> Rep <span class="content" data-win-bind="innerText: firstname"> </span> <span class="letter" data-win-bind="innerText: lastname"> </span> </h3> </div> </div> </div>
Remembering that any style that begins with data- is ignored by html and can be handled by the program for doing template type stuff, you see here that we have a windows control declared which is based in the WinRT runtime (WinJS.UI.ListView). We also have options set declaratively here in the data-win-options. All those can be set in the default.js file which is often easier because the intellisense helps us.
Explaining the options is as follows:
- datasource: defined in the default.js and is what we refer to this listview’s data source with
- itemRenderer: next, I’ll explain this. It points at the div that has the actual rendered template
- layout: just says what kind of layout. in our case, GridLayout.
- style: should really be set in a class but I got a little lazy since I’m not so good at the CSS stuff
And, don’t forget to update your depencies (namespaces for all your c# folks out there). Here are the ones you need to replace in your html file.
<script type="text/javascript" src="WinJS/js/base.js"></script> <script type="text/javascript" src="WinJS/js/ui.js"></script> <script type="text/javascript" src="WinJS/js/binding.js"></script> <script type="text/javascript" src="WinJS/js/controls.js"></script> <script type="text/javascript" src="WinJS/js/res.js"></script> <script type="text/javascript" src="WinJS/js/animations.js"></script> <script type="text/javascript" src="WinJS/js/uicollections.js"></script> <script type="text/javascript" src="WinJS/js/wwaapp.js"></script>
JavaScript File Update
Finally, let’s update the JavaScript file. First, we need to create a simple array that we will databind to. Then, we need to associate that array with the itemDataSource and finally execute. The only thing tricky we have here is a Converter that takes an Id and build an image src tag for the image control itself. Notice that the converter is called convertGovTrackIdToImgTag. It takes in a govTrackId and returns the full blown URL to tick in the image url.
All the source is below.
Hope this helps!
convertGovTrackIdToImgTag = WinJS.Binding.converter(function (govTrackId) { var retUrl = 'http://www.govtrack.us/data/photos/' + govTrackId + '-50px.jpeg'; return retUrl; });var itemDataSource = {};
(function () { ‘use strict’; // Uncomment the following line to enable first chance exceptions. Debug.enableFirstChanceException(true);
<span class="rem">//allLegislatorsCreateFile();</span> WinJS.Application.onmainwindowactivated = <span class="kwrd">function</span> (e) { <span class="kwrd">if</span> (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { <span class="kwrd">var</span> items = []; <span class="kwrd">var</span> itemsFresh = [{ firstname: <span class="str">"Barack"</span>, lastname: <span class="str">"Obama"</span>, govtrack_id: <span class="str">"400629"</span> }, { firstname: <span class="str">"Adam"</span>, lastname: <span class="str">"Smith"</span>, govtrack_id: <span class="str">"400379"</span> }, { firstname: <span class="str">"Adrian"</span>, lastname: <span class="str">"Smith"</span>, govtrack_id: <span class="str">"412217"</span> }]; items.push(itemsFresh[0]); items.push(itemsFresh[1]); items.push(itemsFresh[2]); itemDataSource = <span class="kwrd">new</span> WinJS.UI.ArrayDataSource(items); WinJS.UI.processAll(); } } WinJS.Application.start();
})();