Windows 8
Who Moved My Cheese?
Brendan Forster
Senior Developer, Readify
@shiftkey
#dddbrisbane #mix03
Give Twitter a break
- Discussion benefits everyone here
- Slides will be online after talk
- Screencasts on content for those who couldn't attend
- Lunch is right after!
Source
Topics
-
How to start building an app!
egad! not that HTML thing!
-
New Windows Runtime Features
ooooh! shiny!
-
Tips and Tricks
ninjas may or may not be on fire
Application Features
- Display some tweets
- Authenticate user
- Store application data
- Select an image to set as profile image
- Get the user's location
- Tile notifications when someone mentions you
- Integrate Twitter Search into the desktop
- Share something on Twitter
User Interface
XAML
- for those with strong XAML/C# skills
HTML
- for those with strong HTML/JS skills
User Interface
Common to XAML and HTML
- Visual Studio for development
- Expression Blend for design (only HTML in Win8 Developer Preview)
- Windows Runtime APIs
- Application Manifest file to declare application metadata
XAML
Project Structure
- App.xaml entry point for application
- Project Templates include sample layouts and styles
XAML
Adding logic - the fast way!
- Hook into events raise to load and cleanup
- Add behaviour to the code-behind file
<UserControl x:Class="MyApplication.CollectionPage"
Loaded="Page_Loaded" Unloaded="Page_Unloaded"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1366">
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// attach handlers
// set default view state
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
// detach handlers
}
XAML
Navigating to another page
<UserControl x:Class="MyApplication.MainPage"
...
>
<Frame Source="MyApplication.SomePage">
<Frame.ContentTransitions>
<TransitionCollection>
<-- slide the content in 200px when it changes -->
<EntranceThemeTransition FromHorizontalOffset="200" />
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</UserControl>
XAML
Navigating to another page
- Create a Page matching that type
<Page x:Class="MyApplication.SomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="Hello World" FontSize="50"/>
</Page>
XAML
Navigating to another page
- Project templates use static methods in App to change whole page
- Replaces entire window instead of region
- Application to maintain journal of pages
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
ShowCollection();
Window.Current.Activate();
}
public static void ShowCollection()
{
var page = new CollectionPage();
// setup data
Window.Current.Content = page;
}
HTML
Project Structure
- default.html entry point for application
- CSS for light and dark themes included
- Additional JS files for WinJS features
HTML
Document Structure
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>My Application</title>
<link rel="stylesheet" href="/winjs/css/ui-light.css" />
<script type="ms-deferred/javascript" src="/winjs/js/base.js"></script>
<!-- other WinJS files -->
<link rel="stylesheet" href="/css/default.css" />
<!-- other stylesheets -->
<script type="ms-deferred/javascript" src="/js/default.js"></script>
<!-- other scripts -->
</head>
<body>
<!-- layout here -->
</body>
</html>
"ms-deferred/javascript"?
What?
- ensure a script is only loaded once
- used when composing layouts in a WWA (Windows Web Application)
- fragments use this to manage resources
- ... more on this later
HTML
Layout
- aria-* attributes to support accessibility
- data-win-control to specify the control type of control
- data-win-options to set properties
<body>
<div class="splitPage fragment">
<header role="banner" aria-label="Header content">
<button disabled class="win-backbutton" aria-label="Back"></button>
<h1 class="pageTitle win-title"></h1>
</header>
<section class="itemListSection">
<div class="itemList"
data-win-control="WinJS.UI.ListView"
data-win-options="{layout: {type: WinJS.UI.ListLayout}, selectionMode: 'none' }">
</div>
</section>
</div>
</body>
HTML
Templating
- Set data-win-control attribute to hide it in the DOM
- Use data-win-bind to declare binding expressions
- Bind fields on object to DOM properties
<div class="itemTemplate"
data-win-control="WinJS.Binding.Template">
<div class="largeIconTextTemplate">
<img class="avatar"
data-win-bind="src: user.profile_image_url" />
<div class="largeIconTextTemplateBackground">
<div class="largeIconTextTemplateLargeText"
data-win-bind="textContent: user.screen_name">
</div>
<div class="largeIconTextTemplateMediumText"
data-win-bind="textContent: text"></div>
</div>
</div>
</div>
HTML
Templating
- Find the list
- Set the template and data source
- Refresh the list
var lv = WinJS.UI.getControl(elements.querySelector('.itemList'));
WinJS.UI.setOptions(lv, {
dataSource: pageData.menuItems,
itemRenderer: elements.querySelector('.itemTemplate'),
oniteminvoked: menuItemInvoked
});
lv.refresh();
HTML
Templating
- Animate the inclusion of an item
var list = document.querySelector(".itemList");
var allItems = document.querySelectorAll(".item");
var newItem = document.createElement("div");
newItem.className = "item";
newItem.style.background = randomColor();
var anim = WinJS.UI.Animation.createAddToListAnimation(newItem, allItems);
list.insertBefore(newItem, list.firstChild);
anim.execute();
HTML
Navigation
- App raises fragmentappended event when content is rendered
- Attach callback to run setup code
WinJS.Application.addEventListener('fragmentappended', function handler(e) {
if (e.location === '/html/splitPage.html') {
fragmentLoad(e.fragment, e.state);
}
});
function fragmentLoad(elements, options) {
// attach listeners for layout changes
WinJS.UI.processAll(elements)
.then(function () {
// attach event handlers
// bind data
// other stuff
});
}
HTML
Navigation
- Clone the fragment from a URL
- Find the element to host the fragement
- Raise the fragementappended event
function loadPage(location, state) {
WinJS.UI.Fragments.clone(location, state).then(function (frag) {
var content = document.getElementById("content");
content.innerHTML = "";
content.appendChild(frag);
WinJS.Application.queueEvent({
type: "fragmentappended",
location: location,
fragment: content,
state: state
});
});
}
HTML
Navigation
- Start the navigation when the window activates
WinJS.Application.onmainwindowactivated = function (e) {
if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
loadPage("/fragments/somePage.html");
}
}
HTML
Navigate the page
<a href="/html/somepage.html">
navigate all the things
<a>
async and await
Why?
- Synchronous code is easy to read
- Writing good asynchronous is hard
- Maintaining/debugging asynchronous code is painful
- Task Parallel Library is full of win
- Structure tasks into composable units of work
async and await
How?
- use await in a method which is awaitable
- decorate the method signature with async
- public async void MethodFinishesWhenever()
- public async Task NoResultButWatchMethod()
- public async Task<T> MethodWithResult()
network calls
C#
using System;
using System.Net.Http;
using Windows.Data.Json;
public static async void Process(string url)
{
var client = new HttpClient();
var response = await client.GetAsync(url);
var text = response.Content.ReadAsString();
// convert to JSON
}
network calls
JavaScript
- Promises are used to represent the pattern
- Slightly different syntax
WinJS.xhr({
type: "GET",
url: someTwitterUrl
}).then(processResults);
function processResults(data) {
var tweets = JSON.parse(data.response);
// massage JSON
// update UI
}
oauth
Web Authentication Broker
- Replaces in-app browser control
- Compose URL with authentication info
- Start authenticate process
- Get result (success or failure)
oauth
C#
using System;
using Windows.Security.Authentication.Web;
public static async void Authenticate(string callback)
{
var startURI = BuildOAuthUrl();
var endURI = new Uri(callback);
var auth = WebAuthenticationOptions.Default;
var result = await WebAuthenticationBroker.AuthenticateAsync(auth, startURI, endURI);
HandleCallback(result);
}
oauth
JavaScript
var startURI = new Windows.Foundation.Uri(twitterURL);
var endURI = new Windows.Foundation.Uri(callbackURL);
var auth = Windows.Security.Authentication.Web.WebAuthenticationOptions.default;
asyncCall = Windows.Security.Authentication.Web.WebAuthenticationBroker
.authenticateAsync(auth, startURI, endURI)
.then(function (result) { handleAuthCallback(result, onSuccess, onFailure); },
onFailure });
application storage
Locations
- Local - on device
- Roaming - uses Live ID to sync with cloud
Choices
- Key-value settings
- Composite objects
- Streams
- Listen to data changed events
application storage
C#
var settings = Windows.Storage.ApplicationData.Current.LocalSettings.Values;
// add a value
settings.Add("oAuthTokenClient", oauthToken);
// find a value (returns false if not found)
object token;
var success = settings.TryGetValue("oAuthTokenClient", out token);
// remove from collection
settings.Remove("oAuthTokenClient");
application storage
JavaScript
var settings = Windows.Storage.ApplicationData.current.localSettings.values;
// add a value
settings.insert("oAuthTokenClient", oauth_token);
// find a value (returns null if not found)
var oauth_token_secret = settings.lookup("oAuthTokenClient");
// remove from collection
settings.remove("oAuthTokenClient");
application storage
JavaScript
var composite = new Windows.Storage.ApplicationDataCompositeValue();
composite["foo"] = "bar"; // composite.foo = "bar";
composite["abc"] = 1;
var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
roamingSettings.values["someKey"] = composite;
file picker
New Metro Shell -> Rewritten File Dialog
|
$deity be praised!
|
file picker
C#
public static async void ProcessImages()
{
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
picker.ViewMode = PickerViewMode.Thumbnail;
var file = await picker.PickSingleFileAsync(); // PickMultipleFilesAsync is also available
var details = await file.OpenForReadAsync();
var stream = details.AsStream();
}
file picker
JavaScript
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
// if you want to put the user at a specific folder initially
picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
picker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
openPicker.pickSingleFileAsync().then(function (file) {
if (file) {
// we have a file
} else {
// we do not
}
});
geolocation
- for tablets which have supported hardware
- can query or listen for position changes
- application must specify capability (like WP7 development)
geolocation
C#
using Windows.Devices.Geolocation;
Geolocator locator;
public static void Start()
{
locator = new Geolocator();
locator.PositionChanged += positionChanged;
}
void positionChanged(Geolocator sender, PositionChangedEventArgs args)
{
// get the location
var lat = args.Position.Coordinate.Latitude;
var long = args.Position.Coordinate.Longitude;
// get the address associated with this location
var city = args.Position.CivicAddress.City;
var state = args.Position.CivicAddress.State;
}
JavaScript
var loc = null;
function listen() {
if (!loc) {
loc = new Windows.Devices.Geolocation.Geolocator();
}
if (loc) {
loc.addEventListener("positionchanged", onPositionChanged);
} else {
console.log("Couldn't create Geolocator.");
}
}
function stop() {
if (loc) {
loc.removeEventListener("positionchanged", onPositionChanged);
loc = null;
}
}
function onPositionChanged(args) {
var pos = args.position;
}
geolocation
JavaScript
function getLocation() {
var locator = new Windows.Devices.Geolocation.Geolocator();
if (locator) {
locator.getGeopositionAsync().then(positionChanged);
} else {
console.log("Couldn't create Geolocator.");
}
}
function positionChanged(pos) {
if (pos) {
var newLocation = pos.coordinate;
}
}
tile notifications
Tile Options
- Size - small logo or wide logo
- Specify graphics in application manifest
- User can shrink your wide logo at will
tile notifications
Tile Options
- Configuration options for that tile
tile notifications
Glyphs
- Icon in bottom right of tile
- Background matches font colour
- Can be a number or from set of OS graphics
tile notifications
Create a glyph using C#
- Get template from OS
- Customise (yay, XML)
- Send to BadgeUpdateManager
void UpdateBadgeWithNumber(int number)
{
// can use BadgeTemplateType.BadgeGlyph for icons instead
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
// omitted: inject values into XML
// example of customised XML:
// <?xml version="1.0" encoding="utf-16"?><badge value="play" version="1"/>
// create and fire async
var badge = new BadgeNotification(badgeXml);
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badge);
}
tile notifications
Templates
- Custom toast notifications when user is outside app
- Include text and images
- Lots of different examples (see Advanced
tile notifications
Template Types
- TileSquareImage
- TileWideImageCollection
- TileWideImageAndText
- TileWideImage
- TileWidePeekImageAndText
- TileWidePeekImageCollection01
- TileWidePeekImageCollection02
- TileWidePeekImageCollection03
- TileWidePeekImageCollection04
- TileWidePeekImageCollection05
- TileWidePeekImageCollection06
- TileWidePeekImage01
- TileWidePeekImage02
- TileWidePeekImage03
- TileWidePeekImage04
- TileWidePeekImage05
- TileWidePeekImage06
- TileWideSmallImageAndText01
- TileWideSmallImageAndText02
- TileWideSmallImageAndText03
- TileWideSmallImageAndText04
- TileWideText01
- TileWideText02
- TileWideText03
- TileWideText04
- TileWideText05
- TileWideText06
- TileWideText07
- TileWideText08
- TileWideText09
tile notifications
Using a Template Type in C#
void UpdateTileWithText(string text)
{
var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideText03);
XmlNodeList textElements = tileXml.GetElementsByTagName("text");
textElements.Item(0).AppendChild(tileXml.CreateTextNode(text));
var tile = new TileNotification(tileXml);
TileUpdateManager.CreateTileUpdaterForApplication().Update(tile);
}
search
- APIs to enable developers to expose search to the OS
- One entry point for users
search
- Up to application to define user interface
search contract
C#
- Override the OnSearchActivated method in App.xaml.cs
- SearchResultsPage handles activation and search logic
partial class App
{
// other code
protected override void OnSearchActivated(SearchActivatedEventArgs args)
{
var searchResultsPage = new SearchResultsPage();
searchResultsPage.Activate(args);
}
}
search contract
Javascript
- Check how the application is activated
- Navigate to the search page
function activated(e) {
if (e.kind === Windows.ApplicationModel.Activation.ActivationKind.search) {
WinJS.Navigation.navigate('/search.html', { queryText: e.queryText });
}
}
Windows.UI.WebUI.WebUIApplication.addEventListener('activated', activated);
search contract
Javascript
- Sometimes the app may not be running
- Need to listen to querysubmitted event as well
try {
var pane = Windows.ApplicationModel.Search.SearchPane.getForCurrentView();
pane.addEventListener('querysubmitted', querySubmitted, false);
} catch (e) { }
// If the application is not running when a query is received, navigate to the search page.
function querySubmitted(e) {
if (WinJS.Navigation.location !== searchPage) {
WinJS.Navigation.navigate(searchPage, e);
} else {
handleQuery(document.body, e);
}
}
function handleQuery(elements, e) {
// use e.queryText and perform search
}
sharing
how?
- Application can be a source or a target
- Source specifies the data
- Target receives the data
sharing
what?
- Bitmap
- Html
- Rtf
- StorageItems (collection of files)
- Text
- Uri
- ...extensible (hint: magic strings)
sharing
source share contract in C#
- Listen to the DataTransferManager instance
- Prompt the user to share
- Populate the Data object with something to share
using Windows.ApplicationModel.DataTransfer;
public void MainPage_Loaded()
{
var manager = DataTransferManager.GetForCurrentView();
manager.DataRequested += DataRequested;
}
void DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
{
e.Request.Data.SetText(someImportantTweet);
}
void PromptUserToShare(object sender, RoutedEventArgs e)
{
// user can also select Share from the app bar to view this dialog
DataTransferManager.ShowShareUI();
}
sharing
target share contract in C#
- Override method in App.xaml.cs
protected override void OnSharingTargetActivated(ShareTargetActivatedEventArgs args)
{
var data = args.shareOperation.Data;
bool containsTextFormat;
// check what format is included
data.Contains(StandardDataFormats.Text, out containsTextFormat);
if (containsTextFormat)
{
// handle the ShareOperation content
var title = data.Properties.Title;
var desc = data.Properties.Description;
var msg = data.GetText();
// TODO: other stuff
}
}
things i had to skip
- Windows Metadata - projecting libraries to target C++, C# and JS
- Background Tasks
- Touch - raw input and controls (think Surface/WPF4)
- Layout Changes - Snapped vs Filled vs Fullscreen
- Orientation, Resolution Changes
- Socket Programming
- Push Notifications
- Device Sensors APIs - Accelerometer, Gyro, Compasss, Light, Orientation
- DirectX and Direct 3D
- Windows Store
- Mobile Network APIs
- SMS APIs
- MVVM
- ...
fin
Other Links
questions?
beer?
←
→
/
#