Windows Phone
Pragmatic Tips for Building Better Windows Phone Apps
Andrew Byrne
I love building apps for Windows Phone. I love creating code samples and writing about the Windows Phone platform as part of my day job, though I’ve had my ups and downs along the way. Throughout all of this work I’ve stumbled and picked myself up, and dug myself into holes, but climbed back out. What I want to do is smooth out this obstacle course for you with some pointers in this article. I’ll talk about controls, User Datagram Protocol (UDP) Multicast, the Ad Control, isolated storage, tools and 256MB devices. So sit back, relax and enjoy.
Specify the TargetName when Using a HyperlinkButton
The HyperlinkButton control is a button control that displays a hyperlink. I use this all the time to help the user navigate my app, or the Web.
In one of my apps I was using a HyperlinkButton to show some help information, and I wanted a hyperlink to take the user to more information on the Web. So I did the following (details omitted):
<HyperlinkButton NavigateUri="http://www.msdn.com">Help</HyperlinkButton>
Simple, right? I ran my app, navigated to the page containing my little help button and clicked on the link. Nothing happened. I then tried to debug the app and clicked on the link again. This time I got a NavigationFailed exception that said:
Navigation is only supported to relative URIs that are fragments, or begin with ‘/,’ or which contain ‘;component/.’ Parameter name: uri.
The head scratching began. Reading the documentation a little more carefully revealed the problem. I had not set the TargetName property on the HyperlinkButton control. This attribute specifies the target window for the hyperlink. Not setting it meant I was actually using the default value of “”, an empty string. This translates into the HyperlinkButton attempting to load the contents into the page in which the link was clicked. That can’t work, because the page from where it came is not a browser. Here’s what I should’ve done:
<HyperlinkButton NavigateUri="http://www.msdn.com"
TargetName="_blank">Help</HyperlinkButton>
This loads the linked document into a new browser instance. Clicking on the link now worked.
The RichTextBox control can also display hyperlinks using the HyperLink inline element. This will have the same issue. Set the TargetName of the HyperLink or head scratching will ensue! For more information on the controls available for Windows Phone, see wpdev.ms/windowsphonecontrols.
Use DataContext to Reference a Bound Object in a ListBox
Sometimes you’ll want to perform an action when the user taps and holds. For example, a user can tap and hold an item in a list. Determining which item is being held can be a little tricky. I’ll illustrate using a simplified ViewModel as shown in Figure 1.
Figure 1 A Simple ViewModel
public class SimpleViewModel : INotifyPropertyChanged
{
private string _myText = "Initial Value";
public string MyText
{
get
{
return _myText;
}
set
{
if (value != _myText)
{
_myText = value;
NotifyPropertyChanged("MyText");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(PropertyName));
}
}
}
I want to expose a collection of these as shown here:
public ObservableCollection<SimpleViewModel> Items { get; private set; }
In the page’s constructor I instantiate this collection and add some test data, as shown in Figure 2.
Figure 2 Instantiating a Collection and Adding Test Data in the Page Constructor
// Constructor
public MainPage()
{
InitializeComponent();
// Crete some test data
Items = new ObservableCollection<SimpleViewModel>();
Items.Add(new SimpleViewModel
{
MyText = "My first item",
});
Items.Add(new SimpleViewModel
{
MyText = "My second item",
});
Items.Add(new SimpleViewModel
{
MyText = "My third item",
});
this.DataContext = Items;
}
I want to display the data in Figure 2 in a list on a page and add a little graphic to each item listed. I do this with standard XAML Binding to a ListBox control, as follows:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="duck.png" Height="60" Width="60"/>
<TextBlock Text="{Binding MyText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The end result is a list of items with a cute image and the MyText value for each item, as shown in Figure 3.
.jpg)
Figure 3 A List of Items with an Image
Next, I want to bring up a context menu when the user taps and holds an item in the list. This isn’t selecting an item, it’s just tapping and holding that item. I want the context menu to change based on the current context—that is, the underlying data for whichever item I’m pressing.
To do this, I had to change the ListBox XAML to add an eventHandler for the Hold event, as follows:
<ListBox ItemsSource="{Binding}" Hold="ListBox_Hold">
How do I figure out what the data context is? This magic happens in the Hold event handler:
private void ListBox_Hold(object sender,
GestureEventArgs e)
{
SimpleViewModel data =
((FrameworkElement)e.OriginalSource).DataContext
as SimpleViewModel;
MessageBox.Show(data.MyText);
}
So, what’s happening here? Although ListBox is the control handling the Hold event, the event is actually fired on one of the UI elements of ListBoxItem, such as the Image or the TextBlock. Because I didn’t set a DataContext on each ListBoxItem, they inherit the DataContext from their parent UI element. So, by casting OriginalSource to FrameworkElement, we can access the DataContext, which is the underlying data object—or the SimpleViewModel in this case.
You Can’t Use Multicast on a Cellular Connection
UDP Multicast isn’t supported on a cellular connection. What does that mean? BeginJoinGroup begins the join operation to a Multicast group. If you call BeginJoinGroup when the phone is only connected to a cellular network, a SocketException will occur. You can catch this as shown in Figure 4.
Figure 4 Attempting to Join a UDP Multicast Group
try
{
// Make a request to join the group.
_client.BeginJoinGroup(
result =>
{
// Complete the join
_client.EndJoinGroup(result);
// Send or Receive on the multicast group
}, null);
}
catch (SocketException socketException)
{
if (socketException.SocketErrorCode
== SocketError.NetworkDown)
{
MessageBox.Show("UDP Multicast works only over WiFi/Ethernet");
}
}
Obviously, in the real world you would handle this exception with a little more panache than displaying a MessageBox. You improve the situation by using the SetNetworkRequirement extension method. Then at least you’re declaring up front that you know what you’re doing, but you’ll still get the NetworkDown exception in this case, too. You’ll find all the details about networking on Windows Phone at wpdev.ms/windowsphonenetworking.
Keep Your App Launch Time in Check
My first app was a paid app and I gave it a Trial mode so the user could try the key features before splashing out on the app. I was pleased with the number of downloads, and the conversion rate from Trial to paid was healthy, too. All I needed was big volume! While waiting for the floodgates to open, I thought I’d try shipping a free app, with some ads placed in it, to see what kind of return I would get on my investment.
The ad-based revenue model is a popular way for developers to monetize their development efforts. I initially hesitated to go this route in my own app development, mainly because the first app I wrote was targeted at young children, and I didn’t believe in ads in an app for that target demographic. They get enough of that from TV. My other reason for shying away from this model was my ignorance. I didn’t know how to advertise and I didn’t know how to implement advertising in an app. On looking back, I had no reason to be so timid. Making your app ad-based and learning about impressions, effective cost per thousand impressions (eCPMs) and ad units is a breeze, and the Microsoft Advertising SDK for Windows Phone is a shining example of just how accessible this world of advertising can be.
The Microsoft Ad Control for Windows Phone is built right into the Windows Phone SDK, and you can be up and running with it in no time. All you need to do is create an app id and ad unit in Pub Center, drop the Ad Control control onto your page, set the ApplicationId and AdUnitId properties in that control to the values you got from Pub Center, and you’re done. You can find more information at wpdev.ms/adsdk. Although the Advertising SDK ships with the Windows Phone SDK, be sure to check for Advertising SDK updates at wpdev.ms/adsdkupdates because they can ship independently.
However, with all this power comes responsibility. You want to make sure that, as with all UI elements, loading the Ad Control doesn’t impact the responsiveness of your app, particularly at app launch time. When someone taps your app tile, you want them in your app before they change their minds.
The Marketplace Test Kit, built right into the Visual Studio IDE, is a simple way to test your application’s launch time. Using the monitored tests in the kit, you can start your application on your phone, play with it and then close it down. The kit then analyzes the metrics it gathered while your app was running and gives you feedback on launch time, peak memory usage and more. Failing any of these tests when you submit your app would prevent it from passing certification. The Technical Certification Requirements for Windows Phone include requirements for application launch time. At the time of this writing, the requirement was that an application must render the first screen within five seconds after launch. To see the most recent list of certification requirements, see wpdev.ms/techcert.
Always monitor your launch time and make sure it doesn’t reach the limit of five seconds. Adding an Ad Control to your app’s main page can also impact launch time. This is true for anything you do on that page, but the Ad Control is nice way to illustrate this point. Fortunately, there’s a way to avoid a control such as the Ad Control impacting your launch time, as long as you don’t mind a slight delay in the first ad appearing on the page. I don’t mind, because responsiveness and a good user experience of my app trump those few extra ad impressions any day. The pattern I use is to delay loading the Ad Control. While I’m at it, I also make my code more robust by handling errors that may arise if an ad fails to load due to network issues, a lack of ads to actually load and so on.
Instead of placing the Ad Control into my page in XAML, I instead put a Grid on my page that will eventually contain the Ad Control:
<!—Ad Control is added dynamically in codebehind-->
<Grid Grid.Row="0" Height="90" x:Name="AdGrid"/>
In my codebehind for the page, I then add a member variable to the class for the Ad Control (don’t forget to add a reference to Microsoft.Advertising.Mobile.UI in your project):
// Ad Control to add dynamically to AdGrid
private AdControl adControl = null;
In the page constructor I hook up the loaded event as follows:
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
In the MainPage_Loaded method I add the following:
// Load Ad Control in Loaded event, so it won't count against the app's launch time
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
LoadAdControl();
}
The LoadAdControl method will be used to add a new Ad Control to the AdGrid that I created in XAML, as shown in Figure 5.
Figure 5 Adding an Ad Control to a Grid Programmatically
private void LoadAdControl(){
// Create Ad Control if it doesn't exist already
if (adControl == null || !AdGrid.Children.Contains(adControl))
{
adControl = new AdControl(APPID, ADUNITID, true)
{
Width = 480,
Height = 80,
VerticalAlignment = System.Windows.VerticalAlignment.Top
};
// Hook up some interesting events
adControl.AdRefreshed += new EventHandler(adControl_AdRefreshed);
adControl.ErrorOccurred +=
new EventHandler<Microsoft.Advertising.AdErrorEventArgs>(
adControl_ErrorOccurred);
// Add it to the AdGrid
AdGrid.Children.Add(adControl);
}
}
Note that the APPID and ADUNITID in Figure 5 should be replaced by the ids that you get when you create an ad unit on pub center.
You’ll notice that I also hooked up the AdRefreshed and ErrorOccurred events in this method. It’s a good practice to have something in place for handling these events. Errors can occur when retrieving ads over the network, and it’s important that you handle these cases. In my case, I simply want to display a piece of text, such as the name of my app. Others have been more adventurous and actually filled this spot with ads for their other Windows Phone Marketplace apps or used ads from an alternate provider. Figure 6 shows how I handle an Ad Control error.
Figure 6 Handling an Ad Control Error
// If there's an error, display some text
void adControl_ErrorOccurred(object sender,
Microsoft.Advertising.AdErrorEventArgs e)
{
AdControl ad = (AdControl)sender;
Dispatcher.BeginInvoke(() =>
{
// Hide the Ad Control
ad.Visibility = System.Windows.Visibility.Collapsed;
// Place something in its place. I chose to add a TextBlock
// containing the name of my app. You could instead instantiate
// an ad control from another provider, show static ads for your other
// apps or do nothing.
if (tbBrand == null)
{
tbBrand = new TextBlock()
{
Text = "My Application",
Foreground = (Brush)Resources["PhoneForegroundBrush"],
FontSize = (double)Resources["PhoneFontSizeMedium"],
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = System.Windows.VerticalAlignment.Center,
Margin = new Thickness(10)
};
}
If (!AdGrid.Children.Contains(tbBrand))
{
AdGrid.Children.Add(tbBrand);
}
tbBrand.Visibility = System.Windows.Visibility.Visible;
});
}
I added the AdRefreshed handler because I want to make sure the Ad Control is visible once it’s up and running again, following an error (see Figure 7).
Figure 7 Adding an AdRefreshed Handler
// Make sure the Ad Control is visible if the ad was refreshed
void adControl_AdRefreshed(object sender, EventArgs e)
{
AdControl ad = (AdControl)sender;
Dispatcher.BeginInvoke(() =>
{
ad.Visibility = System.Windows.Visibility.Visible;
if (tbBrand != null)
{
tbBrand.Visibility = System.Windows.Visibility.Collapsed;
}
});
}
This is good practice and makes my Ad Control usage more robust, but what impact does it have on launch time?
To demonstrate, I created a new project using File | New Project | Windows Phone Application. I first dropped an Ad Control directly into XAML. I then ran the Marketplace Test Kit, having deployed my app to my phone. I ran my tiny app multiple times and looked at the launch times. It averaged out at 2.7 seconds. This is still an acceptable launch time. Next, I applied the delay load pattern I outlined previously. I deployed my device again through Marketplace Test Kit and tested it the same number of times as I had done in the first test case. The average launch time was now only 1.9 seconds. Say no more.
Use IsolatedStorageSettings to Store Simple Settings
I’m officially addicted to IsolatedStorageSettings as my preferred way to store application settings. Given its key-value pair structure, it should only be used for the simplest of settings. I typically use it to store things such as whether the user has enabled or disabled sound in my app, the number of objects to display per page, the last time data was updated and more. Simple data should go in here, and it’s really easy to use. Check out the procedure in the MSDN Library docs that shows you how to create your very own Settings Page at wpdev.ms/settingspage. I promise, once you try this, you’ll be hooked.
When it comes to storing large amounts of data, you can’t avoid writing to files in isolated storage. Keeping app launch time in mind, you should adhere to the guidelines to load data asynchronously (BackgroundWorker, an async Web call or whatever your async call du jour may be). I also adopt a pattern of only saving deltas to disk, and I do so more or less as soon as they occur. For more information on state on Windows Phone, check out bit.ly/oR96Ux.
Don’t Use FileMode.Append on a File that Doesn’t Exist
As mentioned previously, great care should be taken to optimize your data transfers to and from isolated storage. The responsiveness of your app should be paramount—otherwise you’ll find the application downloads you fought so hard to get being uninstalled worldwide. In one of my creations I was able to store and load my data pretty snappily until I discovered that what went in wasn’t necessarily coming out. I was losing data somewhere! I didn’t spot it for a number of reasons. One reason was because of a long-lost Debug.WriteLine statement I had dropped into a catch block late one night. So there I was, thinking I was writing everything to isolated storage, when all the time the Error List was going bonkers. I thought I was appending data to a text file, but the Error List was delivering the message:
Operation not permitted on IsolatedStorageFileStream.
I’m not a big fan of displaying the wrong code in an article like this. I learned that habit from my math teacher back in high school, at a time when legwarmers, “Family Ties” and the Commodore 64 were all the rage. Yes, he would never show the wrong answer to a problem for fear it would stick. (Tangent: Strangely enough my teacher’s initials were D.O.S.—I don’t think I got the reference at the time!) So, here’s what I should have been doing.
Never attempt to call FileMode.Append on a nonexistent file. Create it, write to it and then append with subsequent transfers, as shown in Figure 8.
Figure 8 Writing to a File in Isolated Storage
if (!_store.FileExists("mydata.data"))
{
// Open for writing
using (var textFile =
_store.OpenFile("mydata.data", FileMode.CreateNew))
{
WriteRecordsToFile(textFile, dataString);
}
}
else
{
using (var textFile = _store.OpenFile("mydata.data",
FileMode.Append))
{
WriteRecordsToFile(textFile,dataString);
}
}
private void WriteRecordsToFile(IsolatedStorageFileStream textFile,
string data)
{
using (var writer = new StreamWriter(textFile))
{
writer.WriteLine(data);
}
writer.Flush();
writer.Close()
}
Use an Isolated Storage Explorer to Test Storage in Your App
The tooling space for Windows Phone is gathering momentum, and I’m thrilled to see high-quality tools filling gaps in the Microsoft offering. Mind you, these gaps aren’t chasms, the size of which you’d find on other toolsets for other mobile platforms. No, these are gaps that Microsoft simply hasn’t gotten around to filling yet. For example, Microsoft made headway by delivering the Isolated Storage Explorer command-line tool. Out in the wild you’ll find wrappers around this that make life so much easier. One such tool is the Windows Phone 7 Isolated Storage Explorer found on CodePlex (wp7explorer.codeplex.com). Browse around and find the one that suits you, or stick with the command line. But get involved with isolated storage exploration and use the tools to—for example—drop test data into your app’s isolated storage. It’s so much easier than finding these bugs when you’ve already shipped.
Use the Marketplace Test Kit
The Marketplace Test Kit is a great tool for keeping you in check while you work on your app. Run directly from within the Visual Studio IDE, it offers sets of tests that do useful things such as verify that you have the right iconography for your app, detect the actual phone capabilities your app is using, report what your app launch time is like and so on. There are even manual tests that give you a good indication of what’s been looked at during the app certification process. This is one of those features that every developer thinks about knocking out some night and posting on CodePlex for everyone to use. Microsoft got here first, which hopefully means there’s a roadmap for this kit that will build on this foundation and make it rock even further down the road.
Apart from one failure due to a misunderstanding about how the Windows Phone Marketplace test team would test one of my apps, I’ve never failed application certification. I could chalk this up to my own genius or diligence, but really I got by with a little help from my Marketplace Test Kit friend, and you should too.
If I had one gripe—and this is something for you to watch out for—it’s that the monitored tests fail pretty silently when you try to run them on the emulator. This failure simply takes you from Figure 9 to Figure 10 when you click “Start Application” and have the emulator selected as the target device.
.jpg)
Figure 9 Before Running Monitored Tests
.jpg)
Figure 10 A Monitored Test Failure
I think it informs you all too subtly that you can only run these monitored tests on a real phone. A little more in-your-face notification on this one would be a nice improvement, in my opinion. You can read more about the Marketplace Test Kit at wpdev.ms/toHcRb.
Test Your Media App with WPConnect
When I began tinkering with media apps in general, I would hack for a couple of hours and then give up as soon as my first test run annoyed me by telling me I couldn’t view the media library while my phone was connected to my PC. So, away I’d go into a more comfortable area of the platform, just shaking my head and wondering at how patient those media app developers really were. That was, until I found the Windows Phone Connect Tool (WPConnect). Yes, it took me a while, but when I did come across this nugget in the docs it made me smile ear to ear. What is it? Simply put, it enables you to debug media apps while connected to your PC. The instructions are very straightforward, so go check them out at wpdev.ms/wpconnect.
Test Responsiveness on 256MB Devices
Phones with 256MB of RAM exist. Because of their reduced memory capacity, you should monitor your own app’s memory footprint and decide what to do if your app were to land on one of these phones. You could decide to detect whether a host device was a 256MB device and turn the bells and whistles down on your app. You could also come to the conclusion that your app simply wasn’t built for these devices, and you’d rather take the hit of your app not showing up on these than take the risk of one-star ratings showing up because of the perceived performance problems of your app.
With the Windows Phone SDK 7.1.1, Microsoft helps you make informed decisions in this space. You can check what’s known as the host device’s ApplicationWorkingSetLimit to find out whether the phone is of the 256MB class. You can even opt out of making your app visible in the Windows Phone Marketplace to these devices. Check out the details at wpdev.ms/256devices.
In this article I took you on a journey through a broad set of issues I discovered while building apps. I also highlighted some tools that have helped me greatly. It should be clear that application responsiveness is a core value to uphold when building apps. I hope you found something that will help you on your Windows Phone app-building adventure. Windows Phone is a powerful platform and I still love to build for it and write for it. I enjoy browsing the Windows Phone Marketplace every day for all the new goodness that creative folks like you are delivering in a steady stream. If I haven’t tried your app yet, I look forward to doing so.
Andrew Byrne is a senior programming writer on the Windows Phone team. His knowledge and passion for software come from his 21 years in the software development industry working for many multinational organizations, as well as his own startup.
Thanks to the following technical experts for reviewing this article: Kim Cameron, Mark Hopkins, Robert Lyon, Nitya Ravi, Cheryl Simmons and Matt Stroshane
Comments (2)