Having written a lot of WPF code for Visual Studio 2010 I am pretty comfortable with WPF programming, but I’m ashamed to say I was really feeling rather lazy about learning yet another UI framework – Silverlight. I played with Silverlight 3.0 a while back and was somewhat turned off by the lack of features.
But many of my friends at Microsoft dived in and shipped their own Windows Phone 7 apps, beating me to it. Ouch, now I felt shame that I’d fallen behind in the latest technology – so with renewed competitive energy and an idea for an app that will help my kids, how could I resist any longer?
So three weeks later, in my spare time after work, with lots of help from my friends, especially Andrew Byrne, and my guinea pigs, oops, I mean kids, my app is published. It was a fun experience, and now I’m hooked!
The app is a simple tool to help kids memorize their multiplication tables. Here’s some screen shots.
It’s a brain dead simple application right? I first prototyped it using WPF since I was familiar with that and I tried to copy the same page model that the phone has and it worked out nicely. I had something up and running in about a day since I knew what I was doing.
Then I installed the WP7 SDK, played with a few samples, like the UnitConverterStarterKit, and got familiar with the Emulator. I have to say right away that the Emulator was amazing. I built the entire thing and published it before I even owned a phone and was delighted to see it running on the phone exactly as the emulator predicted, almost. There’s one performance issue I think to do with XmlSerializer or LicenseInformation which causes the app to launch quite a bit more slowly than I’d like. I haven’t debugged it yet. But everything else works flawlessly on the phone exactly as it did on the emulator, so hat’s off to the emulator team J
So how does the app work? The app has 4 pages, and a handy page base class, some user controls like the NumberPad, a BarGraph, and a simple view model that these UI components bind to in various ways.
The Data object serializes itself to and from isolated storage, and contains a list of “Sessions”, each session contains a “Cell” containing a times table problem presented to the user, what answers they tried and the time taken to answer them. This data is graphed on the MainPage as you can see in the screen shot.
Of course, I wanted to make it polished, so I added a nice little animation to the graph, the bars grow from the bottom of the screen when you first load the app. Much to my surprise Silverlight doesn’t even have a BeginAnimation method on UIElements, but extension methods are our friend. We can make our favorite parts of WPF look like they are there on Silverlight, so it wasn’t a problem.
public static class AnimationExtensions
public static Storyboard BeginAnimation(this DependencyObject element, Timeline timeline, DependencyProperty propertyBeingAnimated)
Storyboard.SetTargetProperty(timeline, new PropertyPath(propertyBeingAnimated));
Storyboard story = new Storyboard();
I ended up creating a bunch of other helpers which I put in my helpers namespace. Including standard brushes, a HlsColor class for manipulating colors, the isolated storage generic which I got from a sample app, and some of my favorite things that I missed from the System namespace.
While it’s annoying to have to do this, I understand the need to keep the framework small and fast. In fact, I noticed that the XAML designer in Visual Studio 2010 was quite snappy compared to the full WPF designer which certainly makes development fun. So what is my app using from the .NET framework anyway?
System.Core.dll is the usual LINQ stuff. System.Xml was the XmlSerializer used by IsolatedStorage. System.dll and mscorlib are the usual things although I was surprised you couldn’t even get the System.Reflection.Assembly.Version property, but the forum has a post on parsing the full name to get the version that way. Yet another helper method (YAHM).
My dependencies on the Microsoft.Phone assembly are more interesting:
The cool thing here is how few types you really need to learn in order to get a phone app up and running. Funny that System.Diagnostics lives in here. The LicenseInformation is needed to support my app’s “Trial version” feature.
Then from System.Windows, I use a bunch of standard Silverlight controls, media primitives, animation primitives, a couple shapes, and core UI framework base classes, plus a few extras like DispatcherTimer, NavigationEvents and some Data binding stuff.
Click here to see the full dependency graph.
Now I really love the Zune HD calculator number pad because it does a nice slow fade back animation when you lift your finger off the key. This allows you to punch in a whole slew of numbers quickly and the residual trail effect the animation leaves on your retina re-enforces in your brain that you hit the right sequence of numbers. This is exactly what I needed for the hard core Times Tabler who is racing the clock.
So I whipped out the XAML designer and started playing with the Visual State Manager. But I hit a wall and couldn’t figure out how to add an animation from the “Pressed” state back to the “Normal” state. I added it under the “Normal” state but nothing happens. I searched around but nothing. So I gave up and wrote it in code. See NumberPadButton in the code. This class simulates the Zune HD calculator button. The NumberPad then is simply a big arrangement of these buttons in XAML, plus some simple event handling to pump these numbers into a target text block and handle clear and backspace. Why didn’t I just use the built in Soft Input Panel (SIP)? I needed something that would allow my users to devote all their brain power to answering the problems instead of trying to be accurate with their fingers.
The Zune has a nice white glow effect around the button when you click on it, but shock horror, Silverlight has no Effects? WhatjQuery1520600429414305836_1341634595521? I thought the rage these days was hardware accelerated graphics?! So I simulated the glow with some different shades of rounded rectangles. Yuk, but it works, and quite honestly I doubt anyone would notice the difference.
The main part of the app, of course, is to test the user on the times table, so the usual state machinery exists here, for running, pausing, dealing with errors, and I also have a demo mode, so that added some more complexity. Overall developing my app logic and debugging it was a breeze, the tools worked flawlessly.
Now my Options page was more of a challenge than I was expecting. Initially I just stuck some text boxes on there and hoped the SIP with InputScope=Digits would do the trick, but my users hated that for several reasons. One reason was that I couldn’t figure out how to get just the digits 0 through 9 and absolutely nothing else. The other is that the keys are smaller and harder to press than my huge number pad.
They all said, hey, you have this great number pad why can’t I use it there also?
So the work I did to make the NumberPad a reusable control paid off and it wasn’t too hard to wire it up. The only trick was that for the bottom option “Goal” the number pad didn’t fit on screen, and the framework didn’t auto-scroll it into view. I had to add a ScrollViewer around all this, and then when the number pad came up, figure out where the button was, add the height of the number pad (400) then set the vertical offset on the scroller to make sure it was in view. But then “ScrollIntoView” was missing. So I tried to just set the vertical offset on the scroller, but this didn’t work until I inserted a call to UpdateLayout. A bit tricky, but it works.
void EditText(ToggleButton button, string prompt)
if (button.IsChecked == true)
oldValue = button.Content;
ButtonOk.IsEnabled = true;
KeypadOverlay.Visibility = System.Windows.Visibility.Visible;
Keypad.Target = NewValue;
Keypad.NextKeyClears = true;
selected = button;
Point bottomLeft = button.TransformToVisual(LayoutRoot).Transform(new Point(0, button.ActualHeight));
double available = Scroller.ActualHeight;
OverlayGrid.Height = bottomLeft.Y + 400;
Scroller.ScrollToVerticalOffset(OverlayGrid.Height – available);
Then I had to test to make sure the code works on the landscape orientation. I decided to go with a ToggleButton for the actual option instead of a Button so the user gets feedback on which option they are entering, and can click the toggle button again to hide the number pad, so you can find and edit the next option, which is something I struggled with a bit using the standard text box and the SIP.
It took me a while to figure out how to do my page state persistence properly, initially it was spread all over, then after reading around some more I found a good practice which is to put all this in a base class that knows about my View Model so all my pages work consistently. This is TimesTablePage, and it handles the OnNavigatingFrom, OnNavigatedTo and OnBackKeyPress events, loading the data, then providing a virtual method to tell the other pages when the data has changed (OnDataChanged).
One of my guinea pigs complained that when he hit the Start button, clicked on Apps, and launched my program again, all the state was lost. Sure enough in this case my OnNavigatedTo method is not given any of the state I persisted earlier in OnNavigatingFrom. It’s as if the phone really doesn’t want to restore state in that case. But my user didn’t like that, and felt it was inconsistent, so I’ll have to think about this some more. What I found myself doing is storing less and less in the page State dictionary, and more and more in my “Data” object which is serialized back to isolated storage. I suspect by the time I’m done everything will be in there at which point one wonders whether we really need the PhoneApplicationPage.State dictionary.
I put off thinking about this until the end. I saw how the “cool” built in phone apps rotated smoothly as the phone was flipped, my head would spin thinking about all the UI element animations I’d have to do. Then Andy and Rob set me straight – all I needed to do was add the following attribute to all my pages:
And boom, my pages auto-layout in each orientation, and the phone does the magic of rotating the coordinate system – in other words, my app always sees the top left corner is what the user thinks is the top left corner. The following start page just worked with no changes. Cool!
But it didn’t do any nice smooth rotation of things and my second page wasn’t so easy:
This magic didn’t come out of the box of course. Notice that the “problem” flips from a horizontal line layout to a vertical stacking layout, and the keypad flips for left or right handed users in the 2 landscape layouts.
So I developed each page manually in the XAML designer. Thank heavens the designer has the ‘Switch to Landscape’ option…
Then I painstakingly copied all the Grid layout information to code so I could switch things around dynamically by setting different Grid.SetColumn, Grid.SetRow, etc.
Now, I probably could have done it all in XAML, but I didn’t want to.
Of course, I’m still mystified as to how to get things to smoothly rotate when the orientation changes, and perhaps if I did something in the Visual State Manager I’d get that for free, but so far no luck, probably have more reading to do. If you know the answer please comment on my blog!
So that’s really it, publishing was a breeze, had to get my icons right, I was confused by the name ‘Background.png’, it was blank so I thought it was some sort of background mask, but it’s actually the icon they show if the app is pinned to the start page. You can actually test the icon in the emulator if you hold the mouse down on your application, the context menu pops up with the “pin to start” option. Once I figured that out and removed the emulator chrome from my screen shots the app went through the system. One gotcha there is don’t publish your app for a price, then try and remove the price later and make it free. The system won’t let you. So think carefully about pricing before you publish.