Archive for the ‘Windows Phone 7’ Category

Windows Phone navigation workarounds

Tuesday, April 26th, 2011

Silverlight for Windows Phone has adopted a navigation mechanism reminiscent of a web browser. Each page is authored as a separate resource. The application issues commands to a NavigationService to move from one page to the next. The phone even has a hardware back button. This choice of web-like navigation causes no end of difficulties to application developers.

Navigation in Windows Phone starts with a URI. But rather than identifying an HTML resource, this URI identifies a XAML resource. The application doesn’t create pages; it just identifies them. The runtime creates each page based on the given URI. This causes three problems.

  • Parameters
  • View model location
  • Back-stack modification

Parameters

The URI must contain all of the information required to construct the page. For the main page of the application, this is usually fine. Just bring up the XAML representation of the main menu. But subsequent pages often drill down into detail. If the main page displays a list of articles, and we want to bring up a specific article when the user taps, then we have to identify the article in the URI. All of the article links target the same XAML resource. It’s the data that differs.

To differentiate the links, many developers take the typical web-based approach. They add query string parameters to the URL. Chris Koenig gives an excellent example of parameter-passing. The Silverlight NavigationContext object exposes these parameters via the QueryString property, specifically for this purpose.

The problem with query string parameters is that they requires an application to drop down to string-based identification, rather than rely upon direct object access. To generate the query string, an application has to extract an ID from an object. To consume the query string, it has to look that ID up in an object model. It usually ends up getting right back to the object that it started with. Wouldn’t it be easier to use that object reference directly?

The web was designed for stateless operation. Each request is isolated from the ones that came before. No objects are assumed to be kept on the server for the user’s next request. Each request must contain enough information to reload those objects and generate the required page. This is what allows the web to scale.

But the phone isn’t like a stateless web server. It has rich object state. It exists to serve just one user. It can afford to load objects into memory, and use references among those objects as that user navigates through the application. To drop down to a string only to come back to the same object is not taking full advantage of the platform. What’s good for the web is not necessarily good for the phone.

View model location

The second problem is pairing a view model with a view. Since navigation is based on identifying the view, Windows Phone forces you into a view-first approach. The view is left with the responsibility of finding its view model. To complicate matters, the view must sometimes pass its query string parameters to the view model. The view model uses the parameters to find a reference to the model object.

A common solution to this problem is the view model locator pattern. A view model locator is a single-instance object registered as an application resource. Each view binds its DataContext to a property of this resource, which is an instance of a view model.

    <Application.Resources>
        <vm:ViewModelLocator x:Key="Locator"/>
    </Application.Resources>

public class ViewModelLocator
{
    private readonly MainViewModel _main;

    public ViewModelLocator()
    {
        _main = new MainViewModel();
    }

    public MainViewModel Main
    {
        get { return _main; }
    }
}
<UserControl x:Class="ToDoList.MainPage"
    ...
    DataContext="{Binding Main, Source={StaticResource Locator}}">

The biggest problem with the view model locator pattern is that it treats view models as singletons. Since the view model locator is an application resource, there is only one instance serving all of the view models. And since each view data binds to a property, the view model locator can only have one instance of each view model. It is not a view model factory. There is no way to pass query string parameters to a method in order to construct an instance of a view model. You have to write code in the view to pass the parameters to the view model in its DataContext. That one and only instance of the view model must be able to change its behavior to point to a different model instance every time the user navigates.

Back-stack modification

The third problem is removing pages from the back-stack. Some interactions have intermediate steps: the user logs in before seeing content, or a wizard guides a user through a multi-page process to achieve a result. When the user has completed these intermediate steps, they don’t want to return to them. If they have completed the steps prior to reaching the main menu (as in the login example), then they want to exit the application when they hit back from the main menu. They don’t want to see the login screen again. Or if the intermediate steps occur after the main page (as in the wizard example), then they want to return to the main page when they are finished. These intermediate pages should be popped off the stack.

Unfortunately, the Windows Phone OS is completely in charge of the back-stack. It keeps track of each URI that the user visits, and returns to past URIs when they hit the back button. There is no API for removing a URI from the stack. We are left with a couple of hacks. To skip intermediate pages at the beginning of the stack (login), we can exit the application prematurely by throwing an unhandled exception. To skip pages later in the stack (wizard), we can bounce to each intermediate page in the main page’s OnNavigatedTo. Neither solution is particularly appealing.

Conclusion

The Windows Phone navigation system is based on the web metaphor. Unfortunately, the web is the wrong model for a single-user device. The decision to model navigation in this way has caused phone applications to act like web pages. The intent was to bring a well-understood interaction language to the phone. But in practice, it just forced application developers to work around the shortcomings of a misappropriated model.

This decision leads developers into a view-first approach, rather than beginning with the view model. It forces developers to convert object references to strings, only to convert them back into object references. And it requires ugly hacks to produce even the simplest of interactions. If you write Windows Phone apps, prepare to use these workarounds.

Change the Windows Phone 7 background image for the light or dark theme

Sunday, November 28th, 2010

Windows Phone 7 applications have a better sense of presence when they have their own background image. By default, the background is white for the light theme or black for the dark theme. Using only XAML markup, we can choose either a light or dark background image instead.

Create two 480x800 images, one dark and one light. The easiest way to do this is take a photograph, scale and crop it to the right size, then adjust the contrast and brightness. For both images, you want a low contrast. For the dark image, turn down the brightness. For the light one, turn it up.

background2d  background2l

Save both as png, and add them to an Images folder in your application. Set “Build Action” to “Resource” and “Copy to Output Directory” to “Do not copy”.

image

Create two Image controls in your page’s LayoutRoot. By default, the LayoutRoot is a Grid offset 30 pixels from the top. Set the margins to 0,–30,0,0 so that the image fills the entire page. The grid has two rows by default, so set the RowSpan to 2.

image  image

Set the Source of one Image to the dark png. Set Stretch to UniformToFill. Then bind its visibility to PhoneDarkThemeVisibility. Set the Source and Stretch of the other Image, and bind its Visibility to PhoneLightThemeVisibility.

image  image

Now using the Blend Device window, switch between dark and light to make sure the result is what you expect.

image  image

This all works directly in XAML without editing code. You can use this technique for most visual aspects. But if you need to detect which theme is active in code, Magnus Johansson has instructions at Insomniac Geek.

What makes a good phone app?

Wednesday, November 24th, 2010

My head-to-head Reversi app is almost ready for the Windows Phone marketplace. I finally have it on my phone, and I am testing it rigorously.

I’ve owned an iPod Touch, an Android G1, and now a Windows Phone 7. Based on that experience and the reactions from my testers, here’s what I think makes a good phone app.

Quality

It’s hard to quantify but easy to recognize. If an app feels hacked together, I don’t want to use it. It doesn’t matter if it’s only 99 cents, or even if its free. I don’t want to reward an app developer who does not obviously care.

Purposefulness

We are used to doing work with our computers. We are actively involved in every step of the process -- clicking on menus, filling in forms, verifying results.

But a smart phone is an agent. I want to tell it what to do, and then let it do the work. If I have to go through all the steps myself, then the phone is the wrong platform.

Disconnected operation

Smart phones have many radios, so connectivity shouldn’t be a problem, right? Not so. These radios don’t work well indoors, on the road, or in large crowds. If an app needs a connection to work properly, it may as well be a web page.

Smart phones have a lot of storage. I expect my apps to use it. When I pull up IMDB, I expect that the list of movies on the front page will be similar to the ones I saw yesterday. It shouldn’t have to hit the network. Refresh the list in the background, and let me work with the information that I already have.

Discoverability

There is no instruction manual that comes with an app. A user needs to figure it out based only on the clues that it gives them.

An app should know the very next thing that the user wants to do. It should put that plainly on the screen with a meaningful icon, a natural gesture, or as a last resort text.

An app should also anticipate what the user might try. For example, if you have a Windows Phone, press the home button on the lock screen. The screen will bounce to indicate that there is something below it. The user swipes up to see what it is, thereby learning the correct unlock gesture.

Fit the platform

An iPhone app should have an icon with rounded corners and reflections. It should use the standard page animations, title, and back button showing the title of the prior page. A Windows Phone app, on the other hand, should have a flat square icon, and no on-screen back button. Pivot and panorama work on the Windows Phone. Don’t try them on Android.

The mistakes I’ve made

My Reversi game made several of these mistakes.

With respect to quality, it had some serious bugs when I started testing with other people. I had forgiven these bugs, since I know the workarounds. But others did not. Get other people to test your app.

Another quality issue was responsiveness. Initially it took a full second for the game board to appear. This confused people. So I made the game board a separate XAML element. It still takes a second for the pieces to initially appear, but at least the game board is there. It feels like a more solid app.

A final quality issue was that it was far too easy to accidentally hit the home button. I put a piece at the bottom of the board to indicate that it is your move. You drag this piece onto the board to play. Unfortunately, the piece appeared just above the home button on the physical device. My wife went to the home screen once for every 2 or 3 moves that she made.

To provide purposefulness, I give you additional information about the game. I show you not only the pieces that you are about to capture, but also the moves that you are allowing your opponent. Reversi, as it turns out, is more about moves than it is pieces. But I didn’t take purposefulness far enough. I also needed to continue showing those moves after the piece is dropped, so your opponent can easily see what’s available to him. It raises the level of abstraction.

For discoverability, I show a pulsing glow behind the new piece. People understand that this means “drag me”. But I didn’t anticipate what the user would try. Most people just try to touch the square where they want to move. Since that’s what they try, I now honor that gesture.

Disconnected operation is the one place where I focused my attention. Reversi is the inaugural Correspondence application, and Correspondence was designed for disconnected operation. Still, there were a few things that I had to fix. Most significantly, if you make a move while disconnected, it is queued on the device. Once you are connected again, you have to tell the application to send the move. The way you do that in Reversi is to return to the main screen. This is naturally what the user would do, since they can’t do anything else on the game screen.

Finally, to fit the Windows Phone platform, Reversi uses very simple flat graphics. However, the platform also calls for background images. The application feels incomplete without photography. So I’m taking pictures of real Reversi pieces to give the user the feeling of presence.

The app will be in the Marketplace after another week or two of testing.

Getting the users anonymous Live ID on Windows Phone 7

Wednesday, October 6th, 2010

While working on a Reversi game for Windows Phone 7, I needed a good way to identify the user. The idea is to let users challenge their friends by entering their name. On the one hand, anybody with a Windows Phone 7 should be able to play. On the other hand, I don’t want to be in the identity business. So the obvious choice is to use Live ID. You have to use Live ID to activate the phone, so every one of my users would have one.

A quick search reveals that there is an API to obtain the user’s anonymous Live ID. Unfortunately, more vigorous searching does not reveal what an “anonymous” Live ID is. If it’s an ID, how can it be anonymous?

Here is the sample code from the MSDN site:

string anid = UserExtendedProperties.GetValue("ANID") as string;
string anonymousUserId = anid.Substring(2, 32);

This code raises a number of questions for me:

  • What is an anonymous Live ID?
  • Is the length always 32?
  • What are those first two characters you’re skipping?

To find some answers, I ran this code in the emulator. If you’ve tried it, you know that it throws a NullReferenceException. UserExtendedProperties.GetValue(“ANID”) returns null. So you’re stuck. Unless you know someone with a WP7 device.

And I do.

Chris Koenig was kind enough to run the sample code on an actual phone. Here is what the API returns (I’ve anonymized his anonymous ID by scrambling the characters).

? anid

"A=2E434B328BC68118DB640915FFFFFFFF&E=a45&W=1"

? anonymousUserId

"2E434B328BC68118DB640915FFFFFFFF"

So now we have an answer to the questions. The anonymous Live ID is a GUID with no punctuation. It is unique to the user without telling an outside observer with no other information who it is. Yes, it is always 32 characters (since it’s a GUID). And the first two characters are the name half of a name/value pair.

Not wanting to assume that the character position will never change, I modified the parse code. Here’s a parser that will extract the value of “A” in an ampersand-delimited list of name/value pairs.

public static string Parse(string anid)
{
    string[] parts = anid.Split('&');
    IEnumerable<string[]> pairs = parts.Select(part => part.Split('='));
    string id = pairs
        .Where(pair => pair.Length == 2 && pair[0] == "A")
        .Select(pair => pair[1])
        .FirstOrDefault();
    return id;
}

The plan is to prompt the user for an alias the first time they use the app. I’ll send the anonymous ID and the alias to my server, which will ensure that the alias is not already taken and associate the two. Then other users can challenge them using that alias. I get the best of both worlds. I can rely upon Microsoft to be the identity provider, and I can let my users challenge each other with their own names.

Thanks, Chris!