Windows 10 Anniversary Update Preview–Composition and Expressions

When I first looked at the composition APIs for animation in this post I said that I didn’t understand what animation expressions were and that;

“I read through Kenny’s explanation of ‘Expressions’ but I need to dig into that more in order to really get a grip as to what’s going on there.”

For a little while, I’ve felt happy with that ignorance but then I saw this video on C9 a few weeks ago;

image

and what I saw in there was pretty cool and I thought it was about time I tried to give a little bit more thought into these ‘expression animations’ to see what they are.

In the meantime, I’d also read Rob’s excellent post;

“Fun with Expressions”

From that article I think I understand “The basics of an expression” and “Property sets” but I must admit that at the time of writing I skipped the part about parallax scrolling in a ListView – I’ve seen this example in a couple of places before but I feel I need to play with the pieces some more before I attempt to climb that hill. I’m too dumb to make so many mental leaps in one go Smile

Whether it’s right or wrong, I’m starting to think of these ‘expressions’ and ‘CompositionObjects’ as almost being a form of sibling to DependencyObjects and their dependency properties but with a stronger notion of ‘binding’ that has been augmented by an expression language that allows for calculations and functions.

I wanted to try out some pieces for myself though and after scanning the list of functions  for expressions, the first thing I thought of was how I could “bind” the rotation of some Visual to the value of a slider and so that’s what I started to experiment with.

I made a small UI;

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    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}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Rectangle
            Width="100"
            Height="100"
            Fill="Blue"
            Stroke="Black"
            StrokeThickness="2"
            x:Name="rectangle">
            
        </Rectangle>
        <Slider
            Margin="12"
            Grid.Row="1"
            Minimum="0"
            Maximum="360"
            Value="{x:Bind SliderValue,Mode=TwoWay}" />
    </Grid>
</Page>

and then I wrote a little code behind;

using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += OnLoaded;
    }

    public double SliderValue
    {
      get
      {
        // reach into my underlying CompositionPropertySet for the value
        float value = 0.0f;

        this.propertySet?.TryGetScalar("r", out value);

        return (value);
      }
      set
      {
        // reach into my underlying CompositionPropertySet for the value
        this.propertySet.InsertScalar("r", (float)value);
      }
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Get hold of my rectangle's visual
      this.rectangleVisual = ElementCompositionPreview.GetElementVisual(
        this.rectangle);

      // Keep hold of the compositor
      this.compositor = this.rectangleVisual.Compositor;

      // Change the centre point for the animation.
      this.rectangleVisual.CenterPoint = new System.Numerics.Vector3(
        (float)this.rectangle.ActualWidth / 2.0f,
        (float)this.rectangle.ActualHeight / 2.0f,
        0);

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      // Make my animation.
      var expressionAnimation = this.compositor.CreateExpressionAnimation(
        "myProps.r");

      // Explain to the animation that when I say "myProps" I mean this set of
      // properties I'm passing in here...
      expressionAnimation.SetReferenceParameter("myProps", this.propertySet);

      // Kick off the animation.
      this.rectangleVisual.StartAnimation("RotationAngleInDegrees", expressionAnimation);
    }
    CompositionPropertySet propertySet;
    Visual rectangleVisual;
    Compositor compositor;
  }
}

Now, this is more code (of course) than just putting a transform onto the rectangle and then binding it directly to the value of the slider but the aim is to try out the new APIs here rather than to produce the smallest example Smile

In this code, I’ve created my first CompositionPropertySet and I’ve added one property to it that I’d named “r” and it’s a simple scalar which I’ve exposed behind the getter/setter for the class property called SliderValue.

I’ve then created an expression animation that picks up that value (and changes to it) and associates it with the rotation of the rectangle’s visual.

That all works quite nicely.

image

but it’s not exactly enabling any new scenario for me.

My next quick test was to change the expression “myProps.r” into a new expression “myProps.r * 0.5” and, sure enough, my slider now binds to a range from 0 to 180 degrees of rotation on my rectangle.

I duplicated the Rectangle in the XAML file 3 more times, taking away the rectangle’s name and parenting those 4 elements into a Grid named “parentGrid”.

I then changed the OnLoaded function so that it animated each of the 4 rectangles in a different direction while still rotating them – i.e. 2 expression animations applied to each element and a slightly more complex (and dynamic) expression in one of those animations;

 void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Keep hold of the compositor
      this.compositor = ElementCompositionPreview.GetElementVisual(
        this.parentGrid).Compositor;

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      var increment = 360.0d / this.parentGrid.Children.Count;

      for (int i = 0; i < this.parentGrid.Children.Count; i++)
      {
        var angle = (i + 1) * increment;
        var rectangle = (Rectangle)this.parentGrid.Children[i];
        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        var animation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos(ToRadians({angle})) * myProps.r, Sin(ToRadians({angle})) * myProps.r, 0)");

        animation.SetReferenceParameter("myProps", this.propertySet);

        visual.StartAnimation("Offset" , animation);

        animation = this.compositor.CreateExpressionAnimation("myProps.r");
        animation.SetReferenceParameter("myProps", this.propertySet);
        visual.StartAnimation("RotationAngleInDegrees", animation);
      }
    }

and that also worked out quite nicely although debugging missing parentheses in that magic string passed to the CreateExpressionAnimation function starts to get a little bit ‘fun’ Smile

I wanted to take that a little further and so I took out the 4 rectangles from the XAML file and switched the code such that it created the rectangles dynamically, making 30 rectangles and then trying to animate them out along some spiral while rotating and based on the value of the slider still;

and that’s with relatively little code in that OnLoaded method;

    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Keep hold of the compositor
      this.compositor = ElementCompositionPreview.GetElementVisual(
        this.parentGrid).Compositor;

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      var brush = new SolidColorBrush(Colors.Blue);

      // have 30 rectangles
      var rectangleCount = 30.0f;

      // Space them out so that 10 fit around a circle
      var angularSpacing = Math.PI / 5.0f;
      var radius = 10.0f;

      for (int i = 0; i < rectangleCount; i++)
      {
        var rectangle = new Rectangle()
        {
          Width = 20,
          Height = 20,
          Fill = brush
        };
        this.parentGrid.Children.Add(rectangle);

        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        // The rectangle should animate to an angle 
        var angle = angularSpacing * i;

        // And a distance governed by some constant. 360 here is the
        // max value of the slider in the UI.
        var distance = i * radius / 360.0f;

        // We want to animate out to this position based on the value
        // in the slider.
        var animation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos({angle}) * {distance} * myProps.r, " +
           $"Sin({angle}) * {distance} * myProps.r, 0)");
      
        animation.SetReferenceParameter("myProps", this.propertySet);

        visual.StartAnimation("Offset", animation);

        animation = this.compositor.CreateExpressionAnimation("myProps.r");
        animation.SetReferenceParameter("myProps", this.propertySet);
        visual.StartAnimation("RotationAngleInDegrees", animation);
      }
    }

and, so far, I’ve driven everything off the value of my slider. That value is bound to a setter which changes a property in my property set.

All the animations are then driven from the value of r” in that property set.

However, Visuals also have property sets and so I could (e.g.) get rid of my slider altogether and have the positions and rotation values of my rectangles bound to some other property which is also animating.

Taking the slider out of the UI;

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    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}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Grid
            x:Name="parentGrid">
        </Grid>
    </Grid>
</Page>

and re-working the code-behind such that it creates a Visual (spriteVisual in the code below) in the parentGrid Grid with a blue background and an opacity which is then animated from 0->1->0 over a second period.

I can then anchor my other animations off that changing Opacity property;

using System;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Get the background grid visual
      var gridVisual = ElementCompositionPreview.GetElementVisual(this.parentGrid);

      // Keep hold of the compositor
      this.compositor = gridVisual.Compositor;

      // Create a new visual to put on top of the grid
      var spriteVisual = this.compositor.CreateSpriteVisual();

      // Paint it blue
      var colorBrush = this.compositor.CreateColorBrush(Colors.CornflowerBlue);
      spriteVisual.Brush = colorBrush;

      // We should handle re-size too, we don't.
      spriteVisual.Size = new System.Numerics.Vector2(
        (float)this.parentGrid.ActualWidth,
        (float)this.parentGrid.ActualHeight);

      // Put it into the Grid.
      ElementCompositionPreview.SetElementChildVisual(this.parentGrid, spriteVisual);

      // Animate the opacity 0->1->0
      var animation = this.compositor.CreateScalarKeyFrameAnimation();
      animation.InsertKeyFrame(0.0f, 0.0f);
      animation.InsertKeyFrame(0.5f, 1.0f);
      animation.InsertKeyFrame(1.0f, 0.0f);
      animation.Duration = TimeSpan.FromSeconds(2);
      animation.IterationBehavior = AnimationIterationBehavior.Forever;
      spriteVisual.StartAnimation("Opacity", animation);

      // Create the XAML rectangles.
      var brush = new SolidColorBrush(Colors.Blue);

      // have 30 rectangles
      var rectangleCount = 30.0f;

      // Space them out so that 10 fit around a circle
      var angularSpacing = Math.PI / 5.0f;
      var radius = 10.0f;

      for (int i = 0; i < rectangleCount; i++)
      {
        var rectangle = new Rectangle()
        {
          Width = 20,
          Height = 20,
          Fill = brush
        };
        this.parentGrid.Children.Add(rectangle);

        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        // The rectangle should animate to an angle 
        var angle = angularSpacing * i;

        // And a distance governed by some constant. 
        var distance = i * radius;

        // We animate the position of the rectangle based on the Opacity property
        // of the backdrop which is also being animated.
        var exprAnimation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos({angle}) * {distance} * myProps.Opacity, " +
           $"Sin({angle}) * {distance} * myProps.Opacity, 0)");

        exprAnimation.SetReferenceParameter("myProps", spriteVisual);

        visual.StartAnimation("Offset", exprAnimation);

        // We animate the rotation of the rectangle based on the Opacity property
        // of the backdrop which is also being animated.
        exprAnimation = this.compositor.CreateExpressionAnimation("myProps.Opacity * 360");
        exprAnimation.SetReferenceParameter("myProps", spriteVisual);
        visual.StartAnimation("RotationAngleInDegrees", exprAnimation);
      }
    }
    Compositor compositor;
  }
}

and this gives a smooth flashing effect;

and, while I’m sure this can be done to some level in XAML it’s starting to be obvious that there’s a lot of power here that can be expressed quite succinctly and which seems to give smooth, predictable performance and these ideas of;

    1. Property Sets having values that can be monitored for some kind of change notification
    2. Visuals having Property Sets
    3. Animations being able to set property values based on calculated expressions from values in Property Sets

start to hit home and seem to be really clever Smile

I wanted to see how this plays with effects and so I modified my UI again to have both an image and a rectangle and it’s worth saying that everything that I’ve experimented with so far in this post ran on build 10586 but at this point I switched to build 14332 in order to pick up the CompositionBackdropBrush that I tried out in my previous post.

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="parentGrid">
        <Grid
            x:Name="imgGrid">
            <Image
                Source="http://www.travelandescape.ca/wp-content/uploads/2012/05/Thailand-The-Beach.jpg"
                Stretch="UniformToFill" />
        </Grid>
        <Rectangle
            Width="100"
            Height="100"
            Fill="Blue"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            x:Name="myRectangle" />
    </Grid>
</Page>

and then I wrote code behind this in order to rotate the rectangle from 0 to 360 degrees and back over a 5 second interval and to blur the image behind the rectangle by dividing that rotation angle by 36 to scale it from 0 to 10;

using Microsoft.Graphics.Canvas.Effects;
using System;
using System.Numerics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // grab the visual for the rectangle
      var rectangleVisual = ElementCompositionPreview.GetElementVisual(this.myRectangle);

      rectangleVisual.CenterPoint =
        new Vector3(
          (float)this.myRectangle.ActualWidth / 2.0f,
          (float)this.myRectangle.ActualHeight / 2.0f,
          0.0f);

      // grab the compositor
      this.compositor = rectangleVisual.Compositor;

      // animate the rectangle's rotation from 0->360->0 over 5 seconds.
      var animation = this.compositor.CreateScalarKeyFrameAnimation();
      animation.InsertKeyFrame(0.0f, 0.0f);
      animation.InsertKeyFrame(0.5f, 360.0f);
      animation.InsertKeyFrame(1.0f, 0.0f);
      animation.Duration = TimeSpan.FromSeconds(5);
      animation.IterationBehavior = AnimationIterationBehavior.Forever;

      // Start that animation on the rectangle visual.
      rectangleVisual.StartAnimation("RotationAngleInDegrees", animation);

      // Now create a blur effect description.
      var blur = new GaussianBlurEffect()
      {
        Name = "Blur", // Name needed here so we can animate it.
        BorderMode = EffectBorderMode.Hard,
        BlurAmount = 0.0f,
        Source = new CompositionEffectSourceParameter("source")
      };
      // Make the factory.
      var effectFactory = compositor.CreateEffectFactory(blur,
        new string[] { "Blur.BlurAmount" });

      // Make the brush.
      var effectBrush = effectFactory.CreateBrush();

      // Put a backdrop brush as the source of the effect.
      effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush());

      // Make a visual.
      var visual = this.compositor.CreateSpriteVisual();
      visual.Size = new Vector2(
        (float)this.parentGrid.ActualWidth, (float)this.parentGrid.ActualHeight);

      // Paint it with the brush
      visual.Brush = effectBrush;

      // Put it on top of our image so the effect applies to the image
      ElementCompositionPreview.SetElementChildVisual(this.imgGrid, visual);

      // Finally, make an expression animation to set the blur amount to
      // 1/36 * the rotation angle of the rectangle
      var exprAnimation = this.compositor.CreateExpressionAnimation(
        "rectangle.RotationAngleInDegrees / 36");

      // tell the expression what "rectangle" is meant to mean
      exprAnimation.SetReferenceParameter("rectangle", rectangleVisual);

      // Start that animation.
      effectBrush.StartAnimation("Blur.BlurAmount", exprAnimation);
    }
    Compositor compositor;
  }
}

Giving the effect;

and so now the blur of that effect brush is tied to the rotation of the rectangle.

The last experiment that I wanted to try was to mix these expressions with implicit animations and also to try and set up some ‘chain’ of visuals such that each visual had an expression that referenced an earlier visual in the chain.

I wasn’t quite sure what effect I wanted other than to experiment and the effect that I ended up with is as below;

What’s going on in that demo is;

    1. I create a blue rectangle and I move it around the screen whenever the mouse moves (this is all just XAML)
    2. I have an implicit animation which animates any changes to its Offset.
    3. I have an animation that rotates it over a 10 second period through 0->360->0.
    4. I create 10 green rectangles N = 1 to N = 10 with the blue rectangle playing the part of N = 0 and each rectangle M uses an expression to;
      1. Make its X,Y offset equal to 0.75 * that of the M-1 rectangle.
      2. Make its scale equal to 0.75 * that of the M-1 rectangle.
      3. Make its opacity equal to 0.75 * that of the M-1 rectangle.
      4. Make its rotation equal to that of the M-1 rectangle minus 45 degrees.

The XAML here is very simple;

<Page
  x:Class="App24.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:App24"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d">

  <Canvas
    x:Name="parentCanvas"
    PointerMoved="OnPointerMoved"
    Background="Silver">
    <Rectangle
      Width="100"
      Height="100"
      Fill="Blue"
      VerticalAlignment="Center"
      HorizontalAlignment="Center"
      x:Name="myRectangle"
      Visibility="Collapsed" />
  </Canvas>
</Page>

and the code looks a bit lengthy but then it’d be fairly easy for me to refactor this into some common functions but I left it in its ‘longer form’ for now;

using Microsoft.Graphics.Canvas.Effects;
using System;
using System.Numerics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // grab the visual for the rectangle
      this.rectangleVisual = ElementCompositionPreview.GetElementVisual(this.myRectangle);

      // grab the compositor
      this.compositor = rectangleVisual.Compositor;

      // Create an animation for whenever this visual changes its offset
      var animation = this.compositor.CreateVector3KeyFrameAnimation();
      animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
      animation.Target = "Offset";
      animation.Duration = TimeSpan.FromMilliseconds(500);

      // Make an implicit animations collection
      var collection = this.compositor.CreateImplicitAnimationCollection();

      // Whenever our visual changes its offset, we want it implicitly animated.
      collection["Offset"] = animation;
      this.rectangleVisual.ImplicitAnimations = collection;

      // Set the center point for rotation
      this.rectangleVisual.CenterPoint = new Vector3(
        (float)this.myRectangle.ActualWidth / 2.0f,
        (float)this.myRectangle.ActualHeight / 2.0f,
        0.0f);

      // animate the rectangle's rotation from 0->360->0 over 2 seconds.
      var rotationAnimation = this.compositor.CreateScalarKeyFrameAnimation();
      rotationAnimation.InsertKeyFrame(0.0f, 0.0f);
      rotationAnimation.InsertKeyFrame(0.5f, 360.0f);
      rotationAnimation.InsertKeyFrame(1.0f, 0.0f);
      rotationAnimation.Duration = TimeSpan.FromSeconds(10);
      rotationAnimation.IterationBehavior = AnimationIterationBehavior.Forever;

      // set that going.
      this.rectangleVisual.StartAnimation("RotationAngleInDegrees", rotationAnimation);
    }
    void CreateRectangles()
    {
      // Let's make 10, green rectangles.
      var rectangleCount = 10;
      var brush = new SolidColorBrush(Colors.Green);

      var previousVisual = this.rectangleVisual;

      for (int i = 0; i < rectangleCount; i++)
      {
        // XAML rectangle.
        var rectangle = new Rectangle()
        {
          Fill = brush,
          Width = 100,
          Height = 100
        };
        // Add to canvas.
        this.parentCanvas.Children.Add(rectangle);

        // Get visual.
        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        // Center it for rotation.
        visual.CenterPoint = new Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0.0f);

        // We offset it as 0.75* the previous rectangle (X,Y only)
        var exprAnimation = this.compositor.CreateExpressionAnimation(
          $"Vector3(previous.Offset.X * 0.75, previous.Offset.Y * 0.75, 0)");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Offset", exprAnimation);

        // We scale it as 0.75* the previous rectangle (X,Y only)
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "Vector3(previous.Scale.X * 0.75, previous.Scale.Y * 0.75, previous.Scale.Z)");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Scale", exprAnimation);

        // We rotate it 45 degrees behind the previous rectangle
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "previous.RotationAngleInDegrees - 45");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("RotationAngleInDegrees", exprAnimation);

        // We make it 0.75* as opaque as the previous rectangle.
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "previous.Opacity * 0.75");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Opacity", exprAnimation);

        // Current becomes previous.
        previousVisual = visual;
      }
    }
    void OnPointerMoved(object sender, PointerRoutedEventArgs e)
    {
      var pos = e.GetCurrentPoint(this.parentCanvas).Position;

      if (this.myRectangle.Visibility == Visibility.Collapsed)
      {
        this.myRectangle.Visibility = Visibility.Visible;
        this.CreateRectangles();
      }
      Canvas.SetLeft(
        this.myRectangle, pos.X - (this.myRectangle.ActualWidth / 2));
      Canvas.SetTop(
        this.myRectangle, pos.Y - (this.myRectangle.ActualHeight / 2));
    }
    Visual rectangleVisual;
    Compositor compositor;
  }
}

Note that the OnLoaded handler does a bit of the work and then the CreateRectangles function does the rest when it sees the first PointerMoved event.

It turns out then that ‘chaining’ expression animations on a set of visuals is a simple thing to do and, no doubt, could lead to some fairly fancy effects when combined with functions.

The more I play with this layer, the more powerful it seems to be to me. One thing I think that I would like to see is some abstraction around sizing such that sizes could be specified as percentages or similar and perhaps some way of auto-sizing a visual when its container changes. Other than that, things are looking good Smile

Windows 10 Anniversary Update Preview–Composition and Implicit Animations

I like this Smile

I played a little with animations in the Visual Layer back on 10586 in this post and Rob has a better introduction here on his site about animations.

But the //Build 2016 session below talks about the idea of implicit animations;

image

which I’d summarise as a way for the system to monitor a property change on a Visual such that when that property changes it can step in to animate the change in the way that the developer has prescribed.

In pseudo-code;

var visual = GetSomeVisualFromSomewhere();

visual.Property = SomeValue;

with the idea being that on the second line of code the system will say “Aha, the Property value is changing – should I animate that for you?”.

I wanted to give this a quick trial and so I wrote some code which produces the effect in the short video snippet below;

and this is running on build 14352 with SDK 14332 and it’s just a XAML Grid containing a sub-grid and 2 buttons;

<Page
  x:Class="App4.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:App4"
  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}">

    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition
        Height="Auto" />
    </Grid.RowDefinitions>
    <Grid
      x:Name="parentGrid">

    </Grid>
    <StackPanel
      Grid.Row="1"
      HorizontalAlignment="Center"
      Orientation="Horizontal">
      <Button
        Content="Expand"
        Click="OnExpand" />
      <Button
        Content="Collapse"
        Click="OnCollapse" />
    </StackPanel>
  </Grid>
</Page>

and then a little bit of code behind;

using System;
using System.Collections.Generic;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App4
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      this.compositor = ElementCompositionPreview.GetElementVisual(this.parentGrid).Compositor;

      var brush = new SolidColorBrush(Colors.Red);

      for (int i = 0; i < NUM_CIRCLES; i++)
      {
        this.parentGrid.Children.Add(
          new Ellipse()
          {
            Width = RADIUS,
            Height = RADIUS,
            Fill = brush,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
          }
        );
      }
      this.Initialise();
    }
    void Initialise()
    {
      if (!initialised)
      {
        var animation = compositor.CreateVector3KeyFrameAnimation();
        var easingFunction = compositor.CreateCubicBezierEasingFunction(
          new System.Numerics.Vector2(0.5f, 0.0f),
          new System.Numerics.Vector2(1.0f, 1.0f));

        animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue", easingFunction);
        animation.Duration = TimeSpan.FromMilliseconds(250);
        animation.Target = "Offset";

        var collection = compositor.CreateImplicitAnimationCollection();
        collection["Offset"] = animation;

        this.visuals = new List<Visual>();

        foreach (var child in this.parentGrid.Children)
        {
          var ellipse = child as Ellipse;
          var visual = ElementCompositionPreview.GetElementVisual(ellipse);
          visual.ImplicitAnimations = collection;
          this.visuals.Add(visual);
        }
        initialised = true;
      }
    }
    void OnExpand(object sender, RoutedEventArgs e)
    {
      this.OnChangeOffsets(true);
    }
    void OnCollapse(object sender, RoutedEventArgs e)
    {
      this.OnChangeOffsets(false);
    }
    void OnChangeOffsets(bool expand)
    {
      var radius = (this.parentGrid.ActualHeight / 2.0d) * 0.4d;
      var angle = 2.0 * Math.PI / NUM_CIRCLES;

      for (int i = 0; i < NUM_CIRCLES; i++)
      {
        float incrementX = (float)(radius * Math.Cos(i * angle));
        float incrementY = (float)(radius * Math.Sin(i * angle));
        if (!expand)
        {
          incrementX = 0 - incrementX;
          incrementY = 0 - incrementY;
        }

        visuals[i].Offset = new System.Numerics.Vector3(
          visuals[i].Offset.X + incrementX,
          visuals[i].Offset.Y + incrementY,
          0);
      }
    }
    bool initialised;
    List<Visual> visuals;
    static readonly int NUM_CIRCLES = 30;
    static readonly int RADIUS = 30;
    Compositor compositor;
  }
}

and what I like about this is that the code in the function OnChangeOffsets only has to deal with changing the value of the Offset property (X,Y,Z) on the Visuals. It does not have to ‘remember’ that there are a bunch of animations that need to be fired, that’s already been set up by code which is largely orthogonal to this code.

By the way – I think the constant RADIUS in that code might be better called DIAMETER – just noticed that!

I also liked the idea that I can give a single animation to all of my Visuals and I can use the “this.FinalValue” nomenclature to signify “the value to which the property has been set”. I must admit that I didn’t think this was likely to work when I first tried it out and so it was nice to find that my hope matched reality in terms of just creating one animation and one easing function.

The other thing that I like is that the animations aren’t running on my UI thread so I don’t have to worry about that and the end result looks smooth.

Comparing with a Pure XAML Implementation

I did wonder though how this might compare to working entirely at the XAML level and so I thought that I’d quickly rewrite the code so as to have a comparison.

I made one change to my “UI” – I changed the Grid named “parentGrid” to be a Canvas named “parentCanvas” and then I rewrote a chunk of the code behind;

 using System;
  using Windows.UI;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using Windows.UI.Xaml.Media;
  using Windows.UI.Xaml.Media.Animation;
  using Windows.UI.Xaml.Shapes;

  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      var brush = new SolidColorBrush(Colors.Red);

      for (int i = 0; i < NUM_CIRCLES; i++)
      {
        var ellipse = new Ellipse()
        {
          Width = DIAMETER,
          Height = DIAMETER,
          Fill = brush
        };
        Canvas.SetLeft(ellipse, this.parentCanvas.ActualWidth / 2.0d - (DIAMETER / 2.0d));
        Canvas.SetTop(ellipse, this.parentCanvas.ActualHeight / 2.0d - (DIAMETER / 2.0d));

        this.parentCanvas.Children.Add(ellipse);
      }
      this.Initialise();
    }
    void Initialise()
    {
      if (!initialised)
      {
        this.storyboard = new Storyboard();

        var easingFunction = new CubicEase()
        {
          EasingMode = EasingMode.EaseIn
        };

        foreach (var child in this.parentCanvas.Children)
        {
          var ellipse = child as Ellipse;
          var animationX = new DoubleAnimation()
          {
            Duration = TimeSpan.FromMilliseconds(250),
            EasingFunction = easingFunction,
            By = 0,
          };
          var animationY = new DoubleAnimation()
          {
            Duration = TimeSpan.FromMilliseconds(250),
            EasingFunction = easingFunction,
            By = 0
          };
          Storyboard.SetTarget(animationX, ellipse);
          Storyboard.SetTargetProperty(animationX, "(Canvas.Left)");
          Storyboard.SetTarget(animationY, ellipse);
          Storyboard.SetTargetProperty(animationY, "(Canvas.Top)");
          this.storyboard.Children.Add(animationX);
          this.storyboard.Children.Add(animationY);
        }
        initialised = true;
      }
    }
    void OnExpand(object sender, RoutedEventArgs e)
    {
      this.OnChangeOffsets(true);
    }
    void OnCollapse(object sender, RoutedEventArgs e)
    {
      this.OnChangeOffsets(false);
    }
    void OnChangeOffsets(bool expand)
    {
      var radius = (this.parentCanvas.ActualHeight / 2.0d) * 0.4d;
      var angle = 2.0 * Math.PI / NUM_CIRCLES;

      for (int i = 0; i < NUM_CIRCLES; i++)
      {
        float incrementX = (float)(radius * Math.Cos(i * angle));
        float incrementY = (float)(radius * Math.Sin(i * angle));
        if (!expand)
        {
          incrementX = 0 - incrementX;
          incrementY = 0 - incrementY;
        }
        var animationIndex = i * 2;
        ((DoubleAnimation)this.storyboard.Children[animationIndex]).By = incrementX;
        ((DoubleAnimation)this.storyboard.Children[animationIndex + 1]).By = incrementY;
      }
      this.storyboard.Begin();
    }
    bool initialised;
    static readonly int NUM_CIRCLES = 30;
    static readonly int DIAMETER = 30;
    Storyboard storyboard;
  }

There’s a couple of differences from the coding perspective;

    • I end up creating NUM_CIRCLES * 2 animations = 60 animations in one Storyboard whereas in the composition case I created one animation.
    • The implementation of OnChangeOffsets becomes a process of adjusting the By values of those animations and then running the Storyboard which contains them whereas in the composition case that function became focused on changing the property values that I wanted to change.

but it’s not radically different and I don’t (for this tiny example) see much difference in the runtime behaviour of the app although I will say that the composition example does feel a little smoother to me.

I’m keen to dig into this a little more in the future so may have more posts to share on it but my first trial here was a positive one Smile

Windows 10 IoT Core, Raspberry PI 2, Sharing Your WiFi Connection & Making a Web Browser

One of the challenges with working with a device running Windows 10 IoT Core like a Raspberry PI 2 at the moment is that there isn’t support for wireless networking and so there’s a bit of a need to stick a physical cable into the back of the Raspberry PI 2.

It’s probably ‘an obvious thing’ but I thought I’d just make a note of a simple approach to getting your PI onto your WiFi connection via your laptop in case it’s of help to people. It’s what I’ve been doing in places where I might have a WiFi connection but not a wired cable to plug into.

I’ve been simply taking an Ethernet cable and plugging it directly between my laptop and my Raspberry PI. That causes both the PI and my laptop to generate 169.254. IP addresses link-local addresses and it allows me to ping my PI by name;

clip_image001

and also to open up the web interface onto the device;

image

I can also use Visual Studio’s remote debugger to debug on the device simply by choosing that I’m debugging ARM architecture and then selecting the device in the ‘Remote Machine’ dialog box;

image

but this only gives me connectivity between the PC and the PI, if the PI needs to reach out to the wider internet then I’d need to ensure that it’s got a gateway to go through and (I think) the easiest way to do that is to switch on internet connection sharing with the PC’s WiFi connection. This is my setup;

image

and I can then cycle the power on the PI to get it to refresh its IP address as shown here in the web interface;

image

and now the PI can reach out to the internet. As a quick, easy example let’s imagine that I want to build some kind of ‘Web Kiosk’ on my PI. I’ve got a breadboard with a switch on it that is connected to GPIO pin 27 and so I could use a WebView in a UI as below;

<Page
    x:Class="App120.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App120"
    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}">
    <WebView x:Name="webView"/>
  </Grid>
</Page>

and then write some code behind this to handle the switch on my GPIO pin and toggle the WebView to move around the internet a little;

namespace App120
{
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using System;
  using Windows.Devices.Gpio;
  using Windows.UI.Core;

  class GpioPinDevice
  {
    public GpioPinDevice(int pinNumber)
    {
      this.gpioPin = GpioController.GetDefault().OpenPin(pinNumber);
    }
    protected GpioPin gpioPin;
  }


  class PushButtonEventArgs : EventArgs
  {
    public PushButtonEventArgs(bool isOn)
    {
      this.IsOn = isOn;
    }
    public bool IsOn { get; set; }
  }
  class PushButton : GpioPinDevice
  {
    public event EventHandler<PushButtonEventArgs> Pushed;

    public PushButton(int pinNumber) : base(pinNumber)
    {
      this.gpioPin.ValueChanged += OnValueChanged;
    }

    void OnValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
    {
      var handlers = this.Pushed;

      if (handlers != null)
      {
        GpioPinValue value = this.gpioPin.Read();

        handlers(this, new PushButtonEventArgs(
          value == GpioPinValue.High));
      }
    }
  }


  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
      this.index = -1;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
      this.pushButton = new PushButton(27);
      this.pushButton.Pushed += OnPushed;
    }

    private void OnPushed(object sender, PushButtonEventArgs e)
    {
      if (e.IsOn)
      {
        this.Dispatcher.RunAsync(
          Windows.UI.Core.CoreDispatcherPriority.Normal,
          () =>
          {
            this.index++;
            if (this.index > (webSites.Length - 1))
            {
              this.index = 0;
            }
            this.webView.Navigate(new Uri(webSites[this.index], UriKind.Absolute));
          }
        );
      }
    }

    static string[] webSites = {
      "http://www.microsoft.com",
      "http://www.surface.com",
      "http://www.windows.com"
    };
    PushButton pushButton;
    int index;
  }
}

and that all seems to run quite nicely as you can see below;

Please ignore the additional wires and bits on the breadboard, I didn’t want to unplug all that stuff just to get a switch working! Smile