Six months of Windows Phone Development — Tips, tricks, and performance considerations

A stroke order diagram for the character 羲 TL;DR: I’ve taken plenty of notes while developing my YiXue Chinese dictionary for Windows Phone. Topics covered in this article include performance tips, best practices, and plenty of code snippets for Windows Phone.

Introduction

This article presents notes and remarks that I gathered while working on a Chinese Dictionary App for Windows Phone, YiXue Chinese Dictionary: mistakes I made, fun tips I wrote down, and so on.
I initially didn’t really intend to create a full blog post out of these notes, but their increasing number, and my app recently placing second in Microsoft France’s App Awards contest, gave me enough motivation to share them with the community. Along with various tips and tricks and answers to often-asked (but seldom answered) questions, I will discuss a number of performance improvements that specifically apply to Windows Phone apps.

You can skip the DOs and DON’Ts and jump directly to the tips and tricks section, or even straight to the performance tips section, if you feel really confident with Windows Phone development.

App development tips: DOs and DON’Ts

DO keep the size of your XAP package (reasonably) small

Contents of the YiXue Dictionary XAP file

XAP files are just ZIP files with a fancy file extension.Shown above, the contents of the YiXue Dictionary XAP package.

As of March 2013, the limit for users to be able to download your app over the air is 20MB. Above this limit, users will have to switch to connect to a WiFi network before they can download your app. Below 20MB, you’re safe.
That said, keeping an eye on your XAP size is definitely a good idea. PC World ran two studies in 2012, concluding that the average 3G download speed in the United States is about 2.5mbps. That’s about 3.2 seconds per MB in your app’s XAP — about 1 minute for a 20MB app download over 3G. In areas with less good coverage, this will probably climb up to 2 or 3 minutes (assuming 1mbps).

DO use the Silverlight Toolkit for Windows Phone

Logo of the Windows Phone toolkit

The Silverlight toolkit for Windows Phone is a library distributed by Microsoft, which includes a number of controls which didn’t make it into the official SDK. It’s frequently updated, it contains a number of gems that can widely improve your app, and you can download it using nuget straight from Visual studio (more information here).

Though I was initially reluctant to increase my download size by bundling a 500kB dll with my app, I quickly realized it can dramatically improve the user experience. I’ll focus on just two things here: The PerformanceProgressBar, and page transitions (the Windows Phone Geek website has covered in great details pretty much every control in the toolkit).

The PerformanceProgressBar

The performance progressbar

It’s been said pretty much everywhere, but using the default Windows Phone ProgressBar in Indeterminate mode is a bad idea. The animation is based on custom-themed sliders, which eat up a lot of processing power to display the animation (more on that below). The PerformanceProgressBar included in the Silverlight Toolkit fixes that.

Load the toolkit by adding the following to the phone:PhoneApplicationPage tag:

xmlns:tk="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

and create a progress bar using the following:

<tk:PerformanceProgressBar IsIndeterminate="true" />

Page transitions (toolkit:TransitionService.NavigationInTransition)

It’s pretty standard in all system apps on Windows Phone; when you tap a button or a link, the current page will flip out, and a new page will flip in.

Apart from the eye candy, the main advantage of this animation is that it vastly improves the perceived speed of your app, by virtually supressing the delay that separates user actions (tap a button, click a link, pick a ListBox item) and the corresponding reaction; the animation starts immediately, and the constructor and OnNavigatedTo method of your new page are called while the animation runs.

This way, your users have direct feedback that their tap was taken into account. I initially implemented that in YiXue because the pretty complex layout of the Dictionary page would take a split second to render, and users would tap two or three times on the “Dictionary” button, thinking the tap hadn’t been taken into account.

To improve the visual consistency of your app, I recommend using the animation on all your pages. One neat thing is that this animation can be bundled in a style, and reused on all pages without repeating the same code on each. That’s something I didn’t know at first, and that was another reason I was reluctant to generalize the use of that animation to all my pages. But it’s actually rather simple:

First declare the toolkit namespace in your App.xaml using the following line :

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

Then declare the following style in your App.xaml file:

	<Style x:Key="TransitionPageStyle" TargetType="phone:PhoneApplicationPage">
		<Setter Property="toolkit:TransitionService.NavigationInTransition">
			<Setter.Value>
				<toolkit:NavigationInTransition>
					<toolkit:NavigationInTransition.Backward>
						<toolkit:TurnstileTransition Mode="BackwardIn"/>
					</toolkit:NavigationInTransition.Backward>
					<toolkit:NavigationInTransition.Forward>
						<toolkit:TurnstileTransition Mode="ForwardIn"/>
					</toolkit:NavigationInTransition.Forward>
				</toolkit:NavigationInTransition>
			</Setter.Value>
		</Setter>
		
		<Setter Property="toolkit:TransitionService.NavigationOutTransition">
			<Setter.Value>
				<toolkit:NavigationOutTransition>
					<toolkit:NavigationOutTransition.Backward>
						<toolkit:TurnstileTransition Mode="BackwardOut"/>
					</toolkit:NavigationOutTransition.Backward>
					<toolkit:NavigationOutTransition.Forward>
						<toolkit:TurnstileTransition Mode="ForwardOut"/>
					</toolkit:NavigationOutTransition.Forward>
				</toolkit:NavigationOutTransition>
			</Setter.Value>
		</Setter>
	</Style>

Finally, reuse your new style on all pages like this:

<phone:PhoneApplicationPage 
    (...)
    Style="{StaticResource TransitionPageStyle}">

DON’T show a minimized application bar on a page with TextBoxes

The application bar going over the keyboard

Trying to hit the “change language” button (繁) of the keyboard when the application bar is minimized generally results in a lot of frustration.

That’s a usability tip which plenty of apps on the market place fail to follow, especially translation / dictionary apps. If have TextBoxes on a page showing an ApplicationBar, make sure it is not minimized. Otherwise, your international users will have the frustration of hitting the application bar instead of the “change language” button on the keyboard, thereby expanding the application bar, above the keybord. See the screenshot if you’re not sure what I mean by that.

The solution is pretty simple : either hide the application bar when your TextBox gets the focus, or just set the ApplicationBar‘s Mode to Default instead of Minimized.

DO:

<shell:ApplicationBar Mode="Default">

DON’T:

<shell:ApplicationBar Mode="Minimized">

DON’T use the GestureService from the Silverlight toolkit

The flick gesture However awesome it might be, the Silverlight toolkit still has a few rough edges. In particular, it doesn’t unregister it’s listener when you use a GestureService (GestureService allows you to catch touch events like Flick, Slide, and so on). This will bite you if you use an InkSurface for drawing at some point, because a GestureService loaded on another page will keep firing touch events although the page it initially came from should have been disposed. Using the GestureService, furthermore, will prevent your pages from being properly disposed.
Fortunately, there’s a pretty simple workaround: use the XNA TouchPanel instead.

First, enable the listener (can be done in yoru app initialization code):

	Microsoft.Xna.Framework.Input.Touch.TouchPanel.EnabledGestures = 
		Microsoft.Xna.Framework.Input.Touch.GestureType.Flick;

Then, register the ManipulationCompleted event on a control:

	private void Control_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) {
		GestureSample sample = TouchPanel.ReadGesture();
		int delta_x = gesture.Delta.X, delta_y = gesture.Delta.Y;
		
		if (gesture.GestureType == GestureType.Flick 
				&& Math.Abs(delta_x) > 1.5 * Math.Abs(delta_y))
					//User flicked horizontally (delta_x < 0: flicked left; otherwise right)
	}

That works well, but not perfectly. Can you guess why? — The catch is that the TouchPanel will record all gestures made on the page; even if only part of your page is touch-enabled using ManipulationCompleted, the call to TouchPanel.ReadGesture will also report gestures made outside of that area.

The problem, luckily, is easily fixed by discarding all gestures but the last when ManipulationCompleted fires; add the following snippet to the previous function:

	GestureSample? sample = null;
	while (TouchPanel.IsGestureAvailable) //Discard all gestures but the last
		sample = TouchPanel.ReadGesture();
	
	if (sample.HasValue)
		if (gesture.GestureType == GestureType.Flick 
				&& Math.Abs(delta_x) > 1.5 * Math.Abs(delta_y))
					//User flicked horizontally (delta_x < 0: flicked left; otherwise right)

Cool development tips and code snippets

Choice between light and dark themes

Windows Phone users can choose between light and dark themes, which at the core are just white text on a dark background, or black text on a light background.

Override system-default colors and brushes if your app uses a custom background

If your app includes a background image (see my tip on that below), you’ll probably want to force one of the color themes (light / dark). If you do that I’d advise going for a rather dark background, since that will use much less battery ; in that case you will want to enfore the dark theme in your app. I did that in YiXue dictionary, including a piece of famous Chinese calligraphy in the background, and users told me they really liked it.

First, note that you can’t directly replace an existing value in the App.Current.Resources dictionary. Removing the key and adding it again, however, does work (took me a while to find out):

	private static void SetAppResource(object key, object value) {
		//Cannot replace an existing value:
		//using 'App.Current.Resources[foo] = bar' won't work.
		
		if (App.Current.Resources.Contains(key)) 
			App.Current.Resources.Remove(key);

		App.Current.Resources.Add(key, value);
	}

You can also override properties of already existing objects, like system brushes. For your convenience, here is a quick snippet setting most of the common color settings to their default Light Theme values.

//Code from http://pit-claudel.fr/clement/blog/ ; attribution much appreciated!
(App.Current.Resources["PhoneForegroundBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush).Color = Colors.Black;

(App.Current.Resources["PhoneDisabledBrush"] as SolidColorBrush).Color = Color.FromArgb(102, 255, 255, 255);
(App.Current.Resources["PhoneInactiveBrush"] as SolidColorBrush).Color = Color.FromArgb(51, 255, 255, 255);

(App.Current.Resources["PhoneContrastBackgroundBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneContrastForegroundBrush"] as SolidColorBrush).Color = Colors.Black;

(App.Current.Resources["PhoneTextBoxBrush"] as SolidColorBrush).Color = Color.FromArgb(191, 255, 255, 255);
(App.Current.Resources["PhoneTextBoxForegroundBrush"] as SolidColorBrush).Color = Colors.Black;
(App.Current.Resources["PhoneTextBoxSelectionForegroundBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneTextBoxEditBackgroundBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneTextBoxEditBorderBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneTextBoxReadOnlyBrush"] as SolidColorBrush).Color = Color.FromArgb(119, 0, 0, 0);

(App.Current.Resources["PhoneRadioCheckBoxBrush"] as SolidColorBrush).Color = Color.FromArgb(191, 255, 255, 255);
(App.Current.Resources["PhoneRadioCheckBoxCheckBrush"] as SolidColorBrush).Color = Colors.Black;
(App.Current.Resources["PhoneRadioCheckBoxCheckDisabledBrush"] as SolidColorBrush).Color = Color.FromArgb(102, 0, 0, 0);
(App.Current.Resources["PhoneRadioCheckBoxDisabledBrush"] as SolidColorBrush).Color = Color.FromArgb(102, 255, 255, 255);
(App.Current.Resources["PhoneRadioCheckBoxPressedBrush"] as SolidColorBrush).Color = Colors.White;
(App.Current.Resources["PhoneRadioCheckBoxPressedBorderBrush"] as SolidColorBrush).Color = Colors.White;

(App.Current.Resources["PhoneBorderBrush"] as SolidColorBrush).Color = Color.FromArgb(191, 255, 255, 255);
(App.Current.Resources["PhoneSubtleBrush"] as SolidColorBrush).Color = Color.FromArgb(153, 255, 255, 255);

As a side note, remember that you can’t set the application bar’s color to pure white. Just set it to something like #EFEFEF instead, and it will look just as if it was all white.

DO:

shell:SystemTray.ForegroundColor="#FEFEFE"

DON’T:

shell:SystemTray.ForegroundColor="#FFFFFF"

Let users select and copy text

Text highlighted in a control that looks like a textblock

A TextBox styled like a TextBlock, with some text highlighted

Since text can’t be directly copied from TextBlocks, most websites advise that you use a ReadOnly TextBox instead, styled to look just like a TextBox. That’s pretty sweet, but it fails to mention that users need to give input focus to a TextBox before they can select text. Which means that they’ll have to tap on your modified TextBox twice: once to give it focus (but the caret won’t appear, since the TextBox is read-only), and a second time to actually select text. As a user, this drives me nut.

Luckily there’s a pretty simple work-around: if it’s a very small block of text, intercept the Tap event, and select all text immediately. Otherwise, use the GotFocus event. Users can then refine their selection if they want, but they can also copy all contents straight away if they want to.

	private void CopyableTextArea_Tap(object sender, GestureEventArgs e) {
		(sender as TextBox).SelectAll();
	}

Share a background on all your pages

Pages sharing a common background

Pages sharing a common background, with the Background property of their LayoutRoot set to Transparent.

If you want to set an app-level background image (perhaps the same as your splash screen, although I’d advise using something a bit darker to improve the legibility of text over your background), manually setting the background image on all your pages is not the best, especially if you use page-turning animations.
Indeed, if you set the background on a per-page level, you’ll quickly notice that the background flips with the rest of the page content when you change pages. It would be much better if it stayed in place, and only page contents flipped out when changing pages. It’s actually pretty easy to do if you know the trick (that, too, took me a while to find):

In App.xaml:

<ImageBrush x:Key="MainBackground" ImageSource="/resources/MainBackground.jpg" />

In App.xaml.cs

(App.Current as App).RootFrame.Background = App.Current.Resources["MainBackground"] as Brush;

Now the catch is, if some of your pages show an application bar, and other don’t, your background will be resized to fit only the available space: the application bar will force it to shrink — that’s pretty ugly. Instead, what I do is add a 72 pixels high row to the LayoutRoot grid, and set the ApplicationBar’s transparency to .99 (choosing a value other than 1 draws the app bar above your page instead of shrinking it). That way, the Application bar is drawn above your page, and nothing is drawn below it because there’s an empty row of just the right height below:

DO:
In your Grid‘s RowDefinitions:

<RowDefinition Height="72" />

Further below:

	<phone:PhoneApplicationPage.ApplicationBar>
		<shell:ApplicationBar IsVisible="True" Opacity="0.99" Mode="Default">
			(...)
		</shell:ApplicationBar>
	</phone:PhoneApplicationPage.ApplicationBar>

Performance tips

Load your data files faster

Pages sharing a common background

Abandonning your ProgressBars in favour of text-only loading messages can yield substantial performance improvements.

Really that’s pretty universal advice. If you never tried, you’ll probably be surprised how much performance you can gain by simply disabling your “loading” progressbar and replacing it with a Loading... message, possible including a percentage at the end, as in Loading... 10%. In Yixue, where the initial loading of the dictionary has been an area that I’ve worked a lot on, disabling the Loading... progressbar has reduced the overall loading time from 3.2 seconds to 2.7s. That’s a sizable 0.5 seconds, or 15% percent performance improvement :)

Another common mistake when displaying progress is to use a timer running in the background and firing every x milliseconds to update your progressbar. I did this in YiXue, because the animation of the progress bar that I used back then would be smoother that way. The loading time was 3.8 second then, and the UI timer fired every 20ms. Increasing this delay to 100ms took the loading time down to 3.5 seconds. Removing the UI timer altogether and firing a ReportProgress event after each significant loading step took that down to 3.2 seconds… and getting rid of the progress bar I went down to 2.7s.

Load your pages faster

XAML complexity is, in my experience, the main cause for pages loading slowly. Complex bindings (or even simple ones, really, if you’re populating a list) are also very time-consuming. If you’re after the last bit of performance, consider populating your list manually, possibly be removing your listbox alltogether, and using a vertical StackPanel to which you directly add your items, in code-behind. I know that doesn’t really fit in the MVVM pattern, but when I had to display a grid of 214 elements, binding a ListBox using a WrapPanel as its ItemsPanel took close to 2 seconds, while populating a StackPanel only took 0.3 seconds.

To the best of my knowledge, the toolkit’s WrapPanel doesn’t do any kind of UI virtualization, making it useless for displaying moderately large amounts of information.

Don’t use the PivotControl for what it’s not made for

The PivotControl has to render all its pages before it’s displayed. That’s a huge performance killer when you use it to diplay multiple data sets (plus, it’s against the design guidelines: Pivots are used throughout the system to show multiple visualisations of the same data set (all mail, unread, starred, and so on, for example)).

In YiXue Dictionary I have study lists (roughly equivalent to stacks of flash cards, only better ;), and I initially displayed them using a Pivot control, one list per pivot page. When I tested that page with 12 study lists contining about 40 words each, not only did it take 2 seconds to render the page, but the memory profiler reported a 130MB memory usage (!). I’ve now replaced it with a single list, letting users flip between each page using gestures, which doesn’t use more than a few MBs.

Get better I/O performance

The WP emulator is really neat, but there’s one thing that it really doesn’t mimic realistically, namely IO performance. Although it’s much better on my Lumia than on my old HTC, I/O is still much slower on a phone than on your development computer. That’s really something to keep in mind when testing your app, especially if you don’t have a device to test on. Very roughly, my testing indicates that an I/O operation that takes 1 seconds on your computer will take 2 or 3 seconds on the Lumia 920, 3 to 5 seconds one Lumia 800, and 5 to 7 seconds on the Trophy. Take these timings with a pitch of salt since they are really gross approximations, but the general figures are about that. Similarly, garbage collections will be much slower on your phone than in the emulator; if your loading procedure involves a lot of text parsing, it’ll suffer a lot from that on your phone.

So how do you improve that loading performance? Turns out that there is a number of simple tricks that you can use. I won’t get into too much details here, because there’s so much to say, and because you obviously should run benchmarks for your micro-optimisations, but the following general principles will help :

Ditch String.Split

Pages sharing a common background

Screenshot from MDSN’s remarks section regarding String.Split.

String.Split is nice, but it’s a garbage collector nightmare. It allocates plenty of intermediate buffers (and the documentation is pretty straightforward about that, with a section called “Performance Considerations” well worth reading), and it’s too generic to be as fast as it could be. If you’re really after the last bit of performance, re-code the split function by hand. It’s really not hard, but here it is:

	int start = -1, end = -1;
	List<string> parts = new List<string>();
	
	while (end + 1 < string_to_split.Length) { //Assumes that the string to split ends with a '\t'
		start = ++end;
		while (string_to_split[end] != '\t')
			++end;

		parts.Add(string_to_split.Substring(start, end - start));
	}

In YiXue, that shaved an extra 30% off the loading time.

Set images as Content, not resource

Changing the build action of your images from Resource to Content will neatly improve the splash screen time, because your images will only be loaded when needed, instead of being loaded when your app starts.

Minify your XML files.

Minified/Not-minified XML files comparison

Left: efficient representation. Right: Human-readable version of the same data. Smaller is better.

Most apps store use data as XML, using an XMLSerializer. That’s really neat, but it can get slow if you don’t take a few precautions. There are two things that really helped me in YiXue: first one is disabling white space in the XML output; your files won’t be as legible by a human as they were before, but it easily saved me 20% off the size of my user data files, and the parser won’t see the difference.

	XmlWriterSettings settings = new XmlWriterSettings { Indent = false, NewLineHandling = NewLineHandling.None };

Use defaults in your XML serialization attributes

Second one is specifying defaults. In YiXue there can be plenty of info associated to each word in your study lists, but most words don’t have all that info filled in. You can get the Xml serializer to omit the empty properties by simply specifying the default value for the attribute this way:

	[DefaultValue(0)] public int CorrectAnswersGiven;

I’d also advise choosing very short aliases for your attributes and elements names; for example:

	[XmlAttribute(AttributeName = "f")] public string foo;

This will further shrink your data files, and will make it much easier to rename your variables should you want to.

Improve perceived performance by showing a message with actual content while the app loads

Tips show while loading the appIf you need time to load app resources (beyond the split-second), entertain your users while your app is loading by including tips and fun facts: they won’t notice the delay if they have something else to focus on — game developers know this, and always include tips in their loading screens. This principle is easily extended to other apps.
In YiXue, I show tips and fun facts about learning Chinese while the app loads.

Conclusion

That’s pretty much all for now. Hopefully that helps you create even better apps!

You can follow me on Twitter here (me) and here (my YiXue Dictionary app). Of course, links to http://pit-claudel.fr/clement/yixue/ and to this blog are most appreciated. And since I’m sure I forgot plenty:

DO: share your own tips and tricks in the comments!

Happy WP development!
Clément.

PS: If you liked this article, you can flattr this blog post using the link below, or even better buy the app!

3 thoughts on “Six months of Windows Phone Development — Tips, tricks, and performance considerations

  1. Chai

    More posts like this will make the Windows Phone development easier, faster and Developers happier. Thanks man!

Comments are closed.