Condividi tramite


Plumbing the SDK Samples and Unearthing More About Doing Per Frame Animation

There is a very cool sample buried deeply in the SDK called Per Frame Animation that has some very clever and useful code in it.  I recently was revisiting the sample and realized that there is a lot going on in the sample that may not at first be apparent.  As such, I decided to isolate just the firework effects part of the sample and put some more UI around it so that people could tweak the settings and see what is possible visually.  Here's the result - you can click the image to run it and download the source as well.  If you hover or click the blue cross on the right, you will see the effect.

 

If you look at the burst on the right, you'll get a sense for what the application does, although it is of course much prettier if you actually see it in action.  You can play with animating other visuals, including any geometry, text, etc.  You can also play around with the effect using the sliders and other toggles on the left. I borrowed Ernie Booth's color picker that he posted in this sample and I used Expression Interactive Designer to create the layout, playing around with styling and databinding.

So, what is going on here?  The crux is this line 477 in the page1.xaml file:

<ae:FireworkEffect x:Name="theEffect" >
<Button Style="{DynamicResource ButtonStyle1}" Width="100" Height="100" Content="Button"/>
</ae:FireworkEffect>

The button is wrapped in a FireworkEffect class. Now, if you dig into the FireworkEffect class, you'll see that it derives from OverlayRenderDecorator, which a custom FrameworkElement whose sole purpose is to overlay graphics on top of any UI element. It is quite clever what is going on in this class, because the OverlayRenderDecorator has two visual children but only one logical child. The first child, which is both a visual child and a logical child, is whatever is the child of the element, in this case a button.  The second visual child is a OverlayRenderDecoratorOverlayVisual, which is another custom class that derives from DrawingVisual.  This second child doesn't participate in layout, hittesting or any of the other behaviors native to logical children.  But, it does get to display UI.  When and how, you might ask?  

This is where another moment of cleverness comes in: the OverlayRenderDecorator declares its own virtual method, OnOverlayRender, which it calls after Arrange has been called in its ArrangeOverride override.   But, how is the derived type allowed to do its work?  Well, remember, the second visual child, buried inside the OverlayRenderDecorator, is derived from DrawingVisual, so it can pass its drawing context, to the derived class, as follows:

using (DrawingContext dc = _overlayVisual.RenderOpen())
{
//delegate to derived types
OnOverlayRender(dc);
}

 

This means that OnOverlayRender gets called after the logical children have been painted during OnRender, which is where WPF graphics are typically painted. This is real real handy because it can do per frame animations that don't affect layout, hittesting, input or anything else.

I modified the code in the FireworkEffect implementation of OnOverlayRender from the SDK to show off the ability to create some other effects. One that is kind of interesting is the DrawDrawing call, because you can pass any resource you have to it, which might be created by a designer in a tool such as Illustrator, Studio MX or Expression.    At first, I was thrown off by how to use DrawDrawing because, unlike other calls such as DrawGeometry, DrawEllipse, DrawImage or DrawText, there was no way to pass a point where the drawing is to take place.  Because the whole animation effect is created by updating where the drawing takes place, this was problematic.  I then realized that I could push a TranslateTransform to the DrawDrawing and acheive the same effect, as follows:

case "Drawing":
             GeometryDrawing gd = (GeometryDrawing)this.FindResource("d");
             drawingContext.PushTransform(new TranslateTransform(p.Location.X, p.Location.Y));
             drawingContext.DrawDrawing(gd);
             drawingContext.Pop();
             break;

If you want to try some other effects with the code, it is in the OnOverlayRender method of the FireworkEffect class where you would do it.

Another curious thing about the sample is that you'll notice that the CompositionTarget.Rendering doesn't do any UI work inside of it.  All of the painting, as it were, is done by the OnOverlayRender method.  So, what is the point of CompositionTarget.Rendering?  Well, it is used to create the physics effects and keep track of frame/time ratio.  Each particle in the effect is updated in the CompositionTarget.Rendering callback method, passing a delta time.

One final comment: I used the Flexible Application Template for the project so that I could easily toggle whether it is deployed as an XBAP or a Windows Application. 

download the source

Comments

  • Anonymous
    November 23, 2006
    Sweet!!

  • Anonymous
    November 23, 2006
    WOW!  Brings back memories... This was the product of my internship at MS 2 summers ago.  I'm really suprised it made it into the SDK at all because when I left it wasn't checked in. I spent half the summer creating various per-frame-animation prototypes and then finally just removed the old AnimationEffect class and created the CompositionTarget.Rendering event to replace it. Glad to see someone using this stuff!

  • Anonymous
    November 23, 2006
    Brandon -- Can you comment some more on the codebase and any gotchas or issues you know of?