Navigation Transitions with WriteableBitmap
August 14, 2009 @ 10:16 am in Silverlight
If you have not had a chance to play with Navigation Frames in Silverlight they are very cool, they give developers the ability to navigate between "Page" within a single Silverlight Solution. The first time I used them I could believe how easy it was to implement. You simply add a few pages to your solution, drop a Navigation:Frame on to your MainPage.xaml UserControl and define some UriMappers.
One thing that i thought was missing was the ability to create nice looking transitions between pages as they are loaded into a frame. Recently I voiced my complaint to a coworker (the infamous Steve Saxon – @xmlguy) and he had the great idea of using a WritableBitmap to capture the current and next page within the Frame, than animating the images. Here is the solution I arrived at.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
namespace NavigationTransition
{
public partial class MainPage : UserControl
{
private HyperlinkButton _lastClicked;
private bool _isNavigating = false;
private readonly Image _imgLast = new Image();
private readonly Image _imgNext = new Image();
private Storyboard _sb = new Storyboard();
public MainPage()
{
InitializeComponent();
}
void SbCompleted(object sender, EventArgs e)
{
Panel panel = ContentFrame.Parent as Panel;
panel.Children.Remove(_imgLast);
panel.Children.Remove(_imgNext);
ContentFrame.Visibility = Visibility.Visible;
_sb.Stop();
_isNavigating = false;
}
private void ContentFrameNavigated(object sender, NavigationEventArgs e)
{
ContentFrame.Navigated -= ContentFrameNavigated;
Panel panel = ContentFrame.Parent as Panel;
panel.UpdateLayout();
WriteableBitmap bitmapN = new WriteableBitmap(ContentFrame, new TranslateTransform());
_imgNext.Source = bitmapN;
TranslateTransform ttNext = new TranslateTransform();
_imgNext.RenderTransform = ttNext;
panel.Children.Add(_imgNext);
_sb.Children.Add(CreateDoubleAnimation(ttNext, "X", -bitmapN.PixelWidth, 0, true));
ContentFrame.Visibility = Visibility.Collapsed;
_sb.Begin();
}
private void Nav_Click(object sender, RoutedEventArgs e)
{
_lastClicked = (HyperlinkButton)sender;
if (ContentFrame.Source.ToString() != _lastClicked.Tag.ToString() && !_isNavigating)
{
_sb = new Storyboard();
_sb.Completed += SbCompleted;
Panel panel = ContentFrame.Parent as Panel;
WriteableBitmap bitmapL = new WriteableBitmap(ContentFrame, new TranslateTransform());
_imgLast.Source = bitmapL;
TranslateTransform ttLast = new TranslateTransform();
_imgLast.RenderTransform = ttLast;
if (panel != null) panel.Children.Add(_imgLast);
ContentFrame.Navigated += ContentFrameNavigated;
ContentFrame.Navigate(new Uri(_lastClicked.Tag.ToString(), UriKind.Relative));
_sb.Children.Add(CreateDoubleAnimation(ttLast, "X", 0, bitmapL.PixelWidth, true));
_isNavigating = true;
}
}
private static DoubleAnimation CreateDoubleAnimation(DependencyObject element,
string property, double from, double to, bool addEasing)
{
DoubleAnimation da = new DoubleAnimation();
da.To = to;
da.From = from;
if (addEasing)
da.EasingFunction = new PowerEase() { EasingMode = EasingMode.EaseOut, Power = 3 };
Storyboard.SetTargetProperty(da, new PropertyPath(property));
Storyboard.SetTarget(da, element);
return da;
}
// If an error occurs during navigation, show an error window
private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
e.Handled = true;
ChildWindow errorWin = new ErrorWindow(e.Uri);
errorWin.Show();
}
}
} |
One of the things I found necessary was to delay the navigation so that I could capture a bitmap of the current frame. The way I intersected the navigation was to attach a Click event handler to my HyperlinkButton and then store the Uri in the Tag property. With a little more effort you could make this into a Behavior or even add a few additional types of transitions.
Code: NavigationTransition.zip
/p>
Tagged as 

August 20th, 2009 at 1:26 pm
[...] Original Post [...]
September 25th, 2009 at 10:43 am
[...] the Frame control, you will find out that the transition between elements isn’t very nice. Joel Neubeck attempted to create a slide transition within the navigation using WriteableBitmap. The result is [...]