Using controls in Hilo (Windows Store apps using JavaScript and HTML)
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
From: Developing an end-to-end Windows Store app using JavaScript: Hilo
Controls are the core UI objects of the Windows Library for JavaScript. You can use data binding with controls to display data on the UI at run time. Here we'll look at the ListView, FlipView, AppBar, and SemanticZoom WinJS controls, and the canvas and img HTML elements.
Download
You will learn
- How to use binding to declaratively connect your UI to data.
- How to apply templates to conveniently format and display multiple instances of data.
- How to use some of commonly used WinJS controls.
After you download the code, see Getting started with Hilo for instructions.
Applies to
- Windows Runtime for Windows 8
- WinJS
- JavaScript
Common controls used in Hilo
Here are the main controls that we used in Hilo. You can also refer to the HTML files in the Hilo folder in the Microsoft Visual Studio project.
- ListView
- FlipView
- AppBar
- SemanticZoom
- canvas
- img
The ListView, FlipView, AppBar, and SemanticZoom controls are WinJS controls. The canvas and img controls are HTML elements.
Tip See Controls list and Controls by function for all available controls. Bookmark these pages so that you can come back to them when you want to add another feature to your app. Also use the Toolbox window in Visual Studio and the Assets tab in Blend for Microsoft Visual Studio 2012 for Windows 8 to browse and add controls to your pages.
All of these controls display run time data on the UI through binding. Binding provides a data-bound property value, which defers the value until run time. Binding is key to effectively using the WinJS, because it enables you to declaratively connect your UI to data. Binding also helps you to cleanly separate your UI from your data when you design your app, because data is resolved at run time. For Hilo, binding data at run time is critical because we don’t know anything about the user’s Pictures at design time.
In order to minimize memory leaks when you bind data to the UI, you must set the optimizeBindingReferences property to true in your startup code. Here’s the code.
Hilo\default.js
WinJS.Binding.optimizeBindingReferences = true;
Note When you perform declarative binding, you should always set the optimizeBindingReferences property to true in your startup code. If you do not do that, the bindings in your app may leak memory. For more info see JavaScript project templates for Windows Store apps.
Hilo typically binds text content to the UI through a span element. Here’s the code that uses binding to display the page title on the detail page, with the page title being the month and year of the photo.
Hilo\Hilo\detail\detail.html
<span id="pageTitle" class="pagetitle" data-win-bind="innerText: title">[Month Year]</span>
The data-win-bind attribute binds a property of an element to a property of a data source, as a one-time binding.
Note When specifying a data-win-bind attribute, you can specify multiple sets of element/data source property pairs by separating them with a semicolon.
To display the data specified in the binding you must call the WinJS.Binding.processAll function. Here’s the code.
Hilo\Hilo\controls\pages.js
function bindPageTitle(title) {
// Bind the title based on the query's month/year.
var pageTitleEl = document.querySelector("#pageTitle");
WinJS.Binding.processAll(pageTitleEl, { title: title });
}
Here, two arguments are specified in the call to the processAll function. The first argument represents the rootElement parameter, which is the element you traverse to find elements for binding. The second argument represents the dataContext parameter, which is the object to use for data binding. In the second argument, the first use of title denotes the property of the data source specified in the data-win-bind attribute above. The second use of title represents the function parameter.
Note The rootElement parameter can be omitted from the call to the processAll function. This has the effect of searching the entire document to find elements to bind to.
The function binds the value of the data from the dataContext parameter to the elements that are descendants of the rootElement parameter, when those descendants have the data-win-bind attribute specified. The function then returns a promise that completes when every item that contains binding declarations has been processed.
For more info about binding, see Quickstart: binding data and styles, How to bind a complex object, and Using a separated presentation pattern in this guide.
[Top]
ListView
The ListView control displays data from an IListDataSource in a customizable list or grid. Items in a ListView control can contain other controls, but they can’t contain a FlipView or another ListView. In Hilo, the data displayed by ListView controls are typically specified by a template.
WinJS templates are a convenient way to format and display multiple instances of data. Hilo uses templates in conjunction with ListView and FlipView controls to specify the way to display data in the controls.
A template is commonly defined declaratively by creating a DIV element for the template, and adding a data-win-control attribute that has a value of WinJS.Binding.Template. This makes the DIV element host the specified WinJS control. Here’s the code for the template used by the ListView control on the hub page.
Hilo\Hilo\hub\hub.html
<div id="hub-image-template" data-win-control="WinJS.Binding.Template">
<div data-win-bind="style.backgroundImage: url.backgroundUrl; alt: name; className: className" class="thumbnail">
</div>
</div>
A template object must have a single root element, which can also serve as a parent for the template’s contents. Here, the root element sets the data-win-bind attribute to bind the value of the style.backgroundImage property to the value of the url.backgroundUrl property, along with some additional property bindings. The class attribute of the DIV element is set to use the thumbnail styling from the hub.css file.
Note Templates must be defined before they are used.
The ListView control can then use the template to display data. Here’s the code.
Hilo\Hilo\hub\hub.html
<div id="picturesLibrary"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemTemplate: select('#hub-image-template'),
selectionMode: 'single'}" />
The itemTemplate property associates the ListView control with the previously defined template, named hub-image-template. You set the itemTemplate property in the data-win-options attribute.
In the ready function of the page control, hub.js, the ListView control is returned using CSS query selector syntax. Instances of the ListViewPresenter and HubViewPresenter classes are initialized, and the start function of the HubViewPresenter instance is then called. Here's the code.
Hilo\Hilo\hub\hub.js
ready: function (element, options) {
// Handle the app bar button clicks for showing and hiding the app bar.
var appBarEl = document.querySelector("#appbar");
var hiloAppBar = new Hilo.Controls.HiloAppBar.HiloAppBarPresenter(appBarEl, WinJS.Navigation);
// Handle selecting and invoking (clicking) images.
var listViewEl = document.querySelector("#picturesLibrary");
this.listViewPresenter = new Hilo.Hub.ListViewPresenter(listViewEl, Windows.UI.ViewManagement.ApplicationView);
// Coordinate the parts of the hub page.
this.hubViewPresenter = new Hilo.Hub.HubViewPresenter(
WinJS.Navigation,
hiloAppBar,
this.listViewPresenter,
new Hilo.ImageQueryBuilder()
);
this.hubViewPresenter
.start(knownFolders.picturesLibrary)
.then(function () {
WinJS.Application.addEventListener("Hilo:ContentsChanged", Hilo.navigator.reload);
});
},
The start function executes a query to return images to populate the ListView control with. It then calls the loadImages function, which in turn calls the bindImages function. The bindImages function calls the setDataSource function of the ListViewPresenter class, in order to bind the ListView to the data source. Here's the code for the ListViewPresenter.setDataSource function.
Hilo\Hilo\hub\listViewPresenter.js
setDataSource: function (items) {
this.lv.itemDataSource = new WinJS.Binding.List(items).dataSource;
},
The ListView control is referenced through the lv variable. For binding, the ListView control requires a data source that implements IListDataSource. For in-memory arrays of data, Hilo uses WinJS.Binding.List. The itemDataSource property takes an IListDataSource object. However, the List object is not an IListDataSource, but it does have a dataSource property that returns an IListDataSource version of itself.
When the instance of the ListViewPresenter is created in the ready function of the page control, hub.js, the ListViewPresenter constructor calls the setup method. This sets the layout property of the ListView control to the data returned by the selectLayout method. The selectLayout method defines the layout of the photo thumbnails on the hub page. It creates a new instance of the WinJS.UI.GridLayout class, sets the groupInfo property of the instance to the settings contained in the listViewLayoutSettings variable, and sets the maxRows property of the instance to 3. Then it returns the instance. Here's the code.
Hilo\Hilo\hub\listViewPresenter.js
selectLayout: function (viewState, lastViewState) {
if (lastViewState === viewState) { return; }
if (viewState === appViewState.snapped) {
return new WinJS.UI.ListLayout();
}
else {
var layout = new WinJS.UI.GridLayout();
layout.groupInfo = function () { return listViewLayoutSettings; };
layout.maxRows = 3;
return layout;
}
},
Hilo\Hilo\hub\listViewPresenter.js
var listViewLayoutSettings = {
enableCellSpanning: true,
cellWidth: 200,
cellHeight: 200
};
Note The property settings in the listViewLayoutSettings variable must correspond to the height and width values specified in the CSS for the items. They need to be the greatest common denominator of the different widths and heights.
The CSS for the hub page specifies that each photo thumbnail will have a width and height of 200 pixels, with the exception of the first thumbnail which will have a width and height of 410 pixels. 410 pixels is the width and height of a standard thumbnail doubled, plus 10 pixels to account for the margin between the grid cells. Here's the code.
Hilo\Hilo\hub\hub.css
.hub section[role=main] .thumbnail {
width: 200px;
height: 200px;
}
.hub section[role=main] .thumbnail.first {
width: 410px;
height: 410px;
}
For more info about binding by using templates, see How to use templates to bind data. For more info about the ListView control, see Quickstart: Adding a ListView. For more info about how we used the ListView control to help navigate between large sets of pictures, see Working with data sources in this guide.
[Top]
FlipView
The FlipView control displays a collection of items, such as a set of photos, and lets you flip through them one at a time. In Hilo, the data displayed by the FlipView control on the detail page is specified by a template.
WinJS templates are a convenient way to format and display multiple instances of data. Hilo uses templates in conjunction with ListView and FlipView controls to specify the way to display data in the controls.
A template is commonly defined declaratively by creating a DIV element for the template, and adding a data-win-control attribute that has a value of WinJS.Binding.Template. This makes the DIV element host the specified WinJS control. Here’s the code for the template used by the FlipView control.
Hilo\Hilo\detail\detail.html
<div id="image-template" data-win-control="WinJS.Binding.Template">
<div class="flipViewBackground">
<div class="flipViewImage" data-win-bind="backgroundImage: src Hilo.Picture.bindToImageSrc">
</div>
</div>
</div>
A template object must have a single root element, flipViewBackground, to serve as a parent for the template’s contents. Here, the parent element contains only one child element, flipViewImage. This element's data-win-bind attribute binds the backgroundImage property to the src property. The src property is set by the Hilo.Picture.bindToImageSrc function.
The FlipView control can then use the template to display data. Here’s the code.
Hilo\Hilo\detail\detail.html
<div id="flipview"
data-win-control="WinJS.UI.FlipView"
data-win-options="{
itemTemplate: select('#image-template')}">
</div>
The itemTemplate property, which is set in the data-win-options attribute, associates the FlipView control with the previously-defined template, named image-template.
Note The FlipView control does not dynamically adjust its height to fit the content. For a FlipView to render, you must specify an absolute value for its height.
In the ready function of the page control, detail.js, the FlipView control is returned using CSS query selector syntax. An instance of the DetailPresenter class is initialized, and the start function of the DetailPresenter instance is then called. Here's the code.
Hilo\Hilo\detail\detail.js
ready: function (element, options) {
var query = options.query;
var queryDate = query.settings.monthAndYear;
var pageTitle = Hilo.dateFormatter.getMonthFrom(queryDate) + " " + Hilo.dateFormatter.getYearFrom(queryDate);
this.bindPageTitle(pageTitle);
var hiloAppBarEl = document.querySelector("#appbar");
var hiloAppBar = new Hilo.Controls.HiloAppBar.HiloAppBarPresenter(hiloAppBarEl, WinJS.Navigation, query);
var filmstripEl = document.querySelector("#filmstrip");
var flipviewEl = document.querySelector("#flipview");
var flipviewPresenter = new Hilo.Detail.FlipviewPresenter(flipviewEl);
var filmstripPresenter = new Hilo.Detail.FilmstripPresenter(filmstripEl);
var detailPresenter = new Hilo.Detail.DetailPresenter(filmstripPresenter, flipviewPresenter, hiloAppBar, WinJS.Navigation);
detailPresenter.addEventListener("pageSelected", function (args) {
var itemIndex = args.detail.itemIndex;
options.itemIndex = itemIndex;
});
detailPresenter
.start(options)
.then(function () {
WinJS.Application.addEventListener("Hilo:ContentsChanged", Hilo.navigator.reload);
});
},
The start function executes a query to return images to populate the FlipView control. Then the DetailPresenter.bindImages function is called to create a new instance of the FlipviewPresenter class. The FlipviewPresenter constructor in turn calls the FlipviewPresenter.bindImages function to bind the FlipView control to the data source. Here's the code for the FlipviewPresenter.bindImages function.
Hilo\Hilo\detail\flipviewPresenter.js
bindImages: function (images) {
this.bindingList = new WinJS.Binding.List(images);
this.winControl.itemDataSource = this.bindingList.dataSource;
},
You reference the FlipView control through the winControl variable. For binding, the FlipView control requires a data source that implements IListDataSource. For in-memory arrays of data, Hilo uses WinJS.Binding.List. The itemDataSource property takes an IListDataSource object. The List object is not an IListDataSource, but it does have a dataSource property that returns an IListDataSource version of itself.
For more info about binding using templates, see How to use templates to bind data, and Working with data sources in this guide. For more info about the FlipView control, see Quickstart: Adding a FlipView.
[Top]
AppBar
The AppBar control is a toolbar for displaying app-specific commands. Hilo displays a bottom app bar on every page, and a navigation bar on the image view page that displays a filmstrip view of all photos for the current month.
Here’s what the buttons look like on the bottom app bar on the image view page.
Place buttons that enable navigation or critical app features on a page. Place buttons in an app bar if they are not critical to the navigation and use of your app. For example, on the month view page, we enable the user to click on the text for a given month to jump to all photos for that month, because we felt that it was crucial to navigation. We added the rotate and crop commands to the app bar because these are secondary commands and we didn’t want them to distract the user.
The location of the app bar on a page is controlled by its placement property. Hilo has an image navigation control that provides a re-usable implementation of the bottom app bar. This control can be used to navigate to the rotate and crop pages. Here’s the HTML for the bottom app bar that appears on the image view page.
Hilo\Hilo\detail\detail.html
<section id="image-nav" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/Hilo/controls/HiloAppBar/hiloAppBar.html'}"></section>
Per UI guidelines for app bars, the app bar will automatically hide labels and adjust padding when in snapped or portrait orientation. For more info about app bars, see Quickstart: adding an app bar with commands, and Guidelines and checklist for app bars. For more info about how we used the AppBar control in Hilo, see Swipe from edge for app commands in this guide.
[Top]
SemanticZoom
The SemanticZoom control lets the user zoom between two views of a collection of items. For more info about how we use this control to help navigate between large sets of pictures, see Pinch and stretch to zoom in this guide.
[Top]
Canvas
The canvas HTML element provides an object that can be used for drawing, rendering, and manipulating images and graphics on a document. We used this element to perform visual cropping of the photo, to show what the crop result will look like when the file is saved.
Here’s the HTML for the canvas.
Hilo\Hilo\crop\crop.html
<div class="canvas-wrapper">
<canvas id="cropSurface"></canvas>
</div>
Here’s what the Hilo crop UX looks like.
Manipulation of the canvas element occurs in JavaScript. The code obtains the canvas-based crop selection, and then calculates the selected area of the original image by scaling the canvas-based selection to the original image dimensions. The on-screen image is then cropped, to show what the crop result will look like when the file is saved. Multiple cropping operations are supported by storing an offset for the starting location of the crop on the original image, rather than relative to the canvas size. Here’s the code.
Hilo\Hilo\crop\imageView.js
cropImage: function () {
var selectionRectScaledToImage = this.getScaledSelectionRectangle();
// Reset image scale so that it reflects the difference between
// the current canvas size (the crop selection) and the original
// image size, then re-draw everything at that new scale.
this.imageToScreenScale = this.calculateScaleToScreen(selectionRectScaledToImage);
this.drawImageSelectionToScale(selectionRectScaledToImage, this.imageToScreenScale);
// Remember the starting location of the crop on the original image
// and not relative to the canvas size, so that cropping multiple times
// will correctly crop to what has been visually selected.
this.image.updateOffset({ x: selectionRectScaledToImage.startX, y: selectionRectScaledToImage.startY });
return this.canvasEl.toDataURL();
},
For more info about the crop UX, see Using touch in this guide.
[Top]
Img
We use the img HTML element to display photos on the rotate and crop pages of the app.
Here’s the HTML for the img element on the rotate page.
Hilo\Hilo\rotate\rotate.html
<img src="#" id="rotate-image" />
In the ready function of the page control, rotate.js, the img element is returned using CSS query selector syntax. An instance of the RotatePresenter class is initialized, and the start function of the RotatePresenter instance is then called. Here's the code.
Hilo\Hilo\rotate\rotate.js
var imgEl = document.querySelector("#rotate-image");
this.presenter = new Hilo.Rotate.RotatePresenter(imgEl, this.appBarPresenter, fileLoader, expectedName, touchProvider);
this.presenter.start();
The start function in turn calls the _loadAndShowImage internal function. This takes the query result from the image query and displays the image that is loaded, in the img element. Here's the code for the _loadAndShowImage function.
Hilo\Hilo\rotate\rotatePresenter.js
_loadAndShowImage: function (queryResult) {
var self = this;
if (queryResult.length === 0) {
this.navigation.back();
} else {
var storageFile = queryResult[0].storageFile;
if (storageFile.name !== this.expectedFileName) {
this.navigation.back();
} else {
this.hiloPicture = new Hilo.Picture(storageFile);
this.el.src = this.hiloPicture.src.url;
return storageFile.properties
.getImagePropertiesAsync()
.then(function (props) {
self.imageProperties = props;
self.adjustImageSize();
});
}
}
},
The img element is referenced through the el variable. Therefore, the code sets the src property of the img element to the URL of the image to be displayed.
Note The img element displays photos on the rotate and crop pages of the app. On the other pages, photos are displayed using the backgroundImage CSS property of a DIV element.
For more info about images, see Displaying images, graphics, and thumbnails.
[Top]
Styling controls
Hilo’s appearance was customized by styling the controls used in the app. To customize the appearance of controls you use CSS. A Windows Store app using JavaScript also supports some advanced control styling features, with the WinJS providing a set of styles that give your app the Windows 8 look and feel.
For more info, see Adding and styling controls, and Quickstart: styling controls.
[Top]
Touch and gestures
The WinJS runtime provides built-in support for touch. Because the runtime uses a common event system for many user interactions, you get automatic support for mouse, pen, and other pointer-based interactions. The exception to this is the rotate page, which uses the GestureRecognizer class to listen for and handle pointer and gesture events that enable the user to rotate the displayed photo.
Tip Design your app for the touch experience, and mouse and pen support come for free.
For more info about how we used touch in Hilo, see Using touch.
[Top]
Testing controls
When you test your app, ensure that each control behaves as you expect in different configurations and orientations. When we used a control to add a new feature to the app, we ensured that the control behaved correctly in snap and fill views and under both landscape and portrait orientations.
If your monitor isn’t touch-enabled, you can use the simulator to emulate pinch, zoom, rotation, and other gestures. You can also work with different screen resolutions. For more info, see Running apps in the simulator.
You can test your app on a computer that doesn’t have Visual Studio but has hardware you need to test. For more info, see Running apps on a remote machine.
For more info on how we tested Hilo, see Testing and deploying apps.
[Top]