Welcome to the Composition API! If you’ve watch some of the //build talks the past two years you may have seen some members of our team show off the new API. While the API is certainly new for Windows 10, the Composition team has actually been around for awhile…
Since Windows Vista, the DWM has been powering the Windows experience. Everything you see on the screen we almost always have some hand in drawing. Developers were able to (and still are!) leverage the system compositor using Direct Composition. DComp, as we call it, was a way to position elements (that we call Visuals) on the screen in a tree like structure. For any of you that know the concept of the Visual Tree in XAML, this should sound familiar. The API was C++ only, and when Windows 8 came around there wasn’t a way to use the API in the new modern app world. This is where the Windows.UI.Composition API came into existence with Windows 10, as a way to leverage the DWM to make fast and beautiful user interfaces without breaking the perf bank.
While it is possible to create a “pure” Composition app, many of you will be delighted to know that you can actually consume the API from within XAML! This is a way to manipulate your pre-existing applications to add rich new experiences without much code. The very first thing you’ll want to do is to grab the
Visual that represents a
UIElement. For many of you, this method will become your best friend:
What exactly is a visual?
A Visual is a node in the Visual Tree, which means they can be the children of another Visual. However, there are different types of Visuals. First is
ContainerVisual which inherits from
ContainerVisual is different from
Visual in that it can have children of its own.
SpriteVisual inherits from
ContainerVisual, which means that it can have children of its own but also host its own content. This content is applied using the
Brush property on
SpriteVisual. For the most part, expect to be working with
SpriteVisual. Just like with XAML’s Visual Tree, Visuals are always relatively positioned by their parent. This also means that anything you attach to a
UIElement will be placed on top of it.
Comming back to the world of XAML,
ElementCompositionPreview.GetElementVisual retreives the Visual that represents a given
UIElement, which even includes the Frame (this makes for some pretty neat animation tricks and transitions that we’ll cover in a later post)! Some things you should be aware of when getting a
Visual from XAML:
- GetElementVisual returns a
Visual, so you can’t assign content to it. You are however, free to add a child
SpriteVisualthat has content (or event an entire subtree by attaching a
ContainerVisualwith many children!) by using the
- Using the Composition API requires the November 2015 update to Windows 10, otherwise known as build 10586.
- You can move entire subtrees or single visuals anywhere in the tree with ease, but you always need to detach it from its parent first. Currently, Visuals returned by
ElementCompo sitionPreview.GetElementVisualdo not report their parent (even though they have one) and thus cannot be reparented somewhere else without doing it in XAML. The rule of thumb is usually: when you’ve created it you’ve got complete control of it. You can, however, change properties or animate Visuals returned by XAML.
Baby's First Visual
If you’d like to follow along, the entire sample we’ll be building is available here.
The easiest thing to do is to create a
SpriteVisual with a solid color, so we’ll start with that. After creating a blank XAML app, go to MainPage.xaml.cs and try something like this:
What does this do? It first gets the
Compositor object from the Page’s
Compositor object is the second most important concept in the Composition API. Nearly every object related to the Composition API is created through this object. Usually your first plan of attack should be to get ahold of this object. Luckily for you, nearly every Composition object has a reference back to the
Compositor that created it. Within your XAML application, there will only be one
Compositor so it doesn’t matter where you get it from.
After we’ve retrieved the
Compositor we are able to create a
SpriteVisual. After that we set the visual’s size and give it an Offset (notice that you’ll need to be using the System.Numerics namespace for this). Finally we create a brush that just contains the color red and assign it to the visual (we’ll cover image loading in a future post next week). The only thing left to do is to attach it to the tree by using
SetElementChildVisual. Just like with XAML, if it’s not in the tree it won’t draw.
All of that should get you this:
So far, so good. But I think we can do better. One of the strengths of the Composition API are the animations. Let’s try one of those next:
Fun with animations
You may be wondering why we’ve jumped straight to animations, a seemingly advanced concept. Have no fear, as it’s actualy fairly easy using the Composition API! In this section you’ll learn how to create animations, and attach these animations not only to your own visuals but to XAML elements as well.
One of the animations available to us is the
KeyFrameAnimation, which we can create by using the
Next we’ll want to add some key frames:
The first parameter signifies a normalized time for that key frame, 0.0f being the beginning and 1.0f being the end (thus, you can’t assign something more than 1.0f for the first parameter). The second parameter is the actual value that the animation will set on a property. Depending on what type of
KeyFrameAnimation you created the type of the second parameter will vary. Here we are animating a single float value. The third parameter is optional, but it allows you to specify what type of easing function you’d like. Heare we just want a simple linear function, but you can define other functions like a cubic Bezier curve.
Next we are going to set how long the animation will run:
Now all that’s left is to attach it to the visual:
Notice that starting an animation has two parameters, the first being the name of the property as a
string and the second being the animation itself.
UPDATE 04-14-2016: Sometimes you’re up late and
strings can be hard. @NicoVermerir made a great suggestion to use the
nameof operator when referencing a visual’s property for use in an animation or expression. This is a great approach as it still gets you IntelliSense and is replaced at compile time, meaning that there shouldn’t be any difference in performance. To learn more about the
nameof operator, check out this MSDN page. Thanks Nico!
That last line is just specifying what the center point of our visual will be. The
CenterPoint property will determine the point at which the visual will rotate around. Since we want this point to be in the center of the visual, we set it to half the visual’s width and height. This way the visual will rotate around its center. And that’s it! The visual should now be rotating forever! But we can do even better than that. In the next bit we’ll be covering two concepts at once: XAML interop and reusing animations.
XAML interop describes the act of mixing XAML and Composition. Here we’ll be taking the same animation we’ve already built and apply it to a XAML control.
In our XAML for the page, I’m going to add a
Next is to set up our animation for our new button:
One thing to keep in mind is that because we’re using the button’s
ActualHeight properties we need to make sure the control is loaded before we try this out, otherwise it will report a size of 0. The easiest way is to move all of the code we’ve written to
AnimatingButton’s loaded event handler. Here’s how it should look in the end:
And that’s it! That’s how easy it is to use the Composition API and have it interop with XAML.
If you’d like to learn more about animations, you can find that here.
Tune in next week where we’ll be covering how to use images with your visuals and how to apply effects! Make sure you don’t miss a thing by subscribing to the RSS feed. If you have any questions or feedback, feel free to reach me on twitter or by email. For Composition questions you’re also free to contact the team on twitter or file an issue on our GitHub page.