Navigation with Animated Transition Effects
One of the common things that developers want to do is to navigate between pages of their application. Once they have that, then they want to make the transitions look pretty. The attached project (see below) show how to do use a TransitionControl to do both. The TransitionControl is extensible, so you can even write your own transitions. You can download the project by clicking on the Attachment links on the bottom of the post. Here is a snapshot of the demo app that shows the Image Page in mid-spin as it appears on top of the Controls Page:
How it works
The TransitionControl is subclassed from UserControl. This is its public API:
public partial class TransitionControl : UserControl
{
public TransitionControl();
public void GoTo(FrameworkElement newContent, ITransition transition, TimeSpan duration);
public void Completed();
public void CurrentToBack();
public void CurrentToFront();
public FrameworkElement New { get; }
public FrameworkElement Current { get; }
}
The GoTo method is the only method that you'll call to display new content. You pass in the new content, the transition that you wish to use, and how long you want the transition to take. The other methods and properties are for use by the transition. The sample application uses a TransitionControl for its main, but not the entire page. There is nothing magic about it; it is just a control. You can use more than one, make them any size you like, fill up the page with it, etc.
ITransition
The ITransition interface is as follows:
public interface ITransition
{
void GoTo(TransitionControl control, TimeSpan duration);
void Stop();
}
The Goto method is called by the TransitionControl, which passes in itself and the duration it received in its GoTo method. The Stop method is called by the TransitionControl when the TransitionControl.GoTo method is called before the a transition already in progress can finish.
The simplest implementation of ITransition included in the project is Swap:
/// <summary>
/// This is the simplest transition class, and is about all you can really do
/// without animating. This is a synchronous transition.
/// </summary>
public class Swap : ITransition
{
public void GoTo(TransitionControl control, TimeSpan duration)
{
control.CurrentToBack();
control.Completed();
}
public void Stop()
{
// Nothing to do here, since this is not async
}
}
The Swap transition ignores the duration and simply bring the new content to the front by calling control.CurrentToBack() and lets the control know that the transition is finished by calling control.Completed.
Animated transitions
Of course, merely swapping the content is not all that interesting. To get some nice effects, we'll need to animate. There is an abstract class in the sample that helps with this.
/// <summary>
/// The StoryboardTransition classs is designed to encapsulate an asynchronous,
/// animated transition.
/// </summary>
public abstract class StoryboardTransition : ITransition
{
public void GoTo(TransitionControl control, TimeSpan duration)
{
_control = control;
_storyboard = new Storyboard() { Duration = duration };
Storyboard.Completed += storyboardCompleted;
control.New.Resources.Add("storyboard", _storyboard);
GoToCore(duration);
Storyboard.Begin();
}
public void Stop()
{
_storyboard.Stop();
}
protected Storyboard Storyboard { get { return _storyboard; } }
protected TransitionControl Control { get { return _control; } }
// The subclass adds their animations to the Storyboard in this override.
protected abstract void GoToCore(TimeSpan duration);
protected void storyboardCompleted(object sender, EventArgs e)
{
CompletedCore();
_control.Completed();
}
// The subclass can remove the stuff needed only for the transition,
// such as clips or transforms.
protected virtual void CompletedCore() {}
private Storyboard _storyboard;
private TransitionControl _control;
}
To implement a subclass of the StoryboardTransition, you'll need to override GoToCore and add your animations to the Storyboard provided. You may also need to add some things to the new content, such as RenderTransforms or Clips. By overriding the CompletedCore transition, you get the opportunity to remove whatever you added that is no longer needed.
The FadeIn transition
The FadeIn transition is a simple animated transition that gives you an idea of how to subclass StoryboardTransition:
public class FadeIn : StoryboardTransition
{
protected override void GoToCore(TimeSpan duration)
{
Control.CurrentToBack();
DoubleAnimation opacityAnimation = new DoubleAnimation() { From = 0, To = 1, Duration = duration, FillBehavior=FillBehavior.HoldEnd };
Storyboard.SetTarget(opacityAnimation, Control.New);
Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("Opacity"));
Storyboard.Children.Add(opacityAnimation);
}
}
The GoToCore method is overridden (as it must be, since it abstract). The first thing it does is send the current content to the back, since the new content will be appearing in front of it. They it sets up the opacity animation and adds it to the Storyboard. That's all there is to do, because there is nothing to clean up. Note that the FillBehavior has been set to HoldEnd, meaning that if the animation is stopped, it will jump to the To value and stay there.
Other transitions
Here are the transitions that the project supplies:
Swap
Wipe (you can specify the direction)
FadeIn
FadeOut (I thought there would be a difference between fading in and out, but now I'm not so sure...)
Open (a circle opens up to reveal the new content)
Close (a circle closes, hiding the old content)
Grow (the new content zooms in from a point in the middle of the screen; spinning is optional)
Shrink (the old content disppears to a point in the middle of the screen; spinning is optional)
You can also write your own. If you make a cool transition, I'd love to see it.
Comments
Anonymous
June 15, 2008
PingBack from http://www.silverlightshow.net/news/Silverlight-news-for-June-15-2008.aspxAnonymous
June 15, 2008
Dave Relyeaのブログ に、Silverlight 2でのページ遷移エフェクトのサンプルが紹介されています。 ITransitionControlというインターフェイスと、UserControlからTransisionControlクラスをを定義して、スワップやワイプやフェードなどの遷移を実装していますAnonymous
June 16, 2008
Mino has a SL2B2 video player drop-in, Emil Stoychev on Custom Controls, Tamir Khason on a binding bugAnonymous
June 17, 2008
Hi Dave, Enjoyed talking to you last week at the Silverlight training. This is great stuff. I'll have to dig into the code more, but I'm wondering why there's such a performance hit when animating the control (DataGrid). Obviously that's a heavy duty control, but it seems like there would be a way to take a snapshot of the control and animate that. But maybe that's not yet possible in SL2. Again, great stuff. Bruce DenhamAnonymous
June 18, 2008
DataGrid is about the most heavyweight controls demo that I could have chosen, and I think that's what made me do it. So any other mix of controls will look good by comparison. It would also be possible to get more sophisticated and load the new pages up in advance, and make sure it goes through layout, etc. We don't have a way of doing the snapshot thing in this version. But anyway...who really requires a zooming, spinning DataGrid like that? Other transitions are smoother.Anonymous
June 22, 2008
The failure of the datagrid to perform simply points out two nagging problems.
- No threading of the user interface, and failing that, no support for a yield to allow the data to load without freezing the UI.
- As Dave stated, even if you can't do it right, access to a lower level Drawing/Bitmap API would allow you to capture and bitblt to the display. SilverLight has a bit of growing up to do, but we'll get there.
Anonymous
September 02, 2008
Pages as a paradigm likely pre-date the web itself although they have been popularized by document-basedAnonymous
October 14, 2008
Navigation is a fairly big subject, but there is not much built-in support for it in Silverlight 2 as