A short post on the connected animation service that I looked at in Windows Composition on the 14367 preview build and the new 14366 SDK today.
In case you’re curious, I do realise that with these posts on composition, I’m slowly working my way through some of the scenarios that the excellent GitHub samples from the composition team already cover with much more rigour and style than I do.
However, I find that there’s a big difference between opening up a sample, seeing the output and thinking “ok, I get that” versus starting from scratch and putting something together yourself and, generally, I learn better doing the latter because I make a bunch of mistakes that cause me to think about what I’m actually doing and how things might be working.
While I’d seen the “Connected Animations” topic in the newsletter here, I didn’t realise until yesterday that it was an actual thing surfaced by the ConnectedAnimationService class living in Windows.UI.Xaml.Media.Animation.
I knocked together a quick app which displays some pictures on one page and allows the user to click a picture to navigate to a second page which displays it full screen. Here’s that app running;
I then added a connected animation such that the image on page 1 animates to its new position on page 2 and it then runs like this (assuming the animations come out reasonably on the screen capture here);
It was pretty easy to add that animation here even though it deals with content across the “page” boundary in the XAML application although I think it’s useful to remember that;
- UWP applications (XAML or not) don’t have to be made up of Pages hosted within Frames.
- It’s always possible to grab the Window/Frame (or some other container that you have parented them off) in your application and play with the layering to achieve results that cross page boundaries with or without the composition APIs.
Putting that to one side, the app I made here is very, very simple. It just has a main page UI;
<Page x:Class="App5.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App5" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <GridView> <GridView.Items> <x:String>http://www.disney.co.uk/muppets/cms_res/images/download_pics/wallpapers/kermit-wallpaper-1920x1200.jpg</x:String> <x:String>http://www.disney.co.uk/muppets/cms_res/images/download_pics/wallpapers/animal-wallpaper-1920x1200.jpg</x:String> <x:String>http://www.disney.co.uk/muppets/cms_res/images/download_pics/wallpapers/fozzie-wallpaper-1920x1200.jpg</x:String> <x:String>http://www.disney.co.uk/muppets/cms_res/images/download_pics/wallpapers/gonzo-wallpaper-1920x1200.jpg</x:String> </GridView.Items> <GridView.ItemTemplate> <DataTemplate> <Button Template="{x:Null}" Click="OnNavigate" Tag="{Binding}"> <Image Source="{Binding}" Width="300" Stretch="Uniform" /> </Button> </DataTemplate> </GridView.ItemTemplate> </GridView> </Grid> </Page>
and I know that it’s pretty nasty to bind up the Tag in the way that I have but it’s “easy” so I did it and then the code behind just navigates based on that;
using System; using System.Numerics; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Media.Animation; namespace App5 { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } void OnNavigate(object sender, RoutedEventArgs e) { var button = sender as Button; var parameter = button.Tag as String; this.Frame.Navigate(typeof(PicturePage), parameter); } } }
and then I have a PicturePage with an Image in it;
<Page x:Class="App5.PicturePage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App5" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Image x:Name="image" Source="{Binding}" /> </Grid> </Page>
and a bit of code behind;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Navigation; namespace App5 { public sealed partial class PicturePage : Page { public PicturePage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.DataContext = (string)e.Parameter; } } }
and that’s pretty much that (ugly though it may be ).
In order to then add in the notion that the image from the first page should animate across to the second page I don’t have to do very much. On the first page, I simply add some code to the click handler of the button before the navigation is done;
void OnNavigate(object sender, RoutedEventArgs e) { var button = sender as Button; var parameter = button.Tag as String; var service = ConnectedAnimationService.GetForCurrentView(); service.PrepareToAnimate("SelectedMuppet", (Image)button.Content); this.Frame.Navigate(typeof(PicturePage), parameter); }
and so we use the static ConnectedAnimationService.GetForCurrentView() to get a service and then we tell it which piece of content is going to animate from this page to the next page – in this case that’s the image which is the content of the button.
On the ‘receiving’ end I just need to do a little more work in the OnNavigatedTo override;
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.DataContext = (string)e.Parameter; var animation = ConnectedAnimationService.GetForCurrentView(); animation.GetAnimation("SelectedMuppet").TryStart(this.image); }
so I get the service again and tell it to start the specific animation (so that I can have more than one) and give it the element that represents the “destination” here.
Or…so I thought. That doesn’t actually work and this is where those samples become really useful to someone like me as a quick look at the relevant sample code illustrated that I might have to wait until the image was actually opened before trying to start that animation. There’s a comment in the sample that this won’t be the case in later previews but, for now, I added an ImageOpened handler to my Image and moved the bottom two lines of code above into that handler and all was good.
I like this. It’s short, it’s sweet, it’s clear what it does and there’s not much to get your head around to enable an effect which makes even the simplest app feel better. It’s worth saying that the duration of the animation here and the easing function applied to it can also be tweaked very easily.
Pingback: Szumma #044 – 2016 24. hét | d/fuel
Pingback: Windows 10 Anniversary Update Preview–Composition and Connected Animations | Tech News