Navigation Transitions with WriteableBitmap

Date 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>

2 Responses to “Navigation Transitions with WriteableBitmap”

  1. Silveright Resource Directory Update (21 Aug) said:

    [...] Original Post [...]

  2. Apply Transition Animation on Frame | Silverlike - A Free Microsoft Silverlight 3 Directory said:

    [...] 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 [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">