The blog of dlaa.me

You Spin Me Round (Like a Record) [Easily animate orientation changes for any Windows Phone application with this handy source code]

As computing devices have become more powerful, the trend has been toward more "fluid" user interfaces - interaction models that flow smoothly from one state to another. This differs from earlier approaches where resources were more limited and interactions tended to be Spartan and isolated from each another. Some of the motivation behind an increased focus on fluid UI is almost certainly the "wow" factor; modern interfaces can be quite attractive and fun to use! But there's also scientific research to back this up: people tend to find smooth transitions less disruptive to their workflow than abrupt ones - and the animation itself can help tie the "before" and "after" states together by showing how one becomes the other.

With that in mind, one of the things I've wanted to do is apply this principle to enhance the user experience for Silverlight applications on the Windows Phone platform. Specifically, I wanted to make it easy for developers to animate the orientation change of an application that occurs when the phone is rotated from its default portrait orientation to landscape. This is one of those cases where a picture is worth a thousand words, so I've created a short video showing the default behavior of a Silverlight-based Windows Phone application when the device (in this case the emulator) is rotated.

 

Click the image below to view a brief H.264-encoded MP4 video of the default rotation behavior:

Video of default orientation change behavior

For people who have never used the emulator: those two buttons I click rotate the device one quarter turn clockwise or counter-clockwise. I begin by rotating counter-clockwise once, then clockwise once back to the starting orientation, then clockwise three more times to loop all the way around.

 

What I show above is what you get for free when you create a new application - and it's great the device and platform support dynamic orientation changes without any special effort! But I thought it would be cool if I could extend that just a bit in order to animate those orientation changes - again, without requiring the developer to change anything (beyond a couple of superficial name changes).

Click the image below to view a short video of the animated behavior I've created:

Video of animated orientation change behavior

As in the first video, I show the device rotating to landscape, then back, then all the way around. But this time I've included a little bit of fun at the end. :)

 

The custom rotation behavior is made possible by the AnimateOrientationChangesPage class I created which works by lying to the layout system (Now, where have I heard that before?). It's a small, self-contained class you insert into the default hierarchy and then never need to worry about again. AnimateOrientationChangesPage automatically gets involved with the necessary steps for rotation and even handles page-to-page navigation changes seamlessly.

My goal was to make it easy for Windows Phone applications to offer the kind of fluid rotation experience that's becoming common these days - and it seems like AnimateOrientationChangesPage does that. I hope you find it useful!

 

[Click here to download the AnimateOrientationChanges sample for the Windows Phone 7 platform.]

 

Notes:

  • If you download the sample, please be sure to open MainPage.xaml in the development environment before hitting F5 to run it - otherwise deployment to the emulator might fail with the message "Object reference not set to an instance of an object." because of a bug in the tools.
  • I created this sample using the public Windows Phone Developer Tools CTP - April Refresh bits, so anyone else with that toolset installed should be able to run it as-is. I also happened to have a chance to try things out on a more recent (internal) build of the tools - fortunately, things worked just as well there! However, due to a minor change to one of the platform APIs, please remove the following from the top of AnimateOrientationChangesPage.cs if you're trying to run on a more recent build:
    // Remove this #define when using more recent phone/tools builds
    #define APRIL_TOOLS
  • Unfortunately, I don't have actual phone hardware to run this code on, so I can't be sure it runs as smoothly there. While the performance under the Windows Phone emulator is great on my laptop, even a laptop has beefier hardware than something pocket-sized like a phone. That said, the implementation is relatively straightforward: the orientation change notification starts a Storyboard with a single DoubleAnimation of a custom DependencyProperty. The change handler for this property updates the values of a RotateTransform and TranslateTransform and triggers an arrange pass (via InvalidateArrange). Everything so far is part of the Silverlight Performance team's recommendations for good performance, so the bottleneck (if there is one) might turn out to be the arrange pass (note: not measure; just arrange) which is always a recursive operation. However, arrange is a fundamental operation for the Silverlight platform, so I expect it to be optimized and pretty efficient in practice. That said, if I manage to get my hands on a device for long enough to play around, I'll see how things really perform...
  • I wanted AnimateOrientationChangesPage to be as easy to use as possible. And while I couldn't go as far as enabling it with an attached property, I was able to do the next best thing: derive from PhoneApplicationPage. This means any existing Windows Phone application can be converted to use AnimateOrientationChangesPage simply by changing the base class of its pages to AnimateOrientationChangesPage. Specifically, here are the complete steps for converting the MainPage class of a newly created application:
    1. Verify the page supports multiple orientations by checking for the following code in the constructor (it's added by default, so it ought to be there):
      SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
    2. Add the AnimateOrientationChangesPage.cs code file to the project/solution.
    3. Change the base class of MainPage to AnimateOrientationChangesPage in MainPage.cs:
      using System;
      // ...
      using Delay;
      
      namespace WindowsPhoneApplication1
      {
          public partial class MainPage : AnimateOrientationChangesPage
          {
              // ...
          }
      }
    4. Make the corresponding change to MainPage.cs.xaml:
      <delay:AnimateOrientationChangesPage
          x:Class="WindowsPhoneApplication1.MainPage"
          xmlns:delay="clr-namespace:Delay"
          ...>
      
          <!-- ... -->
      
      </delay:AnimateOrientationChangesPage>
    5. Run the application and enjoy the animations!
  • These directions enable the default animation behavior which I've configured to use a 0.4 second duration and a reasonable easing function. However, if you'd like to customize things further, you can set the Duration property to a different TimeSpan and/or change the EasingFunction to the IEasingFunction implementation of your choice. Just in case, there's also an IsAnimationEnabled property that can be set to false to disable orientation change animations entirely. These properties can be set at any time; the new values take effect at the next orientation change.
  • I reserve the right to change my mind and move some of this code up into a PhoneApplicationFrame subclass if it turns out that would be helpful. :) But for right now, the current implementation seems pretty effective - I've specifically tested it with navigation scenarios and it works well there, too.
  • If you watch the video of the default behavior closely, you'll see a brief period of time right after the rotation occurs where the emulator is rotated but the screen isn't. I think this is an artifact of how the emulator works and doesn't represent something users would see on a real device. As it happens, I don't see that same glitch in the video with AnimateOrientationChangesPage enabled - but I'd apply the same caveat there, too.
  • I've deliberately not supported the PageOrientation.PortraitDown enumeration value. This is not because it would be hard to do (it's quite trivial, actually), but rather because no device I've seen (including the Windows Phone Emulator) actually supports flipping completely upside down in portrait orientation. If this design decision is a problem for your scenario, full PortraitDown support requires only three trivial case statements to implement that will be obvious from looking at the code.