Silverlight 3 Navigation: Adding transitions to the Frame control
Continuing with my recent theme of enhancing the built-in support for Navigation in Silverlight 3, I thought I’d use this post to look briefly at enhancing user experience during navigation. On the surface, the Frame control is pretty unexciting from a UX perspective – its job is really just to display pages as a result of requests to navigate (either through the browser’s address bar, responding to HyperlinkButton clicks, or direct calls to Frame.Navigate()). It has barely any UI of its own, and can usually be thought of as an enhanced ContentControl (incidentally, it is one!).
Most of the time, all the Frame is doing is displaying a Page, and this (in my opinion at least) is an appropriate presentation – the Frame shouldn’t get in the way of displaying my Pages. Where I do want the Frame to intervene now and then is during page changes, allowing me to provide rich transitions when navigating from Page to Page. The Frame control provided in the Silverlight 3 SDK doesn’t do anything for us as far as transitions go out of the box, but with a little templating magic and the Silverlight Toolkit’s TransitioningContentControl (which Jesse Liberty has a great blog post on), I think we can get the desired effect!
Let’s begin by taking a peek at the ControlTemplate for the Frame control:
<ControlTemplate TargetType="navigation:Frame"> <Border HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <ContentPresenter Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}"/> </Border> </ControlTemplate>
As you can see, there’s very little to it: a Border and a ContentPresenter – with all the appropriate TemplateBindings.
All we have to do to get transitions on the Frame control is replace that ContentPresenter with the TransitioningContentControl:
<ControlTemplate x:Key="TransitioningFrame" TargetType="navigation:Frame"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> <toolkit:TransitioningContentControl Content="{TemplateBinding Content}" Cursor="{TemplateBinding Cursor}" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Transition="<Transition Name>" /> </Border> </ControlTemplate>
The “toolkit:” xml namespace uses the following definition (since you can find the TransitioningContentControl in the System.Windows.Controls namespace and the System.Windows.Controls.Layout.Toolkit assembly): xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
To change the type of transition the Frame will use, just change the value of the Transition property on the TransitioningContentControl.
There’s not much more to it! Short, sweet, and to the point :).
Check out the live sample and source if you’re curious:
Caveats and Questions
I know you’d be disappointed if I really left it at that, so here’s the one caveat I’ve observed with this technique:
My dynamically-loaded libraries with navigation workaround will cause a few problems with this technique, since it sets the Frame’s Content property twice (once to load the DynamicPageShim, the other to load the DynamicPage itself). The result is that you will see the Frame transition to a blank page and then to the DynamicPage with your content. So here’s the question: how important is this transitioning behavior to you? There are two options for how I proceed here, neither of which are truly ideal:
- Leave DynamicPage and DynamicPageShim as-is and live with the slight wonkiness during transitions.
- Make DynamicPageShim avoid changing the content of the Frame twice (making transitions work just fine :)). This will mean that you won’t be able to use the DynamicPageexactly as you would the standard Page (e.g. binding to "Frame.Content.MyCustomPropertyOnMyPage” won’t be possible… instead you’d have to bind to “Frame.Content.Content.MyCustomPropertyOnMyPage”).
Neither of these options significantly hampers functionality – just impacts either user experience or developer experience, but I’d love to hear your thoughts! Do you prefer option 1 or option 2?
In the meantime, I’m working on some fun little utilities that will make dynamically loading assemblies with navigation much simpler and more declarative, and am looking forward to blogging it when it’s ready!
Update: Credit where credit is due
About 10 minutes after I posted this, I came across someone (Koen Zwikstra) who posted almost the exact same thing before me, and I want to make sure he gets fair mention for getting there before me :)
You can find his post here: http://firstfloorsoftware.com/blog/animated-page-navigation-in-sl3/
Sorry for duplicating! Great stuff!