PUBLISHED BY M crosoft Press A D v s on of M crosoft Corporat on One M crosoft Way Redmond, Wash ngton 98052-6399 Copyr ght © 2010 by M crosoft Corporat on (A ) A r ghts reserved No part of the contents of th s book may be reproduced or transm tted n any form or by any means w thout the wr tten perm ss on of the pub sher L brary of Congress Contro Number 2010941604 ISBN 978-0-7356-5667-3 Pr nted and bound n the Un ted States of Amer ca M crosoft Press books are ava ab e through bookse ers and d str butors wor dw de For further nformat on about nternat ona ed t ons, contact your oca M crosoft Corporat on office or contact M crosoft Press Internat ona d rect y at fax (425) 936-7329 Te us what you th nk of th s book at http //www m crosoft com/ earn ng/booksurvey Send comments to msp nput@m crosoft com M crosoft and the trademarks sted at http //www m crosoft com/about/ ega /en/us/Inte ectua Property/ Trademark/EN-US aspx are trademarks of the M crosoft group of compan es A other marks are the property of the r respect ve owners The examp e compan es, organ zat ons, products, doma n names, e-ma addresses, ogos, peop e, p aces, and events dep cted here n are fict t ous No assoc at on w th any rea company, organ zat on, product, doma n name, e-ma address, ogo, person, p ace, or event s ntended or shou d be nferred Th s book expresses the author’s v ews and op n ons The nformat on conta ned n th s book s prov ded w thout any express, statutory, or mp ed warrant es Ne ther the authors, M crosoft Corporat on, nor ts rese ers, or d str butors w be he d ab e for any damages caused or a eged to be caused e ther d rect y or nd rect y by th s book Acquisitions Editor: Devon Musgrave Developmental Editor: Devon Musgrave Project Editor: Devon Musgrave Editorial Production: Ash ey Schne der, S4Car s e Pub sh ng Serv ces Technical Reviewer: Per B omqv st; Techn ca Rev ew Serv ces prov ded by Content Master, a member of CM Group, Ltd Body Part No X17-35780
Contents at a Glance Part I
1 2 3 4 5 6 Part II
7 8 9 10 11 12 13 14 15 16 17 18
The Basics Hello, Windows Phone 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Getting Oriented. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 An Introduction to Touch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Bitmaps, Also Known as Textures . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Sensors and Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Issues in Application Architecture . . . . . . . . . . . . . . . . . . . . . . . . 107
Silverlight XAML Power and Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elements and Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Intricacies of Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The App Bar and Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dependency Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Data Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vector Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Raster Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Two Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Items Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pivot and Panorama . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
143 171 197 235 297 339 393 459 505 573 633 701
iii
Table of Contents Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xi
Part I
1
The Basics Hello, Windows Phone 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Targeting Windows Phone 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 The Hardware Chassis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Sensors and Services. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 File | New | Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 A First Silverlight Phone Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 The Standard Silverlight Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Color Themes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Points and Pixels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 The XAP is a ZIP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 An XNA Program for the Phone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2
Getting Oriented. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Silverlight and Dynamic Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Orientation Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 XNA Orientation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Simple Clocks (Very Simple Clocks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3
An Introduction to Touch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Low-Level Touch Handling in XNA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 The XNA Gesture Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Low-Level Touch Events in Silverlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 The Manipulation Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Routed Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Some Odd Behavior? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
What do you think of this book? We want to hear from you! M crosoft s nterested n hear ng your feedback so we can cont nua y mprove our books and earn ng resources for you. To part c pate n a br ef on ne survey, p ease v s t:
microsoft.com/learning/booksurvey v
vi
Table of Contents
4
Bitmaps, Also Known as Textures . . . . . . . . . . . . . . . . . . . . . . . . . . 65 XNA Texture Drawing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 The Silverlight Image Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Images Via the Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Image and ImageSource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Loading Local Bitmaps from Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Capturing from the Camera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 The Phone’s Photo Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5
Sensors and Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Accelerometer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 A Simple Bubble Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Geographic Location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Using a Map Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6
Issues in Application Architecture . . . . . . . . . . . . . . . . . . . . . . . . 107 Basic Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Passing Data to Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 Sharing Data Among Pages. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 Retaining Data across Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 The Multitasking Ideal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Task Switching on the Phone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Page State. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 Isolated Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Xna Tombstoning and Settings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Testing and Experimentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Part II
7
Silverlight XAML Power and Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 A TextBlock in Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Property Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Property-Element Syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Colors and Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Content and Content Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 The Resources Collection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Sharing Brushes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 x:Key and x:Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 An Introduction to Styles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Table of Contents
Style Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 Themes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Gradient Accents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
8
Elements and Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Basic Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Animating at the Speed of Video. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Handling Manipulation Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 The Border Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 TextBlock Properties and Inlines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 More on Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Playing Movies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Modes of Opacity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Non-Tiled Tile Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
9
The Intricacies of Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 The Single-Cell Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 The StackPanel Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Text Concatenation with StackPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Nested Panels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Visibility and Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Two ScrollViewer Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 The Mechanism of Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Inside the Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 A Single-Cell Grid Clone. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 A Custom Vertical StackPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 The Retro Canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 Canvas and ZIndex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 The Canvas and Touch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 The Mighty Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
10
The App Bar and Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 ApplicationBar Icons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Jot and Application Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 Jot and Touch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 Jot and the ApplicationBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 Elements and Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 RangeBase and Slider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
vii
The Basic Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 The Concept of Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 Theme Styles and Precedence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 The Button Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Toggling a Stopwatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Buttons and Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 TextBox and Keyboard Input. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
11
Dependency Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 The Problem Illustrated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 The Dependency Property Difference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Deriving from UserControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 A New Type of Toggle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 Panels with Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 Attached Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
12
Data Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 Source and Target . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 Target and Mode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 Binding Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 Relative Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 The “this” Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 Notification Mechanisms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 A Simple Binding Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 Setting the DataContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 Simple Decision Making . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366 Converters with Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370 Give and Take. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 TextBox Binding Updates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
13
Vector Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 The Shapes Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 Canvas and Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 Overlapping and ZIndex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396 Polylines and Custom Curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 Caps, Joins, and Dashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 Polygon and Fill. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 The Stretch Property. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 Dynamic Polygons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Table of Contents
The Path Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 Geometries and Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Grouping Geometries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 The Versatile PathGeometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428 The ArcSegment. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 Bézier Curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 The Path Markup Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 How This Chapter Was Created . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
14
Raster Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 The Bitmap Class Hierarchy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 WriteableBitmap and UIElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 The Pixel Bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468 Vector Graphics on a Bitmap. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 Images and Tombstoning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478 Saving to the Picture Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 Becoming a Photo Extras Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
15
Animations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 Frame-Based vs. Time-Based . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 Animation Targets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508 Click and Spin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509 Some Variations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 XAML-Based Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 A Cautionary Tale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519 Key Frame Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 Trigger on Loaded. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 Animating Attached Properties (or Not) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 Splines and Key Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543 The Bouncing Ball Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 The Easing Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 Animating Perspective Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562 Animations and Property Precedence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
16
The Two Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 ContentControl and DataTemplate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 Examining the Visual Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578 ControlTemplate Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583 The Visual State Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
ix
x
Table of Contents
Sharing and Reusing Styles and Templates . . . . . . . . . . . . . . . . . . . . . . . . . . 603 Custom Controls in a Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 Variations on the Slider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612 The Ever-Handy Thumb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622 Custom Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
17
Items Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 Items Controls and Visual Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634 Customizing Item Displays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641 ListBox Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645 Binding to ItemsSource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649 Databases and Business Objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655 Fun with DataTemplates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 Changing the Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 The DataTemplate Bar Chart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 A Card File Metaphor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
18
Pivot and Panorama . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701 Compare and Contrast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701 Music by Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712 The XNA Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 The XNA Music Classes: MediaLibrary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717 Displaying the Albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723 The XNA Music Classes: MediaPlayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
What do you think of this book? We want to hear from you! M crosoft s nterested n hear ng your feedback so we can cont nua y mprove our books and earn ng resources for you. To part c pate n a br ef on ne survey, p ease v s t:
microsoft.com/learning/booksurvey
Introduction Important Th s book and Microsoft XNA Framework Edition: Programming Windows Phone 7
are fu y ndexed, pr nt-book vers ons of a s ng e free, e ectron c ed t on t t ed Programming Windows Phone 7, wh ch you can find on the M crosoft Press b og http://blogs.msdn.com/b/ microsoft press/. No changes have been made to the or g na ed t on’s text, nc ud ng references to the co or of the or g na mages, wh ch appear b ack and wh te n th s book What fo ows s the Introduct on that or g na y appeared n Programming Windows Phone 7; “Code Samp es” s the on y sect on n th s Introduct on that has been updated
This book is a gift from the Windows Phone 7 team at Microsoft to the programming community, and I am proud to have been a part of it. Within the pages that follow, I show you the basics of writing applications for Windows Phone 7 using the C# programming language with the Silverlight and XNA 2D frameworks. Yes, Programming Windows Phone 7 is truly a free download, but for those readers who still love paper—as I certainly do—this book will also be available (for sale) divided into two fully-indexed print editions: Microsoft Silverlight Edition: Programming Windows Phone 7 and Microsoft XNA Framework Edition: Programming Windows Phone 7. With the money you’ve saved downloading this book, please buy other books. Despite the plethora of information available online, books are still the best way to learn about programming within a coherent and cohesive tutorial narrative. Every book sale brings a tear of joy to an author’s eye, so please help make them weep overflowing rivers. In particular, you might want to buy other books to supplement the material in this book. For example, I barely mention Web services in this book, and that’s a serious deficiency because Web services are likely to become increasingly important in Windows Phone 7 applications. My coverage of XNA is limited to 2D graphics and while I hope to add several 3D chapters in the next edition of this book, I don’t really get into the whole Xbox LIVE community aspect of game development. Nor do I discuss any programming tools beyond Visual Studio—not even Expression Blend. My publisher Microsoft Press has a couple additional Windows Phone 7 books coming soon: Windows Phone 7 Silverlight Development Step by Step by Andy Wigley & Peter Foot offers a more tools-oriented approach. Although Michael Stroh’s Windows Phone 7 Plain & Simple is a guide to using the phone rather than developing for it, I suspect it will give developers some insights and ideas. Moreover, I also hear that my old friend Doug Boling is working hard on a Windows Phone 7 enterprise-programming book that is likely to be considered his masterpiece. Be sure to check out that one. xi
xii
Introduction
Organization This book is divided into three parts. The first part discusses basic concepts of Windows Phone 7 programming using example programs that target both Silverlight and the XNA framework. It is likely that many Windows Phone 7 developers will choose either one platform or the other, but I think it’s important for all developers who have at least a little knowledge of the alternative to their chosen path. The second part of this book focuses entirely on Silverlight, and the third part on XNA 2D. For your convenience, the chapters in each part build upon previous knowledge in a progressive tutorial narrative, and hence are intended to be read sequentially.
My Assumptions About You I assume that you know the basic principles of .NET programming and you have a working familiarity with the C# programming language. If not, you might benefit from reading my free online book .NET Book Zero: What the C or C++ Programmer Needs to Know about C# and the .NET Framework, available from my website at www.charlespetzold.com/dotnet.
System Requirements To use this book properly you’ll need to download and install the Windows Phone Developer Tools, which includes Visual Studio 2010 Express for Windows Phone, XNA Game Studio 4.0, and an on-screen Windows Phone Emulator to test your programs in the absence of an actual device. Get the latest information and downloads at http://developer .windowsphone.com. You can install these tools on top of Visual Studio 2010, in effect enhancing Visual Studio 2010 for phone development. That’s the configuration I used. Although you can do quite a bit with the phone emulator, at some point you’ll want to deploy your programs to an actual Windows Phone 7 device. You can register as a phone developer at http://developer.windowsphone.com and then have the ability to unlock your phone so you can deploy your programs from Visual Studio. Since late July 2010, I’ve had an LG GW910 phone to test the programs in this book. For the record, the final build I installed was 7.0.7003.0.
Using the Phone Emulator Windows Phone 7 supports multi-touch, and working with multi-touch is an important part of developing programs for the phone. When using the Windows Phone Emulator, mouse clicks and mouse movement on the PC can mimic touch on the emulator, but for only one
Introduction
xiii
finger. You can test out multi-touch for real on the phone emulator if you have a multi-touch monitor running under Windows 7. In the absence of a multi-touch monitor, you might want to explore simulating multi-touch with multiple mouse devices. The site http://multitouchvista.codeplex.com has the download you’ll need and includes a link to http://michaelsync.net/2010/04/06/step-by-step-tutorialinstalling-multi-touch-simulator-for-silverlight-phone-7 that provides instructions. Windows Phone 7 devices also have a built-in accelerometer, which can be very difficult to simulate in an emulator. Per Blomqvist, the Technical Reviewer for this book, found an application at http://accelkit.codeplex.com that utilizes the webcam and ARToolkit to emulate the accelerometer sensor and feed that data into the Windows Phone 7 emulator through a TCP/HTTP Server, and although neither of us have tried it out, it sounds quite intriguing.
Code Samples To illustrate Silverlight and XNA programming concepts, this book describes about 190 complete programs. Many of them are small and simple, but others are larger and more interesting. Some people like to learn new programming environments by re-creating the projects in Visual Studio and typing in the source code themselves from the pages of the book. Others prefer to study the code and run the pre-existing programs to see what the code does. If you fall into the latter category, you can download all the source code in a ZIP file via the Companion Content link at http://oreilly.com/catalog/0790145316707/. If you find something in the code that is useful in your own software project, feel free to use the code without restriction—either straight up or modified in whatever way you want. That’s what it’s there for.
Last-Minute Items As I was nearing the completion this book, the first version of the Silverlight for Windows Phone Toolkit was released with some additional elements and controls, and is available for downloading at http://silverlight.codeplex.com. Historically, these Silverlight toolkits very often contain previews of elements and controls that are incorporated into later Silverlight releases. I regret that I could not include a discussion of the toolkit contents in the appropriate chapters of this book. With XNA programs, sometimes Visual Studio complains that it can’t build or deploy the program. If you encounter that problem, in the Solution Platforms drop-down list on the standard toolbar, select “Windows Phone” rather than “Any CPU”. Or, invoke the
xiv
Introduction
Configuration Manager from the Build menu, and in the Active Solution Platform drop-down select “Windows Phone” rather than “Any CPU”. The www.charlespetzold.com/phone page on my website will contain information about this book and perhaps even some information about a future edition. I also hope to blog about Windows Phone 7 programming as much as possible.
The Essential People This book owes its existence to Dave Edson—an old friend from the early 1990s era of Microsoft Systems Journal—who had the brilliant idea that I would be the perfect person to write a tutorial on Windows Phone 7. Dave arranged for me to attend a technical deep dive on the phone at Microsoft in December 2009, and I was hooked. Todd Brix gave the thumbs up on the book, and Anand Iyer coordinated the project with Microsoft Press. At Microsoft Press, Ben Ryan launched the project and Devon Musgrave had the unenviable job of trying to make my code and prose resemble an actual book. (We all go way back: You’ll see Ben and Devon’s names on the bottom of the copyright page of Programming Windows, fifth edition, published in 1998.) My Technical Reviewer was the diligent Per Blomqvist, who apparently tested all the code in both the sample files and as the listings appear in the book, and who in the process caught several errors on my part that were truly, well, shocking. Dave Edson also reviewed some chapters and served as conduit to the Windows Phone team to deal with my technical problems and questions. Early on, Aaron Stebner provided essential guidance; Michael Klucher reviewed chapters, and Kirti Deshpande, Charlie Kindel, Casey McGee, and Shawn Oster also had important things to tell me. Thanks to Bonnie Lehenbauer for reviewing a chapter. I am also indebted to Shawn Hargreaves for his XNA expertise, and Yochay Kiriaty and Richard Bailey for the lowdown on tombstoning. My wife Deirdre Sinnott has been a marvel of patience and tolerance over the past months as she dealt with an author given to sudden mood swings, insane yelling at the computer screen, and the conviction that the difficulty of writing a book relieves one of the responsibility of performing basic household chores. Alas, I can’t blame any of them for bugs or other problems that remain in this book. Those are all mine. Charles Petzold New York City and Roscoe, New York October 22, 2010
Introduction
xv
Errata & Book Support We’ve made every effort to ensure the accuracy of this book and its companion content. If you do find an error, e-mail Microsoft Press Book Support at
[email protected]. (Please note that product support for Microsoft software is not offered through this address.)
We Want to Hear from You At Microsoft Press, your satisfaction is our top priority, and your feedback our most valuable asset. Please tell us what you think of this book at: http://www.microsoft.com/learning/booksurvey The survey is short, and we read every one of your comments and ideas. Thanks in advance for your input.
Stay in Touch Let’s keep the conversation going! We’re on Twitter: http://twitter.com/MicrosoftPress
Part I
The Basics
1
Chapter 1
Hello, Windows Phone 7 Sometimes it becomes apparent that previous approaches to a problem haven’t quite worked the way you anticipated. Perhaps you just need to clear away the smoky residue of the past, take a deep breath, and try again with a new attitude and fresh ideas. In golf, it’s known as a “mulligan”; in schoolyard sports, it’s called a “do-over”; and in the computer industry, we say it’s a “reboot.” A reboot is what Microsoft has initiated with its new approach to the mobile phone market. With its clean look, striking fonts, and new organizational paradigms, Microsoft Windows Phone 7 not only represents a break with the Windows Mobile past but also differentiates itself from other smartphones currently in the market. Windows Phone 7 devices will be made by several manufacturers and available with a variety of cell providers. For programmers, Windows Phone 7 is also exciting, for it supports two popular and modern programming platforms: Silverlight and XNA. Silverlight—a spinoff of the client-based Windows Presentation Foundation (WPF)—has already given Web programmers unprecedented power to develop sophisticated user interfaces with a mix of traditional controls, high-quality text, vector graphics, media, animation, and data binding that run on multiple platforms and browsers. Windows Phone 7 extends Silverlight to mobile devices. XNA—the three letters stand for something like “XNA is Not an Acronym”—is Microsoft’s game platform supporting both 2D sprite-based and 3D graphics with a traditional game-loop architecture. Although XNA is mostly associated with writing games for the Xbox 360 console, developers can also use XNA to target the PC itself, as well as Microsoft’s classy audio player, the Zune HD. Either Silverlight or XNA would make good sense as the sole application platform for the Windows Phone 7, but programmers have a choice. And this we call “an embarrassment of riches.”
Targeting Windows Phone 7 All programs for Windows Phone 7 are written in .NET managed code. Although the sample programs in this book are written in the C# programming language, it is also possible to write Windows Phone 7 applications in Visual Basic .NET. The free downloadable Microsoft Visual Studio 2010 Express for Windows Phone includes XNA Game Studio 4.0 and an on-screen phone emulator, and also integrates with Visual Studio 2010. You can develop visuals and animations for Silverlight applications using Microsoft Expression Blend. 3
4
Part I
The Basics
The Silverlight and XNA platforms for Windows Phone 7 share some libraries, and you can use some XNA libraries in a Silverlight program and vice versa. But you can’t create a program that mixes visuals from both platforms. Maybe that will be possible in the future, but not now. Before you create a Visual Studio project, you must decide whether your million-dollar idea is a Silverlight program or an XNA program. Generally you’ll choose Silverlight for writing programs you might classify as applications or utilities. These programs are built from a combination of markup and code. The markup is the Extensible Application Markup Language, or XAML and pronounced “zammel.” The XAML mostly defines a layout of user-interface controls and panels. Code-behind files can also perform some initialization and logic, but are generally relegated to handling events from the controls. Silverlight is great for bringing to the Windows Phone the style of Rich Internet Applications (RIA), including media and the Web. Silverlight for Windows Phone is a version of Silverlight 3 excluding some features not appropriate for the phone, but compensating with some enhancements. XNA is primarily for writing high-performance games. For 2D games, you define sprites and backgrounds based around bitmaps; for 3D games you define models in 3D space. The action of the game, which includes moving graphical objects around the screen and polling for user input, is synchronized by the built-in XNA game loop. The differentiation between Silverlight-based applications and XNA-based games is convenient but not restrictive. You can certainly use Silverlight for writing games and you can even write traditional applications using XNA, although doing so might sometimes be challenging. In particular, Silverlight might be ideal for games that are less graphically oriented, or use vector graphics rather than bitmap graphics, or are paced by user-time rather than clock-time. A Tetris-type program might work quite well in Silverlight. You’ll probably find XNA to be a bit harder to stretch into Silverlight territory, however. Implementing a list box in XNA might be considered “fun” by some programmers but a torture by many others. The first several chapters in this book describe Silverlight and XNA together, and then the book splits into different parts for the two platforms. I suspect that some developers will stick with either Silverlight or XNA exclusively and won’t even bother learning the other environment. I hope that’s not a common attitude. The good news is that Silverlight and XNA are so dissimilar that you can probably bounce back and forth between them without confusion! Microsoft has been positioning Silverlight as the front end or “face” of the cloud, so cloud services and Windows Azure form an important part of Windows Phone 7 development. The Windows Phone is “cloud-ready.” Programs are location-aware and have access to maps and other data through Bing and Windows Live. One of the available cloud services is Xbox Live,
Chapter 1
Hello, Windows Phone 7
5
which allows XNA-based programs to participate in online multiplayer games, and can also be accessed by Silverlight applications. Programs you write for the Windows Phone 7 will be sold and deployed through the Windows Phone Marketplace, which provides registration services and certifies that programs meet minimum standards of reliability, efficiency, and good behavior. I’ve characterized Windows Phone 7 as representing a severe break with the past. If you compare it with past versions of Windows Mobile, that is certainly true. But the support of Silverlight, XNA, and C# are not breaks with the past, but a balance of continuity and innovation. As young as they are, Silverlight and XNA have already proven themselves as powerful and popular platforms. Many skilled programmers are already working with either one framework or the other—probably not so many with both just yet—and they have expressed their enthusiasm with a wealth of online information and communities. C# has become the favorite language of many programmers (myself included), and developers can use C# to share libraries between their Silverlight and XNA programs as well as programs for other .NET environments.
The Hardware Chassis Developers with experience targeting Windows Mobile devices of the past will find significant changes in Microsoft’s strategy for the Windows Phone 7. Microsoft has been extremely proactive in defining the hardware specification, often referred to as a “chassis.” Initial releases of Windows Phone 7 devices will have one consistent screen size. (A second screen size is expected in the future.) Many other hardware features are guaranteed to exist on each device. The front of the phone consists of a multi-touch display and three hardware buttons generally positioned in a row below the display. From left to right, these buttons are called Back, Start, and Search:
■
Back Programs can use this button for their own navigation needs, much like the Back button on a Web browser. From the home page of a program, the button causes the program to terminate.
■
Start This button takes the user to the start screen of the phone; it is otherwise inaccessible to programs running on the phone.
■
Search The operating system uses this button to initiate a search feature.
Chapter 1
Hello, Windows Phone 7
7
landscape mode, the small screen has an aspect ratio of 3:2 or 1.5; the large screen has an aspect ratio of 5:3 or 1.66. . . . Neither of these matches the aspect ratio of television, which for standard definition is 4:3 or 1.33. . . and for high-definition is 16:9 or 1.77. . . . The Zune HD screen has an aspect ratio of 16:9. Like many recent phones and the Zune HD, the Windows Phone 7 displays will likely use OLED (“organic light emitting diode”) technology, although this isn’t a hardware requirement. OLEDs are different from flat displays of the past in that power consumption is proportional to the light emitted from the display. For example, an OLED display consumes less than half the power of an LCD display of the same size, but only when the screen is mostly black. For an all-white screen, an OLED consumes more than three times the power of an LCD. Because battery life is extremely important on mobile devices, this characteristic of OLED displays implies an aesthetic of mostly black backgrounds with sparse graphics and light-stroked fonts. Regardless, Windows Phone 7 users can choose between two major color themes: light text on a dark background, or dark text on a light background. Most user input to a Windows Phone 7 program will come through multi-touch. The screens incorporate capacitance-touch technology, which means that they respond to a human fingertip but not to a stylus or other forms of pressure. Windows Phone 7 screens are required to respond to at least four simultaneous touch-points. A hardware keyboard is optional. Keep in mind that phones can be designed in different ways, so when the keyboard is in use, the screen might be in either portrait mode or landscape mode. A Silverlight program that uses keyboard input must respond to orientation changes so that the user can both view the screen and use the keyboard without wondering what idiot designed the program sideways. An on-screen keyboard is also provided, known in Windows circles as the Soft Input Panel or SIP. XNA programs also have access to the hardware keyboard and SIP.
Sensors and Services A Windows Phone 7 device is required to contain several other hardware features— sometimes called sensors—and provide some software services, perhaps through the assistance of hardware. These are the ones that affect developers the most: ■
Wi-Fi The phone has Wi-Fi for Internet access to complement 3G data access through the cell provider. Software on the phone includes a version of Internet Explorer.
■
Camera The phone has at least a 5-megapixel camera with flash. Programs can invoke the camera program for their own input, or register themselves as a Photos Extra Application and appear on a menu to obtain access to photographed images, perhaps for some image processing.
8
Part I
The Basics
■
Accelerometer An accelerometer detects acceleration, which in physics is a change in velocity. When the camera is still, the accelerometer responds to gravity. Programs can obtain a three-dimensional vector that indicates how the camera is oriented with respect to the earth. The accelerometer can also detect sharp movements of the phone.
■
Location If the user so desires, the phone can use multiple strategies for determining where it is geographically located. The phone supplements a hardware GPS device with information from the Web or cell phone towers. If the phone is moving, course and speed might also be available.
■
Vibration
The phone can be vibrated through program control.
■
FM Radio
An FM Radio is available and accessible through program control.
■
Push Notifications Some Web services would normally require the phone to frequently poll the service to obtain updated information. This can drain battery life. To help out, a push notification service has been developed that will allow any required polling to occur outside the phone and for the phone to receive notifications only when data has been updated.
File | New | Project I’ll assume that you have Visual Studio 2010 Express for Windows Phone installed, either by itself or supplementing a regular version of Visual Studio 2010. For convenience, I’m going to refer to this development environment simply as “Visual Studio.” The traditional “hello, world” program that displays just a little bit of text might seem silly to nonprogrammers, but programmers have discovered that such a program serves at least two useful purposes: First, the program provides a way to examine how easy (or ridiculously complex) it is to display a simple text string. Second, it gives the programmer an opportunity to experience the process of creating, compiling, and running a program without a lot of distractions. When developing programs that run on a mobile device, this process is a little more complex than customary because you’ll be creating and compiling programs on the PC but you’ll be deploying and running them on an actual phone or at least an emulator. This chapter presents programs for both Microsoft Silverlight and Microsoft XNA that display the text “Hello, Windows Phone 7!” Just to make these programs a little more interesting, I want to display the text in the center of the display. The Silverlight program will use the background and foreground colors selected by the user in the Themes section of the phone’s Settings screen. In the XNA program, the text will be white on a dark background to use less power on OLED. If you’re playing along, it’s time to bring up Visual Studio and from the File menu select New and then Project.
Chapter 1
Hello, Windows Phone 7
9
A First Silverlight Phone Program In the New Project dialog box, on the left under Installed Templates, choose Visual C# and then Silverlight for Windows Phone. In the middle area, choose Windows Phone Application. Select a location for the project, and enter the project name: SilverlightHelloPhone. As the project is created you’ll see an image of a large-screen phone in portrait mode with a screen area 480 × 800 pixels in size. This is the design view. Although you can interactively pull controls from a toolbox to design the application, I’m going to focus instead on showing you how to write your own code and markup. Several files have been created for this SilverlightHelloPhone project and are listed under the project name in the Solution Explorer over at the right. In the Properties folder are three files that you can usually ignore when you’re just creating little sample Silverlight programs for the phone. Only when you’re actually in the process of making a real application do these files become important. However, you might want to open the WMAppManifest.xml file. In the App tag near the top, you’ll see the attribute: Title="SilverlightHelloPhone"
That’s just the project name you selected. Insert some spaces to make it a little friendlier: Title="Silverlight Hello Phone"
This is the name used by the phone and the phone emulator to display the program in the list of installed applications presented to the user. If you’re really ambitious, you can also edit the ApplicationIcon.png and Background.png files that the phone uses to visually symbolize the program. The SplashScreenImage.jpg file is what the program displays as it’s initializing. In the standard Visual Studio toolbar under the program’s menu, you’ll see a drop-down list probably displaying “Windows Phone 7 Emulator.” The other choice is “Windows Phone 7 Device.” This is how you deploy your program to either the emulator or an actual phone connected to your computer via USB. Just to see that everything’s working OK, select Windows Phone 7 Emulator and press F5 (or select Start Debugging from the Debug menu). Your program will quickly build and in the status bar you’ll see the text “Connecting to Windows Phone 7 Emulator. . .” The first time you use the emulator during a session, it might take a little time to start up. If you leave the emulator running between edit/build/run cycles, Visual Studio doesn’t need to establish this connection again. Soon the phone emulator will appear on the desktop and you’ll see the opening screen, followed soon by this little do-nothing Silverlight program as it is deployed and run on the emulator. On the phone you’ll see pretty much the same image you saw in the design view.
10
Part I
The Basics
The phone emulator has a little floating menu at the upper right that comes into view when you move the mouse to that location. You can change orientation through this menu, or change the emulator size. By default, the emulator is displayed at 50% actual size, about the same size as the image on this page. When you display the emulator at 100%, it becomes enormous, and you might wonder “How will I ever fit a phone this big into my pocket?” The difference involves pixel density. Your computer screen probably has about 100 pixels per inch. (By default, Windows assumes that screens are 96 DPI.) The screen on an actual Windows Phone 7 device is more than 2½ times that. When you display the emulator at 100%, you’re seeing all the pixels of the phone’s screen, but at about 250% their actual size. You can terminate execution of this program and return to editing the program either though Visual Studio (using Shift-F5 or by selecting Stop Debugging from the Debug menu) or by clicking the Back button on the emulator. Don’t exit the emulator itself by clicking the X at the top of the floating menu! Keeping the emulator running will make subsequent deployments go much faster. While the emulator is still running, it retains all programs deployed to it. If you click the arrow at the upper-right of the Start screen, you’ll get a list that will include this program identified by the text “Silverlight Hello Phone” and you can run the program again. The program will disappear from this list when you exit the emulator. If you have a Windows Phone 7 device, you’ll need to register for the marketplace at the Windows Phone 7 portal, http://developer.windowsphone.com. After you’re approved, you’ll
Chapter 1
Hello, Windows Phone 7
15
Visual Studio itself. The DesignerWidth and DesignerHeight attributes are ignored during compilation. The compilation of the program generates a file name MainPage.g.cs that contains another partial class definition for MainPage (you can look at it in the \obj\Debug subdirectory) with the InitializeComponent method called from the constructor in MainPage.xaml.cs. In theory, the App.g.cs and MainPage.g.cs files generated during the build process are solely for internal use by the compiler and can be ignored by the programmer. However, sometimes when a buggy program raises an exception, one of these files comes popping up into view. It might help your understanding of the problem to have seen these files before they mysteriously appear in front of your face. However, don’t try to edit these files to fix the problem! The real problem is probably somewhere in the corresponding XAML file. In the root element of MainPage.xaml you’ll see settings for FontFamily, FontSize, and Foreground that apply to the whole page. I’ll describe StaticResource and this syntax in Chapter 7. The body of the MainPage.xaml file contains several nested elements named Grid, StackPanel, and TextBlock in a parent-child hierarchy. Notice the word I used: element. In Silverlight programming, this word has two related meanings. It’s an XML term used to indicate items delimited by start tags and end tags. But it’s also a word used in Silverlight to refer to visual objects, and in fact, the word element shows up in the names of two actual Silverlight classes. Many of the classes you use in Silverlight are part of this important class hierarchy: Object DependencyObject (abstract) UIElement (abstract) FrameworkElement (abstract) Besides UIElement, many other Silverlight classes derive from DependencyObject. But UIElement has the distinction of being the class that has the power to appear as a visual object on the screen and to receive user input. (In Silverlight, all visual objects can receive user input.) Traditionally, this user input comes from the keyboard and mouse; on the phone, most user input comes from touch. The only class that derives from UIElement is FrameworkElement. The distinction between these two classes is a historical artifact of the Windows Presentation Foundation. In WPF, it is possible for developers to create their own unique frameworks by deriving from UIElement. In Silverlight this is not possible, so the distinction is fairly meaningless. One of the classes that derives from FrameworkElement is Control, a word more common than element in traditional graphical user-interface programming. Some objects commonly
16
Part I
The Basics
referred to as controls in other programming environments are more correctly referred to as elements in Silverlight. Control derivatives include buttons and sliders that I’ll discuss in Chapter 10. Another class that derives from FrameworkElement is Panel, which is the parent class to the Grid and StackPanel elements you see in MainPage.xaml. Panels are elements that can host multiple children and arrange them in particular ways on the screen. I’ll discuss panels in more depth in Chapter 9. Another class that derives from FrameworkElement is TextBlock, the element you’ll use most often in displaying blocks of text up to about a paragraph in length. The two TextBlock elements in MainPage.xaml display the two chunks of title text in a new Silverlight program. PhoneApplicationPage, Grid, StackPanel, and TextBlock are all Silverlight classes. In Markup these become XML elements. Properties of these classes become XML attributes. The nesting of elements in MainPage.xaml is said to define a visual tree. In a Silverlight program for Windows Phone 7, the visual tree always begins with an object of type PhoneApplicationFrame, which occupies the entire visual surface of the phone. A Silverlight program for Windows Phone 7 always has one and only one instance of PhoneApplicationFrame, referred to informally as the frame. In contrast, a program can have multiple instances of PhoneApplicationPage, referred to informally as a page. At any one time, the frame hosts one page, but lets you navigate to the other pages. By default, the page does not occupy the full display surface of the frame because it makes room for the system tray (also known as the status bar) at the top of the phone. Our simple application has only one page, appropriately called MainPage. This MainPage contains a Grid, which contains a StackPanel with a couple TextBlock elements, and another Grid, all in a hierarchical tree. The visual tree of a Silverlight program creates by Visual Studio is: PhoneApplicationFrame PhoneApplicationPage Grid named “LayoutRoot” StackPanel named “TitlePanel” TextBlock named “ApplicationTitle” TextBlock named “PageTitle” Grid named “ContentPanel” Our original goal was to create a Silverlight program that displays some text in the center of the display, but given the presence of a couple titles, let’s amend that goal to displaying the
18
Part I
The Basics
This screen shot—and most of the remaining screen shots in this book—are shown on the pages of this book with a size that approximates the size of the actual phone, surrounded by some simple “chrome” that symbolizes either the actual phone or the phone emulator. As simple as it is, this program demonstrates some essential concepts of Silverlight programming, including dynamic layout. The XAML file defines a layout of elements in a visual tree. These elements are capable of arranging themselves dynamically. The HorizontalAlignment and VerticalAlignment properties can put an element in the center of another element, or (as you might suppose) along one of the edges or in one of the corners. TextBlock is one of a number of possible elements you can use in a Silverlight program; others include bitmap images, movies, and familiar controls like buttons, sliders, and list boxes.
Color Themes From the Start screen of the phone or phone emulator, click or touch the right arrow at the upper right and navigate to the Settings page and then select Theme. A Windows Phone 7 theme consists of a Background and an Accent color. For the Background you can select either Dark (light text on a dark background, which you’ve been seeing) or Light (the opposite). Select the Light theme, run SilverlightHelloPhone again, and express some satisfaction that the theme colors are automatically applied:
Chapter 1
Hello, Windows Phone 7
19
Although these colors are applied automatically, you’re not stuck with them in your application. If you’d like the text to be displayed in a different color, you can try setting the Foreground attribute in the TextBlock tag, for example: Foreground="Red"
You can put it anywhere in the tag as long as you leave spaces on either side. As you type this attribute, you’ll see a list of colors pop up. Silverlight supports the 140 color names supported by many browsers, as well as a bonus 141st color, Transparent. In a real-world program, you’ll want to test out any custom colors with the available themes so text doesn’t mysteriously disappear or becomes hard to read.
Points and Pixels Another property of the TextBlock that you can easily change is FontSize: FontSize="36"
But what exactly does this mean? All dimensions in Silverlight are in units of pixels, and the FontSize is no exception. When you specify 36, you get a font that from the top of its ascenders to the bottom of its descenders measures approximately 36 pixels.
20
Part I
The Basics
But fonts are never this simple. The resultant TextBlock will actually have a height more like 48 pixels—about 33% higher than the FontSize would imply. This additional space (called leading) prevents successive lines of text from jamming against each other. Traditionally, font sizes are expressed in units of points. In classical typography, a point is very close to 1/72nd inch but in digital typography the point is often assumed to be exactly 1/72nd inch. A font with a size of 72 points measures approximately an inch from the top of its characters to the bottom. (I say “approximately” because the point size indicates a typographic design height, and it’s really the creator of the font who determines exactly how large the characters of a 72-point font should be.) How do you convert between pixels and points? Obviously you can’t except for a particular output device. On a 600 dots-per-inch (DPI) printer, for example, the 72-point font will be 600 pixels tall. Desktop video displays in common use today usually have a resolution somewhere in the region of 100 DPI. For example, consider a 21” monitor that displays 1600 pixels horizontally and 1200 pixels vertically. That’s 2000 pixels diagonally, which divided by 21” is about 95 DPI. By default, Microsoft Windows assumes that video displays have a resolution of 96 DPI. Under that assumption, font sizes and pixels are related by the following formulas: points
¾ × pixels
pixels
4/3 × points
Although this relationship applies only to common video displays, people so much enjoy having these conversion formulas, they show up in Windows Phone 7 programming as well. So, when you set a FontSize property such as FontSize="36"
you can also claim to be setting a 27-point font. For a particular point size, increase by 33% to get a pixel size. This is what you set to the FontSize property of TextBlock. The resultant TextBlock will then be another 33% taller than the FontSize setting. The issue of font size becomes more complex when dealing with high-resolution screens found on devices such as Windows Phone 7. The 480 × 800 pixel display has a diagonal of 933 pixels. The phone I used for this book has a screen with about 3½” for a pixel density closer to 264 DPI. (Screen resolution is usually expressed as a multiple of 24.) Roughly that’s 2½ times the resolution of conventional video displays. This doesn’t necessarily mean that all the font sizes used on a conventional screen need to be increased by 2½ times on the phone. The higher resolution of the phone—and the closer viewing distance common with phones—allows smaller font sizes to be more readable.
Chapter 1
Hello, Windows Phone 7
21
When running in a Web browser, the default Silverlight FontSize is 11 pixels, corresponding to a font size of 8.25 points, which is fine for a desktop video display but a little too small for the phone. For that reason, Silverlight for Windows Phone defines a collection of common font sizes that you can use. (I’ll describe how these work in Chapter 7.) The standard MainPage .xaml file includes the following attribute in the root element: FontSize="{StaticResource PhoneFontSizeNormal}"
This FontSize is inherited through the visual tree and applies to all TextBlock elements that don’t set their own FontSize properties. It has a value of 20 pixels—almost double the default Silverlight FontSize on the desktop. Using the standard formulas, this 20-pixel FontSize corresponds to 15 points, but as actually displayed on the phone, it’s about 2/5 the size that a 15-point font would appear in printed text. The actual height of the TextBlock displaying text with this font is about 33% more than the FontSize, in this case about 27 pixels.
The XAP is a ZIP If you navigate to the \bin\Debug directory of the Visual Studio project for SilverlightHelloPhone, you’ll find a file named SilverlightHelloPhone.xap. This is commonly referred to as a XAP file, pronounced “zap.” This is the file that is deployed to the phone or phone emulator. The XAP file is a package of other files, in the very popular compression format known as ZIP. (Shouting “The XAP is a ZIP” in a crowded room will quickly attract other Silverlight programmers.) If you rename SilverlightHelloPhone.xap to SilverlightHelloPhone.zip, you can look inside. You’ll see several bitmap files that are part of the project, an XML file, a XAML file, and a SilverlightHelloPhone.dll file, which is the compiled binary of your program. Any assets that your program needs can be made part of the Visual Studio project and added to this XAP file. Your program can access these files at runtime. I’ll discuss some of the concepts in Chapter 4.
An XNA Program for the Phone Next up on the agenda is an XNA program that displays a little greeting in the center of the screen. While text is often prevalent in Silverlight applications, it is less common in graphical games. In games, text is usually relegated to describing how the game works or displaying the score, so the very concept of a “hello, world” program doesn’t quite fit in with the whole XNA programming paradigm.
22
Part I
The Basics
In fact, XNA doesn’t even have any built-in fonts. You might think that an XNA program running on the phone can make use of the same native fonts as Silverlight programs, but this is not so. Silverlight uses vector-based TrueType fonts and XNA doesn’t know anything about such exotic concepts. To XNA, everything is a bitmap, including fonts. If you wish to use a particular font in your XNA program, that font must be embedded into the executable as a collection of bitmaps for each character. XNA Game Studio (which is integrated into Visual Studio) makes the actual process of font embedding very easy, but it raises some thorny legal issues. You can’t legally distribute an XNA program unless you can also legally distribute the embedded font, and with most of the fonts distributed with Windows itself or Windows applications, this is not the case. To help you out of this legal quandary, Microsoft licensed some fonts from Ascender Corporation specifically for the purpose of allowing you to embed them in your XNA programs. Here they are: Kootenay
Lindsey
Miramonte
Pescadero
Miramonte Bold
Pescadero Bold
Pericles
Segoe UI Mono
Pericles Light
Segoe UI Mono Bold
Notice that the Pericles font uses small capitals for lower-case letters, so it’s probably suitable only for headings. From the File menu of Visual Studio select New and Project. On the left of the dialog box, select Visual C# and XNA Game Studio 4.0. In the middle, select Windows Phone Game (4.0). Select a location and enter a project name of XnaHelloPhone. Visual Studio creates two projects, one for the program and the other for the program’s content. XNA programs usually contain lots of content, mostly bitmaps and 3D models, but fonts as well. To add a font to this program, right-click the Content project (labeled “XnaHelloPhoneContent (Content)” and from the pop-up menu choose Add and New Item. Choose Sprite Font, leave the filename as SpriteFont1.spritefont, and click Add. The word “sprite” is common in game programming and usually refers to a small bitmap that can be moved very quickly, much like the sprites you might encounter in an enchanted forest. In XNA, even fonts are sprites. You’ll see SpriteFont1.spritefont show up in the file list of the Content directory, and you can edit an extensively commented XML file describing the font.
26
Part I
The Basics
in the content’s properties. This statement stores the loaded font in the segoe14 field of type SpriteFont. In XNA, sprites (including text strings) are usually displayed by specifying the pixel coordinates relative to the upper-left corner or the sprite relative to the upper-left corner of the display. To calculate these coordinates, it’s helpful to know both the screen size and the size of the text when displayed with a particular font. The SpriteFont class has a very handy method named MeasureString that returns a Vector2 object with the size of a particular text string in pixels. (For the 14-point Segoe UI Mono font, which has an equivalent height of 18-2/3 pixels, the MeasureString call returns a height of 28 pixels.) An XNA program generally uses the Viewport property of the GraphicsDevice class to obtain the size of the screen. This is accessible through the GraphicsDevice property of Game and provides Width and Height properties. It is then straightforward to calculate textPosition—the point relative to the upper-left corner of the viewport where the upper-left corner of the text string is to be displayed. The initialization phase of the program has now concluded, and the real action begins. The program enters the game loop. In synchronization with the 30 frame-per-second refresh rate of the video display, two methods in your program are called: Update followed by Draw. Back and forth: Update, Draw, Update, Draw, Update, Draw. . . . (It’s actually somewhat more complicated than this if the Update method requires more than 1/30th of a second to complete, but I’ll discuss these timing issues in more detail in a later chapter.) In the Draw method you want to draw on the display. But that’s all you want to do. If you need to perform some calculations in preparation for drawing, you should do those in the Update method. The Update method prepares the program for the Draw method. Very often an XNA program will be moving sprites around the display based on user input. For the phone, this user input mostly involves fingers touching the screen. All handling of user input should also occur during the Update method. You’ll see an example in Chapter 3. You should write your Update and Draw methods so that they execute as quickly as possible. That’s rather obvious, I guess, but here’s something very important that might not be so obvious: You should avoid code in Update and Draw that routinely allocates memory from the program’s local heap. Eventually the .NET garbage collector will want to reclaim some of this memory, and while the garbage collector is doing its job, your game might stutter a bit. Throughout the chapters on XNA programming, you’ll see techniques to avoid allocating memory from the heap.
Chapter 1
Hello, Windows Phone 7
Much better! But this raises a question: Do Silverlight programs always run in portrait mode and XNA programs always run in landscape mode? Is program biology destiny?
29
Chapter 2
Getting Oriented By default, Silverlight programs for Windows Phone 7 run in portrait mode, and XNA programs run in landscape mode. This chapter discusses how to transcend those defaults and explores other issues involving screen sizes, element sizes, and events.
Silverlight and Dynamic Layout If you run the SilverlightHelloPhone program from the last chapter, and you turn the phone or emulator sideways, you’ll discover that the display doesn’t change to accommodate the new orientation. That’s easy to fix. In the root PhoneApplicationPage tag, of MainPage.xaml change the attribute SupportedOrientations="Portrait"
to: SupportedOrientations="PortraitOrLandscape"
SupportedOrientations is a property of PhoneApplicationPage. It’s set to a member of the SupportedPageOrientation enumeration, either Portrait, Landscape, or PortraitOrLandscape. Recompile. Now when you turn the phone or emulator sideways, the contents of the page shift around accordingly:
The SupportedOrientations property also allows you to restrict your program to Landscape if you need to. This response to orientation really shows off dynamic layout in Silverlight. Everything has moved around and some elements have changed size. Silverlight originated in WPF and the 31
34
Part I
The Basics
Now there’s a 100-pixel breathing room between the TextBlock and the left and top edges of the client area. The Margin property is of type Thickness, a structure that has four properties named Left, Top, Right, and Bottom. If you specify only one number in XAML, that’s used for all four sides. You can also specify two numbers like this: Margin="100 200"
The first applies to the left and right; the second to the top and bottom. With four numbers Margin="100 200 50 300"
they’re in the order left, top, right, and bottom. Watch out: If the margins are too large, the text or parts of the text will disappear. Silverlight preserves the margins even at the expense of truncating the element. If you set both HorizontalAlignment and VerticalAlignment to Center, and set Margin to four different numbers, you’ll notice that the text is no longer visually centered in the content area. Silverlight bases the centering on the size of the element including the margins. TextBlock also has a Padding property:
Padding is also of type Thickness, and when used with the TextBlock, Padding is visually indistinguishable from Margin. But they are definitely different: Margin is space on the outside of the TextBlock; Padding is space inside the TextBlock not occupied by the text itself. If you were using TextBlock for touch events (as I’ll demonstrate in the next chapter), it would respond to touch in the Padding area but not the Margin area. The Margin property is defined by FrameworkElement; in real-life Silverlight programming, almost everything gets a non-zero Margin property to prevent the elements from being jammed up against each other. The Padding property is rarer; it’s defined only by TextBlock, Border, and Control. It’s possible to use Margin to position multiple elements within a single-cell Grid. It’s not common—and there are better ways to do the job—but it is possible. I’ll have an example in Chapter 5. What’s crucial to realize is what we’re not doing. We’re not explicitly setting the Width and Height of the TextBlock like in some antique programming environment:
38
Part I
The Basics
The 32-pixel difference between the MainPage size and the frame size accommodates the system tray at the top. You can prevent that tray from appearing while your application is running (and in effect, get access to the entire screen) by changing an attribute in the root element of MainPage.xaml from: shell:SystemTray.IsVisible="True"
to shell:SystemTray.IsVisible="False"
The syntax of this attribute might seem a little peculiar. SystemTray is a class in the Microsoft .Phone.Shell namespace and IsVisible is a property of that class, and both the class and property appear together because it’s a special kind of property called an attached property. The topmost Grid named LayoutRoot is the same size as MainPage. The vertical size of the TitlePanel (containing the two titles) and the vertical size of ContentPanel don’t add up to the vertical size of LayoutRoot because of the 45-pixel vertical margin (17 pixels on the top and 28 pixels on the bottom) of the TitlePanel. Subsequent SizeChanged events occur when something in the visual tree causes a size change, or when the phone changes orientation:
Notice that the frame doesn’t change orientation. In the landscape view, the system tray takes away 72 pixels of width from MainPage.
Orientation Events In many of the simpler Silverlight programs in this book, I’ll set SupportedOrientations to PortraitOrLandscape, and try to write orientation-independent applications. For Silverlight programs that get text input, it’s crucial for the program to be aligned with the hardware keyboard (if one exists) and the location of that keyboard can’t be anticipated.
40
Part I
The Basics
The OnOrientationChanged method obtains the new value from the event arguments.
XNA Orientation By default, XNA for Windows Phone is set up for a landscape orientation, perhaps to be compatible with other screens on which games are played. Both landscape orientations are supported, and the display will automatically flip around when you turn the phone from one landscape orientation to the other. If you prefer designing your game for a portrait display, it’s easy to do that. In the constructor of the Game1 class of XnaHelloPhone, try inserting the following statements: graphics.PreferredBackBufferWidth = 320; graphics.PreferredBackBufferHeight = 480;
The back buffer is the surface area on which XNA constructs the graphics you display in the Draw method. You can control both the size and the aspect ratio of this buffer. Because the buffer width I’ve specified here is smaller than the buffer height, XNA assumes that I want a portrait display:
Look at that! The back buffer I specified is not the same aspect ratio as the Windows Phone 7 display, so the drawing surface is letter-boxed! The text is larger because it’s the same pixel size but now the display resolution has been reduced.
Chapter 2
Getting Oriented
45
A Silverlight program often doesn’t seem to draw at all! Deep inside of Silverlight is a visual composition layer that operates in a retained graphics mode and organizes all the visual elements into a composite whole. Elements such as TextBlock exist as actual entities inside this composition layer. At some point, TextBlock is rendering itself—and re-rendering itself when one of its properties such as Text changes—but what it renders is retained along with the rendered output of all the other elements in the visual tree. In contrast, an XNA program is actively drawing during every frame of the video display. This is conceptually different from older Windows programming environments as well as Silverlight. It is very powerful, but I’m sure you know quite well what must also come with great power. Sometimes an XNA program’s display is static; the program might not need to update the display every frame. To conserve power, it is possible for the Update method to call the SuppressDraw method defined by the Game class to inhibit a corresponding call to Draw. The Update method will still be called 30 times per second because it needs to check for user input, but if the code in Update calls SuppressDraw, Draw won’t be called during that cycle of the game loop. If the code in Update doesn’t call SuppressDraw, Draw will be called. An XNA clock program doesn’t need a timer because a timer is effectively built into the normal game loop. However, the clock I want to code here won’t display milliseconds so the display only needs to be updated every second. For that reason it uses the SuppressDraw method to inhibit superfluous Draw calls.
48
Part I
The Basics
And here’s the result:
SuppressDraw can be a little difficult to use—I’ve found it particularly tricky during the time that the program is first starting up—but it’s one of the primary techniques used in XNA to reduce the power requirements of the program.
Chapter 3
An Introduction to Touch Even for experienced Silverlight and XNA programmers, Windows Phone 7 comes with a feature that is likely to be new and unusual. The screen on the phone is sensitive to touch. And not like old touch screens that basically mimic a mouse, or the tablet screens that recognize handwriting. The multi-touch screen on a Windows Phone 7 device can detect at least four simultaneous fingers. It is the interaction of these fingers that makes multi-touch so challenging for programmers. For this chapter, however, I have much a less ambitious goal. I want only to introduce the touch interfaces in the context of sample programs that respond to simple taps. For testing critical multi-touch code, an actual Windows Phone 7 device is essential. In the interim, the phone emulator will respond to mouse activity and convert it to touch input. If you run the emulator under Windows 7 with a multi-touch display and a Windows 7 driver, you can also use touch directly on the emulator. The programs in this chapter look much like the “Hello, Windows Phone 7!” programs in the first chapter, except that when you tap the text with your finger, it changes to a random color, and when you tap outside the area of the text, it goes back to white (or whatever color the text was when the program started up). In a Silverlight program, touch input is obtained through events. In an XNA program, touch input comes through a static class polled during the Update method. One of the primary purposes of the XNA Update method is to check the state of touch input and make changes that affect what goes out to the screen during the Draw method.
Low-Level Touch Handling in XNA The multi-touch input device is referred to in XNA as a touch panel. You use methods in the static TouchPanel class to obtain this input. Although you can obtain gestures, let’s begin with the lower-level touch information. It is possible (although not necessary) to obtain information about the multi-touch device itself by calling the static TouchPanel.GetCapabilities method. The TouchPanelCapabilities object returned from this method has two properties: ■
IsConnected is true if the touch panel is available. For the phone, this will always be true.
■
MaximumTouchCount returns the number of touch points, at least 4 for the phone.
49
50
Part I
The Basics
For most purposes, you just need to use one of the other two static methods in TouchPanel. To obtain low-level touch input, you’ll probably be calling this method during every call to Update after program initialization: TouchCollection touchLocations = TouchPanel.GetState();
The TouchCollection is a collection of zero or more TouchLocation objects. TouchLocation has three properties: ■
State is a member of the TouchLocationState enumeration: Pressed, Moved, Released.
■
Position is a Vector2 indicating the finger position relative to the upper-left corner of the viewport.
■
Id is an integer identifying a particular finger from Pressed through Released.
If no fingers are touching the screen, the TouchCollection will be empty. When a finger first touches the screen, TouchCollection contains a single TouchLocation object with State equal to Pressed. On subsequent calls to TouchPanel.GetState, the TouchLocation object will have State equal to Moved even if the finger has not physically moved. When the finger is lifted from the screen, the State property of the TouchLocation object will equal Released. On subsequent calls to TouchPanel.GetState, the TouchCollection will be empty. One exception: If the finger is tapped and released on the screen very quickly—that is, within a 1/30th of a second—it’s possible that the TouchLocation object with State equal to Pressed will be followed with State equal to Released with no Moved states in between. That’s just one finger touching the screen and lifting. In the general case, multiple fingers will be touching, moving, and lifting from the screen independently of each other. You can track particular fingers using the Id property. For any particular finger, that Id will be the same from Pressed, through all the Moved values, to Released. Very often when dealing with low-level touch input, you’ll use a Dictionary object with keys based on the Id property to retain information for a particular finger. TouchLocation also has a very handy method called TryGetPreviousLocation, which you call like this: TouchLocation previousTouchLocation; bool success = touchLocation.TryGetPreviousLocation(out previousTouchLocation);
Almost always, you will call this method when touchLocation.State is Moved because you can then obtain the previous location and calculate a difference. If touchLocation.State equals Pressed, then TryGetPreviousLocation will return false and previousTouchLocation.State will equal the enumeration member TouchLocationState.Invalid. You’ll also get these results if you use the method on a TouchLocation that itself was returned from TryGetPreviousLocation.
Chapter 3
An Introduction to Touch
55
The high-level interface consists of three events defined by the UIElement class: ManipulationStarted, ManipulationDelta, and ManipulationCompleted. The Manipulation events, as they’re collectively called, consolidate the interaction of multiple fingers into movement and scaling factors. The core of the low-level touch interface in Silverlight is a class called TouchPoint, an instance of which represents a particular finger touching the screen. TouchPoint has four get-only properties: ■
Action of type TouchAction, an enumeration with members Down, Move, and Up.
■
Position of type Point, relative to the upper-left corner of a particular element. Let’s call this element the reference element.
■
Size of type Size. This is supposed to represent the touch area (and, hence, finger pressure, more or less) but Windows Phone 7 doesn’t return useful values.
■
TouchDevice of type TouchDevice.
The TouchDevice object has two get-only properties: ■
Id of type int, used to distinguish between fingers. A particular finger is associated with a unique Id for all events from Down through Up.
■
DirectlyOver of type UIElement, the topmost element underneath the finger.
As you can see, the Silverlight TouchPoint and TouchDevice objects give you mostly the same information as the XNA TouchLocation object, but the DirectlyOver property of TouchDevice is often very useful for determining what element the user is touching. To use the low-level touch interface, you install a handler for the static Touch.FrameReported event: Touch.FrameReported += OnTouchFrameReported;
The OnTouchFrameReported method looks like this: void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { ... }
The event handler gets all touch events throughout your application. The TouchFrameEventArgs object has a TimeStamp property of type int, plus three methods: ■
GetTouchPoints(refElement) returns a TouchPointCollection
■
GetPrimaryTouchPoint(refElement) returns one TouchPoint
■
SuspendMousePromotionUntilTouchUp()
56
Part I
The Basics
In the general case, you call GetTouchPoints, passing to it a reference element. The TouchPoint objects in the returned collection have Position properties relative to that element. You can pass null to GetTouchPoints to get Position properties relative to the upper-left corner of the application. The reference element and the DirectlyOver element have no relationship to each other. The event always gets all touch activity for the entire program. Calling GetTouchPoints or GetPrimaryTouchPoints with a particular element does not limit the events to only those events involving that element. All that it does is cause the Position property to be calculated relative to that element. (For that reason, Position coordinates can easily be negative if the finger is to the left of or above the reference element.) The DirectlyOver element indicates the element under the finger. A discussion of the second and third methods requires some background: The Touch. FrameReported event originated on Silverlight for the desktop, where it is convenient for the mouse logic of existing controls to automatically use touch. For this reason, touch events are “promoted” to mouse events. But this promotion only involves the “primary” touch point, which is the activity of the first finger that touches the screen when no other fingers are touching the screen. If you don’t want the activity of this finger to be promoted to mouse events, the event handler usually begins like this: void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { TouchPoint primaryTouchPoint = args.GetPrimaryTouchPoint(null); if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down) { args.SuspendMousePromotionUntilTouchUp(); } ... }
The SuspendMousePromotionUntilTouchUp method can only be called when a finger first touches the screen when no other fingers are touching the screen. On Windows Phone 7, such logic presents something of a quandary. As written, it basically wipes out all mouse promotion throughout the application. If your phone application incorporates Silverlight controls that were originally written for mouse input but haven’t been upgraded to touch, you’re basically disabling those controls. Of course, you can also check the DirectlyOver property to suspend mouse promotion selectively. But on the phone, no elements should be processing mouse input except for those controls that don’t process touch input! So it might make more sense to never suspend mouse promotion. I’ll leave that matter for your consideration and your older mouse-handling controls. Meanwhile, the program I want to write is only interested in the primary touch point when it has a TouchAction of Down, so I can use that same logic.
Chapter 4
Bitmaps, Also Known as Textures Aside from text, one of the most common objects to appear in both Silverlight and XNA applications is the bitmap, formally defined as a two-dimensional array of bits corresponding to the pixels of a graphics display device. In Silverlight, a bitmap is sometimes referred to as an image, but that’s mostly a remnant of the Windows Presentation Foundation, where the word image refers to both bitmaps and vector-based drawings. In both WPF and Silverlight, the Image element displays bitmaps but the Image element is not the bitmap itself. In XNA, a bitmap has a data type of Texture2D and hence is often referred to as a texture, but that term is mostly related to 3D programming where bitmaps are used to cover surfaces of 3D solids. In XNA 2D programming, bitmaps are often used as sprites. Bitmaps are also used to symbolize your application on the phone. A new XNA or Silverlight project in Visual Studio results in the creation of three bitmaps for various purposes. The native Windows bitmap format has an extension of BMP but it’s become less popular in recent years as compressed formats have become widespread. At this time, the three most popular bitmap formats are probably: ■
JPEG (Joint Photography Experts Group)
■
PNG (Portable Network Graphics)
■
GIF (Graphics Interchange File)
XNA supports all three (and more). Silverlight supports only JPEG and PNG. (And if you’re like most Silverlight programmers, you’ll not always remember this simple fact and someday wonder why your Silverlight program simply refuses to display a GIF or a BMP.) The compression algorithms implemented by PNG and GIF do not result in the loss of any data. The original bitmap can be reconstituted exactly. For that reason, these are often referred to as “lossless” compression algorithms. JPEG implements a “lossy” algorithm by discarding visual information that is less perceptible by human observers. This type of compression works well for real-world images such as photographs, but is less suitable for bitmaps that derive from text or vector-based images, such as architectural drawings or cartoons.
65
66
Part I
The Basics
Both Silverlight and XNA allow manipulating bitmaps at the pixel level for generating bitmaps—or altering existing bitmaps—interactively or algorithmically. This chapter will focus more on the techniques of obtaining bitmaps from various sources, including the program itself, the Web, the phone’s built-in camera, and the phone’s photo library.
XNA Texture Drawing Because XNA 2D programming is almost entirely a process of moving sprites around the screen, you might expect that loading and drawing bitmaps in an XNA program is fairly easy, and you would be correct. The first project is called XnaLocalBitmap, so named because this bitmap will be stored as part of the program’s content. To add a new bitmap to the program’s content project, right-click the XnaLocalBitmapContent project name, select Add and then New Item, and then Bitmap File. You can create the bitmap right in Visual Studio. Or, you can create the bitmap in an external program, as I did. Windows Paint is often convenient, so for this exercise I created the following bitmap with a dimension of 320 pixels wide and 160 pixels high:
I saved it under the name Hello.png. To add this file as part of the program’s content, right-click the XnaLocalBitmapContent project in Visual Studio, select Add and Existing Item, and then navigate to the file. Once the file shows up, you can right-click it to display Properties, and you’ll see that it has an Asset Name of “Hello.” The goal is to display this bitmap centered on the screen. Define a field in the Game1.cs file to store the Texture2D and another field for the position:
68
Part I
The Basics
The final argument to Draw is a color that can be used to attenuate the existing colors in the bitmap. Use Color.White if you want the bitmap’s colors to display without any alteration. And here it is:
The Silverlight Image Element The equivalent program in Silverlight is even simpler. Let’s create a project named SilverlightLocalBitmap. First create a directory in the project to store the bitmap. This isn’t strictly required but it makes for a tidier project. Programmers usually name this directory Images or Media or Assets depending on the types of files that might be stored there. Right-click the project name and choose Add and then New Folder. Let’s name it Images. Then right-click the folder name and choose Add and Existing Item. Navigate to the Hello. png file. (If you’ve created a different bitmap on your own, keep in mind that Silverlight supports only JPEG and PNG files.) From the Add button choose either Add or Add as Link. If you choose Add, a copy will be made and the file will be physically copied into a subdirectory of the project. If you choose Add as Link, only a file reference will be retained with the project but the file will still be copied into the executable. The final step: Right-click the bitmap filename and display Properties. Note that the Build Action is Resource. It’s possible to change that Build Action to Content, but let’s leave it for now and I’ll discuss the difference shortly. In Silverlight, you use the Image element to display bitmaps just as you use the TextBlock element to display text. Set the Source property of Image to the folder and filename of the bitmap within the project:
70
Part I
The Basics
Here it is:
This is certainly easy enough, and pulling images off the Web rather than binding them into the application certainly keeps the size of the executable down. But an application running on Windows Phone 7 is not guaranteed to have an Internet connection, and you’re undoubtedly associated with other problems associated with downloading. The Image element has two events named ImageOpened and ImageFailed that you can use to determine if the download was successful or not. For Windows Phone 7 programs that display a lot of bitmaps, you need to do some hard thinking. You can embed the bitmaps into the executable and have their access guaranteed, or you can save space and download them when necessary. In XNA, downloading a bitmap from the Web is not quite as easy, but a .NET class named WebClient makes the job relatively painless. It’s somewhat easier to use than the common alternative (HttpWebRequest and HttpWebResponse) and is often the preferred choice for downloading individual items. You can use WebClient to download either strings (commonly XML files) or binary objects. The actual transfer occurs asynchronously and then WebClient calls a method in your program to indicate completion or failure. This method call is in your program’s thread, so you get the benefit of an asynchronous data transfer without explicitly dealing with secondary threads.
78
Part I
The Basics
In either case, when you press the Accept button, the camera goes away and the program’s OnCameraCaptureTaskCompleted method takes over. It creates a BitmapImage object, sets the input stream from args.ChoosenPhoto, and then sets the BitmapImage object to the Image element, displaying the photo on the screen. The whole process seems fairly straightforward. Conceptually it seems as if the program is spawning the camera process, and then resuming control when that camera process terminates. However, the Windows Phone 7 documentation that I’m consulting warns that this is not the case. There’s something else going on that is not so evident at first and which you will probably find somewhat unnerving. When the SilverlightTapToShoot program calls the Show method on the CameraCaptureTask object, the SilverlightTapToShoot program is terminated. (Not immediately, though. The OnManipulationStarted method is allowed to return back to the program, and a couple other events are fired, but then the program is definitely terminated.) The camera utility then runs. When the camera utility has done its job, the SilverlightTapToShoot program is re-executed. It’s a new instance of the program. The program starts up from the beginning, the MainPage constructor is eventually called which sets the Completed event of the CameraCaptureTask to OnCameraCaptureTaskCompleted, and then that method is called. For these reasons, the documentation advises that when you use a chooser or launcher such as CameraCaptureTask, the object must be defined as a field, and the handler for the Completed event must be attached in the program’s constructor, and as late in the constructor as possible because once the handler is attached when the program starts up again, it will be called. This termination and re-execution of your program is a characteristic of Windows Phone 7 programming call tombstoning. When the program is terminated as the camera task begins, sufficient information is retained by the phone operating system to start the program up again when the camera finishes. However, not enough information is retained to restore the program entirely to its pre-tombstone state. That’s your responsibility. Running a launcher or chooser is one way tombstoning can occur. But it also occurs when the user leaves your program by pressing the Start button on the phone. Eventually the user could return to your program by pressing the Back button, and your program needs to be re-executed from its tombstoned state. Tombstoning also takes place when a lack of activity on the phone causes it to go into a lock state. Tombstoning does not occur when your program is running and the user presses the Back button. The Back button simply terminates the program normally.
Chapter 5
Sensors and Services This chapter covers two of the facilities in Windows Phone 7 that provide information about the outside world. With the user’s permission, the location service lets your application obtain the phone’s location on the earth in the traditional geographic coordinates of longitude and latitude, whereas the accelerometer tells your program which way is down. The accelerometer and location service are related in that neither of them will work very well in outer space. Although the accelerometer and the location service are ostensibly rather easy, this chapter also explores issues involved with working with secondary threads of execution, handling asynchronous operations, and accessing web services.
Accelerometer Windows Phones contain an accelerometer—a small hardware device that essentially measures force, which elementary physics tells us is proportional to acceleration. When the phone is held still, the accelerometer responds to the force of gravity, so the accelerometer can tell your application the direction of the Earth relative to the phone. A simulation of a bubble level is an archetypal application that makes use of an accelerometer, but the accelerometer can also provide a basis for interactive animations. For example, you might pilot a messenger bike through the streets of Manhattan by tilting the phone left or right to indicate steering. The accelerometer also responds to sudden movements such as shakes or jerks, useful for simulations of dice or some other type of randomizing activity. Coming up with creative uses of the accelerometer is one of the many challenges of phone development. It is convenient to represent the accelerometer output as a vector in three-dimensional space. Vectors are commonly written in boldface, so the acceleration vector can be symbolized as (x, y, z). XNA defines a three-dimensional vector type; Silverlight does not. While a three-dimensional point (x, y, z) indicates a particular location in space, the vector (x, y, z) encapsulates instead a direction and a magnitude. Obviously the point and the vector are related: The direction of the vector (x, y, z) is the direction from the point (0, 0, 0) to the point (x, y, z). But the vector (x, y, z) is definitely not the line from (0, 0, 0) to (x, y, z). It’s only the direction of that line.
83
84
Part I
The Basics
The magnitude of the vector (x, y, z) is calculable from the three-dimensional form of the Pythagorean Theorem:
For working with the accelerometer, you can imagine the phone as defining a three-dimensional coordinate system. No matter how the phone is oriented, the positive Y axis points from the bottom of the phone (with the buttons) to the top, the positive X axis points from left to right,
This is a traditional three-dimensional coordinate system, the same coordinate system used in XNA 3D programming. It’s termed a right-hand coordinate system: Point the index finger of your right hand to increasing X, the middle finger to increase Y, and your thumb points to increasing Z. Or, curve the fingers of your right hand from the positive X axis to the positive Y axis. Your thumb again points to increasing Z. This coordinate system remains fixed relative to the phone regardless how you hold the phone, and regardless of the orientation of any programs running on the phone. In fact, as you might expect, the accelerometer is the basis for performing orientation changes of Windows Phone 7 applications. When the phone is still, the accelerometer vector points towards the Earth. The magnitude is 1, meaning 1 g, which is the force of gravity on the earth’s surface. When holding your phone in the upright position, the acceleration vector is (0, –1, 0), that is, straight down.
Chapter 5
Sensors and Services
89
else { txtblk.Dispatcher.BeginInvoke(() => { txtblk.Text = str; }); }
The duplicated code that sets the Text property of TextBlock to str looks a little ugly here (and would be undesirable if it involved more than just one statement), but you don’t really need to call CheckAccess. You can just call BeginInvoke and nothing bad will happen even if you are calling it from the UI thead. The Windows Phone 7 emulator doesn’t contain any actual accelerometer, so it always reports a value of (0, 0, –1), which indicates the phone is lying on a flat surface. The program only makes sense when running on an actual phone:
The values here indicate the phone is roughly upright but tilted back a bit, which is a very natural orientation in actual use.
A Simple Bubble Level One handy tool found in any workshop is a bubble level, also called a spirit level. A little bubble always floats to the top of a liquid, so it visually indicates whether something is parallel or orthogonal to the earth, or tilted in some way.
94
Part I
The Basics
The program doesn’t look like much, and is even more boring running on the emulator. Here’s an indication that the phone is roughly upright and tilted back a bit:
You’ll discover that the accelerometer is very jittery and cries out for some data smoothing.
Geographic Location With the user’s permission, a Windows Phone 7 program can obtain the geographic location of the phone using a technique called Assisted-GPS or A-GPS. The most accurate method of determining location is accessing signals from Global Positioning System (GPS) satellites. However, GPS can be slow. It doesn’t work well in cities or indoors, and it’s considered expensive in terms of battery use. To work more cheaply and quickly, an A-GPS system can attempt to determine location from cell-phone towers or the network. These methods are faster and more reliable, but less accurate. The core class involved in location detection is GeoCoordinateWatcher. You’ll need a reference to the System.Device assembly and a using direction for the System.Device. Location namespace. The WMAppManifest.xml file requires the tag:
This is included by default.
Chapter 5
Sensors and Services
95
The GeoCoordinateWatcher constructor optionally takes a member of the GeoPositionAccuracy enumeration: ■
Default
■
High
After creating a GeoCoordinateWatcher object, you’ll want to install a handler for the PositionChanged event and call Start. The PositionChanged event delivers a GeoCoordinate object that has eight properties: ■
Latitude, a double between –90 and 90 degrees
■
Longitude, a double between –180 and 180 degrees
■
Altitude of type double
■
HorizontalAccuracy and VerticalAccuracy of type double
■
Course, a double between 0 and 360 degrees
■
Speed of type double
■
IsUnknown, a Boolean that is true if the Latitude or Longitude is not a number
If the application does not have permission to get the location, then Latitude and Longitude will be Double.NaN, and IsUnknown will be true. In addition, GeoCoordinate has a GetDistanceTo method that calculates the distance between two GeoCoordinate objects. I’m going to focus on the first two properties, which together are referred to as geographic coordinates to indicate a point on the surface of the Earth. Latitude is the angular distance from the equator. In common usage, latitude is an angle between 0 and 90 degrees and followed with either N or S meaning north or south. For example, the latitude of New York City is approximately 40°N. In the GeoCoordinate object, latitudes north of the equator are positive values and south of the equator are negative values, so that 90° is the North Pole and –90° is the South Pole. All locations with the same latitude define a line of latitude. Along a particular line of latitude, longitude is the angular distance from the Prime Meridian, which passes through the Royal Observatory at Greenwich England. In common use, longitudes are either east or west. New York City is 74°W because it’s west of the Prime Meridian. In a GeoCoordinate object, positive longitude values denote east and negative values are west. Longitude values of 180 and –180 meet up at the International Date Line. Although the System.Device.Location namespace includes classes that use the geographic coordinates to determine civic address (streets and cities), these are not implemented in the initial release of Windows Phone 7.
98
Part I
The Basics
Because the GeoCoordinateWatcher is left running for the duration of the program, it should update the location as the phone is moved. Here’s where I live:
With the phone emulator, however, the GeoCoordinateWatcher program might not work. With some beta software releases of Windows Phone 7 development tools, the Accelerometer always returned the coordinates of a spot in Princeton, New Jersey, perhaps as a subtle reference to the college where Alan Turing earned his PhD.
Using a Map Service Of course, most people curious about their location prefer to see a map rather than numeric coordinates. The Silverlight demonstration of the location service displays a map that comes to the program in the form of bitmaps. In a real phone application, you’d probably be using Bing Maps, particularly considering the existence of a Bing Maps Silverlight Control tailored for the phone. Unfortunately, making use of Bing Maps in a program involves opening a developer account, and getting a maps key and a credential token. This is all free and straightforward but it doesn’t work well for a program that will be shared among all the readers of a book. For that reason, I’ll be using an alternative that doesn’t require keys or tokens. This alternative is Microsoft Research Maps, which you can learn all about at msrmaps.com. The aerial images are provided by the United States Geological Survey (USGS). Microsoft Research Maps makes these images available through a web service called MSR Maps Service, but still sometimes referred to by its old name of TerraService. The downside is that the images are not quite state-of-the-art and the service doesn’t always seem entirely reliable. MSR Maps Service is a SOAP (Simple Object Access Protocol) service with the transactions described in a WSDL (Web Services Description Language) file. Behind the scenes, all the
Chapter 5
Sensors and Services
99
transactions between your program and the web service are in the form of XML files. However, to avoid programmer anguish, generally the WSDL file is used to generate a proxy, which is a collection of classes and structures that allow your program to communicate with the web service with method calls and events. You can generate this proxy right in Visual Studio. Here’s how I did it: I first created an Windows Phone 7 project in Visual Studio called SilverlightLocationMapper. In the Solution Explorer, I right-clicked the project name and selected Add Service Reference. In the Address field I entered the URL of the MSR Maps Service WSDL file: http://MSRMaps.com/ TerraService2.asmx. (You might wonder if the URL should be http://msrmaps.com/TerraService2.asmx?WSDL because that’s how WSDL files are often referenced. That address will actually seem to work at first, but you’ll get files containing obsolete URLs.) After you’ve entered the URL in the Address field, press Go. Visual Studio will access the site and report back what it finds. There will be one service, called by the old name of TerraService. Next you’ll want to enter a name in the Namespace field to replace the generic ServiceReference1. I used MsrMapsService and pressed OK. You’ll then see MsrMapsService show up under the project in the Solution Explorer. If you click the little Show All Files icon at the top of the Solution Explorer, you can view the generated files. In particular, nested under MsrMapsService and Reference.svcmap, you’ll see Reference.cs, a big file (over 4000 lines) with a namespace of XnaLocationMapper. MsrMapsService, which combines the original project name and the name you selected for the web service. This Reference.cs file contains all the classes and structures you need to access the web service, and which are documented on the msrmaps.com web site. To access these classes in your program, add a using direction: using SilverlightLocationMapper.MsrMapsService;
You also need a reference to the System.Device assembly and using directives for the System.Device.Location, System.IO, and System.Windows.Media.Imaging namespacess. In the MainPage.xaml file, I left the SupportedOrientations property at its default setting of Portrait, I removed the page title to free up more space, and I moved the title panel below the content grid just in case there was a danger of something spilling out of the content grid and obscuring the title. Moving the title panel below the content grid in the XAML file ensures that it will be visually on top.
Chapter 5
Sensors and Services
105
If you change the second argument of the proxy.GetAreaFromPtAsync call from a 1 to a 2, you get back images of an actual map rather than an aerial view:
It has a certain retro charm—and I love the watercolor look—but I’m afraid that modern users are accustomed to something just a little more 21st century.
Chapter 6
Issues in Application Architecture
111
Let’s run the program. The program begins with the main page, and you can touch the screen to change the color:
Now touch the TextBlock that says “Navigate to 2nd Page” and the second page comes into view:
112
Part I
The Basics
You can touch that screen to change to a different color:
Now touch the TextBlock that says “Go Back to 1st Page”. (Alternatively, you can press the phone’s hardware Back button.) You’ll be whisked back to the main page with the color just as you left it:
Chapter 6
Issues in Application Architecture
113
Now touch the TextBlock again to navigate to the second page:
The background is black. The second page does not display the color you set when you last visited the second page. This is very obviously a brand new instance of the SecondPage class. The navigation system in Silverlight for Windows Phone is based around the metaphor of the last-in-first-out data structure called the stack. I’ll sometimes refer to the page calling Navigate as the source page and the page being navigated to as the destination page. When the source page calls Navigate, the source page is put on the stack and a new instance of the destination page is created and displayed. When a page calls GoBack—or when the user presses the phone’s hardware Back button—that page is abandoned, and the page at the top of the stack is popped off and displayed. Within a Silverlight application, the phone’s Back button performs the same function as a call to GoBack except if you’re at the initial page of the program, in which case the hardware Back button terminates the application. Try this: Replace the GoBack call in SecondPage.xaml.cs with the following: this.NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
This is not the same as the GoBack call. You won’t go back to the original instance of MainPage. This call causes SecondPage to navigate to a new instance of MainPage, and if you keep pressing the TextBlock on each on the pages, you’ll build up a whole stack of alternating MainPage and SecondPage instances, each of which can have its own unique
124
Part I
The Basics
The Multitasking Ideal Over the past few decades, it’s been a common desire that our personal computers be able to do more than one thing at a time. But when user interfaces are involved, multitasking is never quite as seamless as we’d like. The Terminate-and-Stay-Resident (TSR) programs of MS-DOS and the cooperative multitasking of early Windows were only the first meager attempts in an ongoing struggle. In theory, process switching is easy. But sharing resources—including the screen and a handful of various input devices—is very hard. While the average user might marvel at the ability of modern Windows to juggle many different applications at once, we programmers still wrestle with the difficulties of multitasking— carefully coding our UI threads to converse amicably with our non-UI threads, always on the lookout for the hidden treachery of asynchronous operations. Every new application programming interface we encounter makes a sort of awkward accommodation with the ideals of multitasking, and as we become familiar with the API we also become accustomed to this awkward accommodation, and eventually we might even consider this awkward accommodation to be a proper solution to the problem. On Windows Phone 7, that awkward accommodation is known as tombstoning.
Task Switching on the Phone We want our phones to be much like our other computers. We want to have a lot of applications available. We want to start up a particular application as soon as we conceive a need for it. While that application is running, we want it to be as fast as possible and have access to unlimited resources. But we want this application to coexist with other running applications because we want to be able to jump among multiple applications running on the machine. Arbitrarily jumping among multiple running applications is somewhat impractical on the phone. It would require some kind of display showing all the currently running applications, much like the Windows taskbar. Either this taskbar would have to be constantly visible—taking valuable screen space away from the active applications—or a special button or command would need to be assigned to display the taskbar or task list. Instead, Windows Phone 7 manages multiple active applications by implementing a stack. In a sense, this application stack extends the page stack within a single Silverlight program. You can think of the phone as an old-fashioned web browser with no tab feature and no Forward button. But it does have a Back button and it also has a Start button, which brings you to the Start screen and allows you to launch a new program. Suppose you choose to launch a program called Analyze. You work a little with Analyze and then decide you’re finished. You press the Back button. The Analyze program is terminated and you’re back at the Start screen. That’s the simple scenario.
Chapter 6
Issues in Application Architecture
125
Later you decide you need to run Analyze again. While you’re using Analyze, you need to check something on the Web. You press the Start button to get to the Start screen and select Internet Explorer. While you’re browsing, you remember you haven’t played any games recently. You press the Start button, select Backgammon and play a little of that. While playing Backgammon, you wonder about the odds of a particular move, so you press the Start button again and run Calc. Then you feel guilty about not doing any work, so you press the Start button again and run Draft. Draft is a Silverlight program with multiple pages. From the main page, you navigate to several other pages. Now start pressing the Back button. You go backwards through all the pages in the page stack of the Draft, then Draft is terminated as you go back to Calc. Calc still displays the remnants of your work, and Calc is terminated as you go back to Backgammon, which shows a game in progress, and Backgammon is terminated as you go back to Internet Explorer, and again you go backwards through any Web pages you may have navigated through, and IE is terminated as you go back to Analyze, and Analyze is terminated as you go back to the Start screen. The stack is now empty. This type of navigation is a good compromise for small devices, and it’s consistent with users’ experiences in web browsing. The stack is conceptually very simple: The Start button pushes the current application on the stack so a new application can be run; the Back button terminates the current application and pops one off the top of the stack. However, the limited resources of the phone convinced the Windows Phone 7 developers that applications on the stack should have as minimum a footprint as possible. For this reason, an application put on the stack does not continue plugging away at work. It’s not even put into a suspended state of some sort. Something more severe than that happens. The process is actually terminated. When this terminated program comes off the stack, it is then re-executed from scratch. This is tombstoning. The application is killed but then allowed to come back to life. You’ve probably seen enough movies to know that re-animating a corpse can be a very scary proposition. Almost always the hideous thing that arises out of the filthy grave is not the clean and manicured loved one who went in. The trick here is to persuade the disinterred program to look and feel much the same as when it was last alive and the user interacted with it. This process is a collaboration between you and Windows Phone 7. The phone gives you the tools (events and a place to put some data); your job is to use the tools to restore your program to a presentable state. Ideally the user should have no idea that it’s a completely new process. For some applications, resurrection doesn’t have to be 100% successful. We all have experience with navigating among Web pages to know what’s acceptable and what’s not.
130
Part I
The Basics
As you experiment, you’ll discover that the settings are saved when the application is tombstoned (that is, when you navigate away from the application with the Start button and then return) but not when a new instance starts up from the Start list. This is correct behavior. The operating system discards the State dictionary when the program terminates for real. The State dictionary is only for transient data and not for data that affects other instances of the same application. If you want some similar data shared among all instances of a program, you probably want to implement what’s often called application settings. You can do that as well.
Isolated Storage Every program installed on Windows Phone 7 has access to its own area of permanent disk storage referred to as isolated storage, which the program can access using classes in the System.IO.IsolatedStorage namespace. Whole files can be read and written to in isolated storage, and I’ll show you how to do that in the program that concludes this chapter. For the program that following I’m going to focus instead on a special use of isolated storage for storing application settings. The IsolatedStorageSettings class exists specifically for this purpose. For application settings, you should be thinking in terms of the whole application rather than a particular page. Perhaps some of the application settings apply to multiple pages. Hence, a good place to deal with these application settings is in the program’s App class. Not coincidently, it is the App.xaml file that creates a PhoneApplicationService object (the same PhoneApplicationService object used for saving transient data) and assigns event handlers for four events:
The Launching event is fired when the program is first executed from the Start screen. The Deactivated event occurs when the program is tombstoned, and the Activated event occurs when the program is resurrected from tombstoning. The Closing event occurs when the program is really terminated, probably by the user pressing the Back button. So, when a program starts up, it gets either a Launching event or an Activated event (but never both), depending whether it’s being started from the Start screen or coming out of a tombstoned state. When a program ends, it gets either a Deactivated event or a Closing event, depending whether it’s being tombstoned or terminated for real. A program should load application settings during the Launching event and save them in response to the Closing event. That much is obvious. But a program should also save application settings during the Deactivated event because the program really doesn’t know
140
Part I
The Basics
It’s possible that you’ll need to save an unserializable object as a transient setting, but because it’s not serializable, you can’t use the State dictionary of the PhoneApplicationService class. You’ll need to use isolated storage for such an object, but you don’t want to accidently retrieve that object and reuse it when the program is run again. In this case, you’ll use a flag in the State dictionary indicating whether you need to load the transient object from isolated storage.
Testing and Experimentation Programmers at Microsoft who have been writing Windows Phone 7 applications longer than many of us report that dealing with tombstoning can be one of the trickier aspects of phone development. The techniques I’ve shown you in this chapter illustrate a good starting point but all applications will have slightly different requirements. Surely you’ll want to do a lot of testing in your own programs, and it always helps to know exactly what methods of a program are being called and in what order. For this job, the Debug.WriteLine method of the System.Diagnostics namespace can be very helpful.
Part II
Silverlight
141
Chapter 7
XAML Power and Limitations As you’ve seen, a Silverlight program is generally a mix of code and XAML. Most often, you’ll use XAML for defining the layout of the visuals of your application, and you’ll use code for event handling, including all user-input events and all events generated by controls as a result of processing user-input events. Much of the object creation and initialization performed in XAML would traditionally be done in the constructor of a page or window class. This might make XAML seem just a tiny part of the application, but it turns out to be much more than that. As the name suggests, XAML is totally compliant XML, so it’s instantly toolable—machine writable and machine readable as well as human writable and human readable. Although XAML is usually concerned with object creation and initialization, certain features of Silverlight provide much more than object initialization would seem to imply. One of these features is data binding, which involves connections between controls, or between controls and underlying data, so that properties are automatically updated without the need for explicit event handlers. Entire animations can also be defined in XAML. Although XAML is sometimes referred to as a “declarative language,” it is certainly not a complete programming language. You can’t perform arithmetic in any generalized manner in XAML, and you can’t dynamically create objects in XAML. Experienced programmers encountering XAML for the first time are sometimes resistant to it. I know I was. Everything that we value in a programming language such as C#—required declarations, strong typing, array-bounds checking, tracing abilities for debugging—largely goes away when everything is reduced to XML text strings. Over the years, however, I’ve gotten very comfortable with XAML, and I find it very liberating in using XAML for the visuals of the application. In particular I like how the parent-child relationship of controls on the surface of a window is mimicked by the parent-child structure inherent in XML. I also like the ability to experiment with XAML—even just in the Visual Studio designer. Everything you need to do in Silverlight can be allocated among these three categories: ■
Stuff you can do in either code or XAML
■
Stuff you can do only in code (e.g., event handling and methods)
■
Stuff you can do only in XAML (e.g., templates)
In both code and XAML you can instantiate classes and structures, and set the properties of these objects. A class or structure instantiated in XAML must be defined as public (of course), but it must also have a parameterless constructor. When XAML instantiates the class, 143
144
Part II Silverlight
it has no way of passing anything to the constructor. In XAML you can associate a particular event with an event handler, but the event handler itself must be implemented in code. You can’t make method calls in XAML because, again, there’s no way to pass arguments to the method. If you want, you can write almost all of your Silverlight application entirely in code. However, page navigation is based around the existence of XAML files for classes that derive from PhoneApplicationPage, and there also is a very important type of job that must be done in XAML. This is the construction of templates. You use templates in two ways: First, to visually display data using a collection of elements and controls, and secondly, to redefine the visual appearance of a control while maintaining its functionality. You can write alternatives to templates in code, but you can’t write the templates themselves. After some experience with Silverlight programming, you might decide that you want to use a design program such as Expression Blend to generate XAML for you. But I urge you—speaking programmer to programmer—to learn how to write XAML by hand. At the very least you need to know how to read the XAML that design programs generate for you. One of the very nice features of XAML is that you can experiment with it in a very interactive manner, and by experimenting with XAML you can learn a lot about Silverlight. Programming tools designed specifically for experimenting with XAML are available. These programs take advantage of a static method named XamlReader.Load that can convert XAML text into an object at runtime. In Chapter 13 you’ll see an application that lets you experiment with XAML right on the phone! Until then, however, you can experiment with XAML in the Visual Studio designer. Generally the designer responds promptly and accurately to changes you make in the XAML. Only when things get a bit complex will you actually need to build and deploy the application to see what it’s doing.
A TextBlock in Code Before we get immersed in experimenting with XAML, however, I must issue another warning: As you get accustomed to using XAML exclusively for certain common chores, it’s important not to forget how to write C#! You’ll recall the XAML version of the TextBlock in the Grid from Chapter 2:
Elements in XAML such as TextBlock are actually classes. Attributes of these elements (such as Text, HorizontalAlignment, and VerticalAlignment) are properties of the class. Let’s see how
Chapter 7
XAML Power and Limitations
147
The text shows up in the upper-left corner of the page’s client area. Let’s make the text italic. You can do that by setting the FontStyle property in the TextBlock:
Alternatively, you can put that FontStyle attribute in the PhoneApplicationPage tag:
Chapter 7
XAML Power and Limitations
159
The precise problem with this syntax is revealed when you put in the missing property elements for the Children property of the Grid:
Now it’s obvious that the Children property is being set twice—and that’s clearly illegal.
The Resources Collection In one sense, computer programming is all about the avoidance of repetition. (Or at least repetition by us humans. We don’t mind if our machines engage in repetition. We just want it to be efficient repetition.) XAML would seem to be a particularly treacherous area for repetition because it’s just markup and not a real programming language, You can easily imagine situations where a bunch of elements have the same HorizontalAlignment or VerticalAlignment or Margin settings, and it would certainly be convenient if there were a way to avoid a lot of repetitive markup. If you ever needed to change one of these properties, changing it just once is much better than changing it scores or hundreds of times. Fortunately XAML has been designed by programmers who (like the rest of us) prefer not to type in the same stuff over and over again. The most generalized solution to repetitive markup is the Silverlight style. But a prerequisite to styles is a more generalized sharing mechanism. This is called the resource, and right away we need to distinguish between the resources I’ll be showing you here, and the resources encountered in Chapter 4 when embedding images into the application. Whenever there’s a chance of confusion, I will refer to the resources in this chapter as XAML resources, even though they can exist in code as well.
160
Part II Silverlight
XAML resources are always instances of a particular .NET class or structure, either an existing class or structure or a custom class. When a particular class is defined as a XAML resource, only one instance is made, and that one instance is shared among everybody using that resource. The sharing of resources immediately disqualifies many classes from being defined as XAML resources. For example, a single instance of TextBlock can’t be used more than once because the TextBlock must have a unique parent and a unique location within that parent. And that goes for any other element as well. Anything derived from UIElement is probably not going to show up as a resource because it can’t be shared. However, it is very common to share brushes. This is a typical way to give a particular application a certain consistent and distinctive visual appearance. Animations are also candidates for sharing. It’s also possible to share text strings and numbers. Think of these as the XAML equivalents of string or numeric constants in a C# program. When you need to change one of them, you can just change the single resource rather than hunting through the XAML to change a bunch of individual occurrences. To support the storage of resources, FrameworkElement defines a property named Resources of type ResourceDictionary. On any element that derives from FrameworkElement, you can define Resources as a property element. By converntion this appears right under the start tag. Here’s a Resources collection for a page class that derives from PhoneApplicationPage: ... ...
The collection of resources within those Resources tags is sometimes called a resource section, and anything in that particular PhoneApplicationPage can then use those resources. The Application class also defines a Resources property, and the App.xaml file that Visual Studio creates for you in a new Silverlight application already includes an empty resource section: ...
The resources defined in the Resources collection on a FrameworkElement are available only within that element and nested elements; the resources defined in the Application class are available throughout the application.
Chapter 7
XAML Power and Limitations
161
Sharing Brushes Let’s suppose your page contains several TextBlock elements, and you want to apply the same LinearGradientBrush to the Foreground of each of them. This is an ideal use of a resource. The first step is to define a LinearGradientBrush in a resource section of a XAML file. If you’re defining the resource in a FrameworkElement-derivative, the resource must be defined before it is used, and it can only be accessed by the same element or a nested element.
Notice the x:Key attribute. Every resource must have a key name. There are only four keywords that must be prefaced with “x” and you’ve already seen three of them: Besides x:Key they are x:Class, x:Name and x:Null. Accessing that resource is possible with a couple kinds of syntax. The rather verbose way is to break out the Foreground property of the TextBlock as a property element and set it to an object of type StaticResource referencing the key name:
There is, however, a shortcut syntax that makes use of what is called a XAML markup extension. A markup extension is always delimited by curly braces. Here’s what the StaticResource markup extension looks like:
Notice that within the markup extension the word “brush” is not in quotation marks. Quotation marks within a markup extension are always prohibited. Suppose you want to share a Margin setting. The Margin is of type Thickness, and in XAML you can specify it with 1, 2, or 4 numbers. Here’s a Thickness resource: 12 96
Suppose you want to share a FontSize property. That’s of type double, and you’re going to need a little help. The Double structure, which is the basis for the double C# data type, is defined in the System namespace, but the XML namespace declarations in a typical XAML file
164
Part II Silverlight
x:Key and x:Name If you need to reference a XAML resource from code, you can simply index the Resources property with the resource name. For example, in the MainPage.xaml.cs code-behind file, this code will retrieve the resource named “brush” stored in the Resources collection of MainPage: this.Resources["brush"]
You would then probably cast that object to an appropriate type, in this case either Brush or LinearGradientBrush. Because the Resources collection isn’t built until the XAML is processed, you can’t access the resource before the InitializeComponent call in the constructor of the code-behind file. If you have resources defined in other Resource collections in the same XAML file, you can retrieve those as well. For example, if you’ve defined a resource named “margin” in the Resources collection of the content grid, you can access that resource using: ContentPanel.Resources["margin"]
If no resource with that name is found in the Resources collection of an element, then the Resources collection of the App class is searched. If the resource is not found there, then the indexer returns null. Due to a legacy issue involving Silverlight 1.0, you can use x:Name rather than using x:Key to identify a resource: ...
There is one big advantage to this: The name is stored as a field in the generated code file so you can reference the resource in the code-behind file just like any other field: txtblk.Foreground = brush;
This is a much better syntax for sharing resources between XAML and code. However, if you use x:Name for a resource, that name must be unique in the XAML file.
An Introduction to Styles One very common item in a Resources collection is a Style, which is basically a collection of property assignments for a particular element type. Besides a key, the Style also requires a TargetType:
Chapter 8
Elements and Properties You’ve already seen several examples of TextBlock and Image, which are surely two of the most important elements supported by Silverlight. This chapter explores text and bitmaps in more depth, and also describes other common elements and some important properties you can apply to all these elements, including transforms. This lays the groundwork for the subject of Panel elements that provide the basis of Silverlight’s dynamic layout system (in the next chapter) and then the huge subject of controls (Chapter 10).
Basic Shapes The System.Windows.Shapes namespace includes elements for displaying vector graphics—the use of straight lines and curves for drawing and defining filled areas. Although the subject of vector graphics awaits us in Chapter 13, two of the classes in this namespace—Ellipse and Rectangle—are a little different from the others in that you can use them without specifying any coordinate points. Go back to the XamlExperiment program from the Chapter 7 and insert this Ellipse element into the content grid:
You’ll see a blue ellipse with a red outline fill the Grid:
171
172
Part II Silverlight
Now try setting HorizontalAlignment and VerticalAlignment to Center. The Ellipse disappears. What happened? This Ellipse has no intrinsic minimum size. When allowed to, it will assume the size of its container, but if it’s forced to become small, it will become as small as possible, which is nothing at all. This is one case where explicitly setting Width and Height properties of an element is appropriate and often necessary. The terms stroke and fill are common in vector graphics. The basis of vector graphics is the use of coordinate points to define straight lines and curves. These are mathematical entities that only become visible by being stroked with a particular color and line thickness. The lines and curves might also defined enclosed areas, in which case this area can be filled. Both the Fill property and the Stroke property of Ellipse are of type Brush, so you can set either or both to gradient brushes. It is very common to set the Width property of an Ellipse to the Height to create a circle. The Fill can then be set to a RadialGradientBrush that starts at White in the center and then goes to a gradient color at the perimeter. Normally the gradient center is the point (0.5, 0.5) relative to the ball’s dimension, but you can offset that like so:
The offset white spot looks like reflection from a light source, suggesting a three dimensional shape:
Chapter 8
Elements and Properties
173
The Rectangle has the same properties as Ellipse except that Rectangle also defines RadiusX and RadiusY properties for rounding the corners.
Transforms Until the advent of the Windows Presentation Foundation and Silverlight, transforms were mostly the tools of the graphics mavens. Mathematically speaking, transforms apply a simple formula to all the coordinates of a visual object and cause that object to be shifted to a different location, or change size, or be rotated. In Silverlight, you can apply transforms to any object that descends from UIElement, and that includes text, bitmaps, movies, panels, and all controls. The property defined by UIElement that makes transforms possible is RenderTransform, which you set to an object of type Transform. Transform is an abstract class, but it is the parent class to seven non-abstract classes: ■
TranslateTransform to shift location
■
ScaleTransform to increase or decrease size
■
RotateTransform to rotate around a point
■
SkewTransform to shift in one dimension based on another dimension
■
MatrixTransform to express transforms with a standard matrix
■
TransformGroup to combine multiple transforms
■
CompositeTransform to specify a series of transforms in a fixed order
The whole subject of transforms can be quite complex, particularly when transforms are combined, so I’m really only going to show the basics here. Very often, transforms are used in combination with animations. Animating a transform is the most efficient way that an animation can be applied to a visual object. Suppose you have a TextBlock and you want to make it twice as big. That’s easy: Just double the FontSize. Now suppose you want to make the text twice as wide but three times taller. The FontSize won’t help you there. You need to break out the RenderTransform property as a property element and set a ScaleTransform to it:
Most commonly, you’ll set the RenderTransform property of an object of type TranslateTransform, ScaleTransform, or RotateTransform. If you know what you’re doing, you can combine multiple transforms in a TransformGroup. In two dimensions, transforms are expressed as 3×3 matrices, and combining transforms is equivalent to matrix multiplication. It is well known that matrix multiplication is not commutative, so the order that transforms are multiplied makes a difference in the overall effect.
176
Part II Silverlight
ActualWidth and ActualHeight properties of the TextBlock), divide the values by 2 and set CenterX and CenterY to the results. For the text string in TransformExperiment, try 96 and 13, respectively. Now the scaling is relative to the center. But there’s a much easier way: TextBlock itself has a RenderTansformOrigin property that it inherits from UIElement. This property is a point in relative coordinates where (0, 0) is the upper-left corner, (1, 1) is the lower-right corner, and (0.5, 0.5) is the center. Set CenterX and CenterY in the ScaleTransform back to 0, and set RenderTransformOrigin in the TextBlock like so: RenderTransformOrigin="0.5 0.5"
Leave RenderTransformOrigin at this value when you set the ScaleX and ScaleY properties of ScaleTransform back to the default values of 1, and play around with RotateTransform. As with scaling, rotation is always relative to a point. You can use CenterX and CenterY to set that point in absolute coordinates relative to the object being rotated, or you can use RenderTransformOrigin to use relative coordinates. The Angle property is in degrees, and positive angles rotate clockwise. Here’s rotation of 45 degrees around the center.
The SkewTransform is hard to describe but easy to demonstrate. Here’s the result when AngleX is set to 30 degrees:
The X coordinates are shifted to the right based on values of Y so as Y becomes larger (at the bottom of the text) values of X also increase. Use a negative angle to simulate oblique (italic-like) text. Setting AngleY causes vertical shifting based on increasing X coordinates. Here’s AngleY set to 30 degrees:
180
Part II Silverlight
The timer callback obtains the current time and calculates the angles for the second, minute, and hour hands relative to their high-noon positions. Each hand gets a call to SetupHand to do all the remaining work. The CompositeTransform must perform several chores. The translation part must move the TextBlock elements so the beginning of the text is positioned in the center of the Grid. But I don’t want the upper-left corner of the text to be positioned in the center. I want a point that is offset by that corner by half the height of the text. That explains the TranslateX and TranslateY properties. Recall that in the CompositeTransform the translation is applied last; that’s why I put these properties at the bottom of the method, even though the order that these properties are set is irrelevant. Both ScaleX and ScaleY are set to the scaling factor calculated earlier. The angle parameter passed to the method is relative to the high-noon position, but the TextBlock elements are positioned at 3:00. That’s why the Rotation angle offsets the angle parameter by –90 degrees. Both scaling and rotation are relative to CenterX and CenterY, which is a point at the left end of the text, but offset from the upper-left corner by half the text height. Here’s the clock in action:
Windows Phone also supports the Projection transform introduced in Silverlight 3, but it’s almost exclusively used in connection with animations, so I’ll hold off on Projection until Chapter 15.
Animating at the Speed of Video The use of the DispatcherTimer with a one-second interval makes sense for the HybridClock program because the positions of the clock hands need to be updated only once per second. But switching to a sweep second hand immediately raises the question: How often should the clock hands be updated? Considering that the second hand only needs to move a few pixels per second, setting the timer for 250 milliseconds would probably be fine, and 100 milliseconds would be more than sufficient.
184
Part II Silverlight
Using DeltaManipulation is often easier than CumulativeManipulation. If only one finger is manipulating the element, then only the Translation factors are valid, and these can just be added to the TranslateX and TranslateY properties of the CompositeTransform. If two fingers are touching the screen, then the Scale values are non-zero, although they could be negative and they’re often unequal. To keep the circle a circle, I use the maximum and multiply by the existing scaling factors of the transform. This enables “pinch” and “stretch” manipulations. The XAML file sets the transform center to the center of the ellipse; in theory it should be based on the position and movement of the two fingers, but this is a rather more difficult thing to determine.
The Border Element The TextBlock doesn’t include any kind of border that you can draw around the text. Fortunately Silverlight has a Border element that you can use to enclose a TextBlock or any other type of element. The Border has a property named Child of type UIElement, which means you can only put one element in a Border; however, the element you put in the Border can be a panel, and you can then add multiple elements to that panel. If you load the XamlExperiment program from the last chapter into Visual Studio, you can put a TextBlock in a Border like so:
The Child property is the ContentProperty attribute of Border so the Border.Child tags are not required. Without setting any HorizontalAlignment and VerticalAlignment properties, the Border element occupies the entire area of the Grid, and the TextBlock occupies the entire area of the Border, even though the text itself sits at the upper-left corner. You can center the TextBlock within the Border:
Chapter 8
Elements and Properties
185
Or, you can center the Border within the Grid:
At this point, the Border contracts in size to become only large enough to fit the TextBlock. You can also set the HorizontalAlignment and VerticalAlignment properties of the TextBlock but they would now have no effect. You can give the TextBlock a little breathing room inside the border by either setting the Margin or Padding property of the TextBlock, or the Padding property of the Border:
And now we have an attractive Border surrounding the TextBlock. The BorderThickness property is of type Thickness, the same structure used for Margin or Padding, so you can potentially have four different thicknesses for the four sides. The CornerRadius property is of type CornerRadius, a structure that also lets you specify four different values for the four corners. The Background and BorderBrush properties are of type Brush, so you can use gradient brushes. If you want a Border with a “normal” thickness, you can use one of the pre-defined resources: This represents some long text that needs to display a month name of 200" />
The Versatile PathGeometry LineGeometry, RectangleGeometry, EllipseGeometry, GeometryGroup—those are all convenient special cases of PathGeometry, certainly the most versatile of the Geometry derivatives. With Path and PathGeometry you can perform any vector graphics job that Silverlight allows. PathGeometry defines just two properties of its own: the familiar FillRule and a property named Figures of type PathFigureCollection, a collection of PathFigure objects. Conceptually, a PathFigure is a series of connected lines and curves. The key word here is connected. The PathFigure starts at a particular point, indicated by the StartPoint property, and then the PathFigure continues in a series of connected segments. For these connected segments, PathFigure defines a property named Segments of type PathSegmentCollection, a collection of PathSegment objects. PathSegment is an abstract class, as shown here: Object DependencyObject (abstract) PathSegment (abstract) LineSegment (sealed)
Chapter 13
Vector Graphics
429
PolyLineSegment (sealed) ArcSegment (sealed) QuadraticBezierSegment (sealed) PolyQuadraticBezierSegment (sealed) BezierSegment (sealed) PolyQuadraticBezierSegment (sealed) The PathFigure indicates a StartPoint. The first PathSegment object in the Segments collection continues from that point. The next PathSegment continues from where the first PathSegment left off, and so forth. The last point of the last PathSegment in the Segments collection might be the same as the StartPoint of the PathFigure or it might not. To ensure that a PathFigure is closed, you can set the IsClosed property. If necessary, this will cause a straight line to be drawn from the last point of the last PathSegment to the StartPoint of the PathFigure. PathFigure also defines an IsFilled property that is true by default. This property is independent of any Fill brush you might set on the Path itself. It’s used instead for clipping and hit-testing. In some cases, Silverlight might perceive that an area is filled for purposes of clipping and hit-testing when that is not your intention. In that case, set IsFilled to false. In summary, a PathGeometry is a collection of PathFigure objects. Each PathFigure object is a series of connected lines or curves indicated by a collection of PathSegment objects. Let’s look at the PathSegment derivatives in more detail. LineSegment defines just one property on its own, Point of type Point. It just needs one Point object because it draws a line from the StartPoint property of PathFigure (if the LineSegment is the first segment in the collection) or from the end of the previous segment. PolyLineSegment defines a Points property of type PointCollection to draw a series of connected straight lines. Here’s a PathGeometry with three PathFigure objects containing 3, 2, and 1 PathSegment objects, respectively:
430
Part II Silverlight
The second and third figures are explicitly closed with the IsClosed property, but all three PathFigure collections are filled.
The ArcSegment Things start getting tricky with ArcSegment. An arc is just a partial circumference of an ellipse, but because the ArcSegment must fit in with the paradigm of start points and end points, the arc must be specified with two points on the circumference of some ellipse. But if you define an ellipse with a particular center and radii, how do you specify a point on that ellipse circumference exactly without doing some trigonometry? The solution is to define only the size of this ellipse and not where the ellipse is positioned. The actual location of the ellipse is defined by the two points. I think we need an example. Here’s a little line that begins at the point (120, 240) and ends at the point (360, 240).
This line is just for reference. I want to draw an arc between these same two points. Now obviously there are an infinite number of arcs you can draw between these two points, but for any particular ellipse size, there are only four.
Chapter 13
Vector Graphics
431
Let me demonstrate: Suppose I want the two points to be connected by an arc on the circumference of a circle that has a radius of 144 pixels. Here’s how you specify an ArcSegment of that size that goes between the points (120, 240) and (360, 240):
By default, arcs are drawn from the start point to the end point in a counter-clockwise direction. You can override that behavior by setting the SweepDirection property to Clockwise:
In both those cases, the arc drawn between the two points is the shorter of two possibilities. Here’s the result with the default SweepDirection of CounterClockwise but the IsLargeArc property set to true:
Finally, you can set both IsLargeArc to true and SweepDirection to Clockwise:
When you see them all together, you can get a better sense of how this works (at least on a conceptual level). Think of a circle of a particular size positioned so it meets the two points. You can fit the circle against those two points in one of two ways, and then go around the circle in one of two ways:
Chapter 13
Vector Graphics
433
434
Part II Silverlight
This also works with an ellipse. The following markup is the same as the previous example except the Size property of the ArcSegment has been changed from (144, 144) to (200, 100):
If you want to use an arc based on the circumference of an ellipse that does not have its axes parallel to the horizontal and vertical, you can set the final property of ArcSegment, which is RotationAngle. 16); 0x0000FF00 >> 8); 0x000000FF);
When the Alpha channel byte is 255, the pixel is opaque. A value of 0 means completely transparent, and values in between indicate various levels of transparency. In the PARGB32 pixel format, the P stands for “premultiplied,” which means that if the Alpha value is anything other than 255, then the Red, Green, and Blue values have been already adjusted for the transparency indicated by that Alpha value. To better understand this concept, let’s look at an example involving a single pixel. Suppose you want the pixel to have the following color: Color.FromArgb(128, 0, 0, 255)
That’s blue with 50% transparency. When that pixel is rendered on a particular background surface, the color of the pixel must be combined with the existing colors of the surface. Drawn against a black background, the resultant RGB color is (0, 0, 128), which is the average of the blue pixel and the black background. Drawn against a white background, the resultant color is (127, 127, 255). Each of the three components is an average of the pixel and the surface.
470
Part II Silverlight
With a transparency of anything other than 50%, the resultant color is a weighted average of the pixel source and the surface: The subscripts in the following formulas indicate the “result” of rendering a partially transparent “source” pixel on an existing “surface”:
When a bitmap is rendered on an arbitrary surface, these calculations must be performed for each pixel. Very often a single bitmap is rendered on different surfaces multiple times. The calculations shown above can be speeded up somewhat if the Red, Green, and Blue components of the pixels in the bitmap have already been multiplied by the Alpha channel. These pre-multiplied components are calculated like so:
and similarly for Green and Blue. The resultant formulas for rendering the bitmap have half the number of multiplications:
Whenever you’re working with the Pixels property of WriteableBitmap, you’re dealing with pre-multiplied alphas. For example, suppose you want a pixel in the bitmap to have an RGB color value of (40, 60, 255) but with an Alpha value of 192. The ARGB value in the bitmap would be (192, 30, 45, 192). Each of the R, G, and B values have been multiplied by 192/255 or about 0.75. In any pre-multiplied color value, the R, G, or B values should all be less than or equal to the A value. Nothing will “blow up” if any R, G, or B value is greater than A, but you won’t get the level of transparency you want. When working with ARGB color values without pre-multiplied alphas, there is a distinction between “transparent black,” the ARGB color (0, 0, 0, 0), and “transparent white,” the ARGB color (0, 255, 255, 255). With pre-multiplied alphas, the distinction disappears because transparent white is also (0, 0, 0, 0). When you first create a WriteableBitmap, all the pixels are zero, which you can think of as “transparent black” or “transparent white” or “transparent chartreuse.” By directly writing into the Pixels array of a WriteableBitmap you can create any type of image you can conceive.
472
Part II Silverlight
The center of the WriteableBitmap is the point (200, 200). The code within the nested for loops begins by skipping every pixel that is more than 200 pixels in length from that center. Within the square bitmap, only a circle will have non-transparent pixels. If you connect that center point with any pixel in the bitmap, the line makes an angle with the horizontal axis. The angle of that line is obtained from the Math.Atan2 method. The method then assigns values to the R and B variables based on this angle, creates a color value, and stores it in the Pixels array. A call to Invalidate then makes the actual bitmap image match these pixels, and the bitmap is set to the Source property of the Image element:
Vector Graphics on a Bitmap You can combine the two approaches of drawing on a WriteableBitmap. The next sample displays a Path on a WriteableBitmap against a gradient that uses transparency so that you can see how the premultiplied alphas work. I’m sure you remember the Path element from the end of the previous chapter that displayed a cat from a string in the Silverlight Path Markup Syntax. The goal of the VectorToRaster program is to make a bitmap of precisely the right size for that cat, and then put that cat in the bitmap. The Path Markup Syntax for the cat is defined in a Path element in the Resources section of the MainPage.xaml file:
476
Part II Silverlight
At this point, two nested for loops take x and y though all the pixels of the bitmap. For each pixel, an opacity value is calculated ranging from 0 (transparent) to 1 (opaque): double opacity = Math.Min(1, pointToCenter / ellipseToCenter);
This opacity value is used not only to calculate the Alpha byte but also as a pre-multiplication factor for the Red, Green, and Blue values: byte byte byte byte
A R G B
= = = =
(byte)(opacity (byte)(opacity (byte)(opacity (byte)(opacity
* * * *
255); baseColor.R); baseColor.G); baseColor.B);
Then it’s just a matter of shifting all the color components into place and indexing the Pixels array: int color = A