460 likes | 613 Views
What Silverlight Developers Must Know to Build Great Mobile Apps. Jeff Prosise http://www.wintellect.com/CS/blogs/jprosise/default.aspx http://twitter.com/#!/jprosise. Page Orientation. Display can rotate when orientation changes
E N D
What Silverlight Developers Must Know to Build Great Mobile Apps Jeff Prosise http://www.wintellect.com/CS/blogs/jprosise/default.aspx http://twitter.com/#!/jprosise
Page Orientation • Display can rotate when orientation changes • PhoneApplicationPage.SupportedOrientations controls behavior when orientation changes • Portrait – Display does not rotate • Landscape – Display does not rotate • PortraitOrLandscape – Display rotates automatically • PhoneApplicationPage.OrientationChanged events fire when orientation changes • If SupportedOrientations = "PortraitOrLandscape"
Preventing the Page from Rotating // In MainPage.xaml SupportedOrientations="Portrait"
Rotating the Page // In MainPage.xaml SupportedOrientations="PortraitOrLandscape"
Customizing the Rotated Page // In the page constructor this.OrientationChanged+= new EventHandler<OrientationChangedEventArgs>(OnOrientationChanged); private void OnOrientationChanged(object sender, OrientationChangedEventArgs e) { if ((e.Orientation & PageOrientation.Landscape) != 0) { // TODO: Adjust the page for landscape mode } else // Portrait { // TODO: Adjust the page for portrait mode } }
SIPs and Input Scopes • SIP = Software Input Panel • InputScope property of text elements permits SIP to be tailored to input type • More than 60 input scopes available • Text, Number, TelephoneNumber, EmailSmtpAddress, etc. InputScope="Default" InputScope="TelephoneNumber"
Specifying an Input Scope // Short form (XAML) <TextBox x:Name="Phone" InputScope="TelephoneNumber" /> // Long form (XAML with IntelliSense) <TextBox x:Name="Phone"> <TextBox.InputScope> <InputScope> <InputScopeNameNameValue="TelephoneNumber" /> </InputScope> </TextBox.InputScope> </TextBox> // C# Phone.InputScope = new InputScope() { Names = { new InputScopeName() { NameValue = "TelephoneNumber" } } };
Stock Font Resources • Built-in FontFamily resources • PhoneFontFamilyNormal, PhoneFontFamilyLight, PhoneFontFamilySemiLight, PhoneFontFamilySemiBold • Built-in FontSize resources • PhoneFontSizeSmall, Normal, Medium, MediumLarge, Large, ExtraLarge, ExtraExtraLarge, Huge • Built-in Style resources • PhoneTextNormalStyle, PhoneTextAccentStyle, PhoneTextContrastStyle, and many others • Combine FontFamily, FontSize, and Foreground
Stock Font Resources in Action PhoneTextNormalStyle PhoneTextTitle1Style PhoneTextExtraLargeStyle PhoneTextSubtleStyle PhoneTextAccentStyle
Using Stock Font Resources <TextBlock ... Style="{StaticResourcePhoneTextExtraLargeStyle}" /> <TextBlock ... Style="{StaticResourcePhoneTextAccentStyle}" /> {StaticResource} markup extension loads the specified resource
Stock Brush Resources • Approximately 25 built-in brush resources • PhoneAccentBrush and PhoneSubtleBrush • PhoneBackgroundBrush and PhoneForegroundBrush • PhoneChromeBrush and PhoneDisabledBrush • PhoneBorderBrush and many others • Colors change with theme and accent colors • Complete list at http://msdn.microsoft.com/en-us/library/ff769552(v=vs.92).aspx • Also in ThemeResources.xaml
Stock Brush Resources in Action Dark theme Blue accent Light theme Blue accent Dark theme Orange accent
Using Stock Brush Resources <Rectangle Width="300" Height="80" Stroke="{StaticResourcePhoneBorderBrush}" Fill="{StaticResourcePhoneAccentBrush}" />
Touch Input • Mouse events • Touch event promotion; single-touch only • Frame.TouchReported events • Low-level, multi-touch capable • Manipulation events • Drag/pan gestures and pinch gestures • Built-in inertia support • GestureListener events • Included in Silverlight for Windows Phone Toolkit • Rich gesture support
Mouse Events • Primary touch events promoted to mouse events • MouseLeftButtonDown, MouseLeftButtonUp, MouseMove, MouseEnter, and MouseLeave • "Primary" means first finger down • Build touch interfaces using mouse logic only • Same code works in Silverlight desktop apps • Single-touch only!
Responding to Touch with Mouse Events // XAML <Rectangle Width="300" Height="200" Fill="Red" MouseLeftButtonDown="OnRectangleClicked" /> // C# void OnRectangleClicked(object sender, RoutedEventArgs e) { (sender as Rectangle).Fill = new SolidColorBrush(Colors.Blue); }
Touch.FrameReported Events • Low-level touch events fired by Silverlight • Up to four fingers at a time • Fired at application level, not by individual objects • Information regarding individual fingers conveyed in TouchPoint objects revealing: • Whether finger went down, moved, or lifted up • Finger position relative to specified UI element • Contact area size • Topmost element underneath finger and finger ID • Primary touch points promoted to mouse events
Responding to Touch // XAML <Rectangle x:Name="Rect" Width="300" Height="200" Fill="Red" /> // C# Touch.FrameReported += OnFrameReported; ... void OnFrameReported(object sender, TouchFrameEventArgs e) { // Single touch only TouchPoint point = e.GetPrimaryTouchPoint(null); if (point.Action == TouchAction.Down && point.TouchDevice.DirectlyOver == Rect) { Rect.Fill = new SolidColorBrush(Colors.Blue); } }
Responding to Multi-Touch // XAML <Rectangle x:Name="Rect" Width="300" Height="200" Fill="Red" /> // C# Touch.FrameReported += OnFrameReported; ... void OnFrameReported(object sender, TouchFrameEventArgs e) { TouchPointCollection points = e.GetTouchPoints(null); foreach (TouchPoint point in points) { if (point.Action == TouchAction.Down && point.TouchDevice.DirectlyOver == Rect) Rect.Fill = new SolidColorBrush(Colors.Blue); } }
Manipulation Events • High-level touch events fired by UI elements • Do not support simultaneous manipulation • Perform implicit capture when element is moved • Built-in inertia support • Velocity info in ManipulationCompleted events • You provide logic to use the velocity info • Consolidate interaction of two fingers into X and Y scaling factors for pinch-zooming
Moving a UI Element // XAML <Rectangle ... Fill="Red" ManipulationDelta="OnManipulationDelta"> <Rectangle.RenderTransform> <TranslateTransform /> </Rectangle.RenderTransform> </Rectangle> // C# private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e) { Rectangle rect = sender as Rectangle; TranslateTransform transform = rect.RenderTransform as TranslateTransform; transform.TranslateX += e.DeltaManipulation.Translation.X; transform.TranslateY += e.DeltaManipulation.Translation.Y; }
Scaling a UI Element // XAML <Rectangle ... Fill="Red" ManipulationDelta="OnManipulationDelta"> <Rectangle.RenderTransform> <ScaleTransform /> </Rectangle.RenderTransform> </Rectangle> // C# private void OnManipulationDelta(object sender, ManipulationDeltaEventArgse) { if (e.DeltaManipulation.Scale.X > 0.0 && e.DeltaManipulation.Scale.Y > 0.0) { Rectangle rect = sender as Rectangle; ScaleTransform transform = rect.RenderTransformas ScaleTransform; transform.ScaleX *= e.DeltaManipulation.Scale.X; transform.ScaleY *= e.DeltaManipulation.Scale.Y; } }
Gestures • Three ways to support gestures • Roll your own using Touch.FrameReported events • Use the XNA Framework's TouchPanel class • Use the Silverlight for Windows Phone Toolkit's GestureListener class • GestureListener makes it easy • Event-based API recognizes six basic gestures • No simultaneous manipulation of UI elements • Microsoft.Phone.Controls.Toolkitassembly
Responding to Taps // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListener Tap="OnTap" /> </toolkit:GestureService.GestureListener> </Rectangle> // C# private void OnTap(object sender, GestureEventArgs e) { (sender as Rectangle).Fill = new SolidColorBrush(Colors.Blue); } GestureBegin Tap GestureCompleted
Responding to Double Taps // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListenerDoubleTap="OnDoubleTap" /> </toolkit:GestureService.GestureListener> </Rectangle> // C# private void OnDoubleTap(object sender, GestureEventArgs e) { (sender as Rectangle).Fill = new SolidColorBrush(Colors.Blue); } GestureBegin Tap GestureCompleted GestureBegin DoubleTap GestureCompleted
Responding to Holds // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListener Hold="OnHold" /> </toolkit:GestureService.GestureListener> </Rectangle> // C# private void OnHold(object sender, GestureEventArgs e) { (sender as Rectangle).Fill = new SolidColorBrush(Colors.Blue); } GestureBegin Hold GestureCompleted
Moving a UI Element // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListenerDragDelta="OnDragDelta" /> </toolkit:GestureService.GestureListener> <Rectangle.RenderTransform> <TranslateTransform /> </Rectangle.RenderTransform> </Rectangle> // C# private void OnDragDelta(object sender, DragDeltaGestureEventArgs e) { Rectangle rect = sender as Rectangle; TranslateTransform transform = rect.RenderTransform as TranslateTransform; transform.X += e.HorizontalChange; transform.Y += e.VerticalChange; } GestureBegin DragStarted DragDelta DragDelta ... DragDelta DragDelta DragCompleted GestureCompleted
Responding to Flicks // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListener Flick="OnFlick" /> </toolkit:GestureService.GestureListener> <Rectangle.RenderTransform> <TranslateTransform /> </Rectangle.RenderTransform> </Rectangle> // C# private void OnFlick(object sender, FlickEventArgs e) { Rectangle rect = sender as Rectangle; TranslateTransform transform = rect.RenderTransform as TranslateTransform; InertiaAnimation.To = transform.X + e.HorizontalVelocity / 10.0; InertiaStoryboard.Begin(); } GestureBegin DragStarted DragDelta DragDelta ... Flick DragCompleted GestureCompleted
Scaling a UI Element // XAML <Rectangle Width="300" Height="200" Fill="Red"> <toolkit:GestureService.GestureListener> <toolkit:GestureListenerPinchDelta="OnPinchDelta" /> </toolkit:GestureService.GestureListener> <Rectangle.RenderTransform> <ScaleTransform /> </Rectangle.RenderTransform> </Rectangle> // C# private void PinchDelta(object sender, PinchGestureEventArgs e) { Rectangle rect = sender as Rectangle; ScaleTransform transform = rect.RenderTransformas ScaleTransform; transform.ScaleX = _cx * e.DistanceRatio; transform.ScaleY = _cy * e.DistanceRatio; } GestureBegin PinchStarted PinchDelta PinchDelta ... PinchDelta PinchDelta PinchCompleted GestureCompleted
Tombstoning • Occurs when app is deactivated and terminated • To restore app to same state when reactivated, persist state in application state or page state • Or use isolated storage if volume of data exceeds limits on application state and page state • Isolated storage is 30% to 50% slower • Use page's OnNavigatedFrom and OnNavigatedTo methods to save and restore page state • App is allowed 10 seconds to tombstone data
Saving Page State protected override void OnNavigatedFrom(NavigationEventArgs e) { this.State["x1"] = x1; this.State["y1"] = y1; this.State["x2"] = x2; this.State["y2"] = y2; }
Restoring Page State protected override void OnNavigatedTo(NavigationEventArgs e) { if (this.State.ContainsKey("x1")) x1 = (double)this.State["x1"]; if (this.State.ContainsKey("y1")) y1 = (double)this.State["y1"]; if (this.State.ContainsKey("x2")) x2 = (double)this.State["x2"]; if (this.State.ContainsKey("y2")) y2 = (double)this.State["y2"]; }
When Does Tombstoning Occur? • When user presses Start button • Exception: Start and Back pressed in rapid succession • When app launches a launcher or chooser • PhotoChooserTask and certain others excepted • When device time-out occurs • Screen locks due to inactivity • Always assume tombstoning will occur when app is deactivated, even though it may not • Tombstoning will occur less often in future builds
Location Awareness • Location service provides location data • Latitude, longitude, altitude, speed, and course • System.Device.Location.GeoCoordinateWatcher class provides core API • Data from multiple sources with varying accuracy • A-GPS (most accurate), Wi-Fi (WPS), and cell towers • System.Device assembly • Be sensitive to battery drain • Only use location when needed • Higher accuracy = Higher power consumption
Starting the Location Service // Create and configure a GeoCoordinateWatcher GeoCoordinateWatcher watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); watcher.MovementThreshold = 20; // 20 meters // Register event handlers watcher.StatusChanged += new EventHandler <GeoPositionStatusChangedEventArgs>(OnStatusChanged); watcher.PositionChanged += new EventHandler <GeoPositionChangedEventArgs<GeoCoordinate>> (OnPositionChanged); // Start the service watcher.Start();
Tracking Position void OnPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { // Latitude, longitude, and altitude Latitude.Text = e.Position.Location.Latitude.ToString("0.000"); Longitude.Text = e.Position.Location.Longitude.ToString("0.000"); Altitude.Text = e.Position.Location.Altitude.ToString("0.000"); // Direction and speed Altitude.Text = e.Position.Location.Course.ToString("0.000"); Altitude.Text = e.Position.Location.Speed.ToString("0.000"); // Horizontal and vertical accuracy Altitude.Text = e.Position.Location.HorizontalAccuracy.ToString("0.000"); Altitude.Text = e.Position.Location.VerticalAccuracy.ToString("0.000"); }
Stay up to date with MSDN Belux • Register for our newsletters and stay up to date:http://www.msdn-newsletters.be • Technical updates • Event announcements and registration • Top downloads • Follow our bloghttp://blogs.msdn.com/belux • Join us on Facebookhttp://www.facebook.com/msdnbehttp://www.facebook.com/msdnbelux • LinkedIn: http://linkd.in/msdnbelux/ • Twitter: @msdnbelux DownloadMSDN/TechNet Desktop Gadgethttp://bit.ly/msdntngadget
TechDays 2011 On-Demand • Watchthis session on-demand via Channel9http://channel9.msdn.com/belux • Download to your favorite MP3 or video player • Get access to slides and recommended resources by the speakers