Office 365 recently updated on my Surface Book 2 and I noticed it has a refreshed look and feel to it and I liked that a lot! I especially like what has happened to the Ribbon:


Figure 1: Collapsed


Figure 2: Expanded

After using this for a while I switched to my desktop and HORROR it was the regular look and feel. A quick check of the versions and I saw that my desktop was on the “Semi-annual Channel” and my laptop was on the “Monthly Channel (Targeted)” – what to do? After some digging, I found the steps necessary to switch channel:

  1. Open the command prompt as an admin

  2. In order to switch the channel, enter the following:

    OfficeC2RClient.exe /changesetting Channel=Insiders
  3. In order to start the channel update, enter the following

    OfficeC2RClient.exe /update user

    The office update process should launch. Once this has completed you should be able to relaunch Outlook and see that the version has changed.


Win2D and

The Windows 10 April 2018 Update includes a new set of APIs in the Composition namespace that support various geometries such as lines, ellipses, rectangles and paths. However, during the insider cycle, it wasn’t possible to create arbitrary paths as there was a reliance on an implementation of IGeometrySource2D that could not be found. Fast forward until today when a new version of the Win2D package has been released – V1.22.0 and now we can make use of CanvasGeometry class to create our paths.

Here is quick walk-through on getting started with the Geometry APIs.

  1. Create a blank UWP app.

  2. Add the Win2D.UWP nuget package.

  3. Update the MainPage.xaml so that the Grid has the name “RootGrid”:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="RootGrid"> </Grid>
  4. Switch to the MainPage.xaml.cs code behind and update the constructor to:

    public MainPage() { InitializeComponent(); Loaded += OnLoaded; }
  5. Add the OnLoaded method:

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { var c = Window.Current.Compositor; // Need this so we can add multiple shapes to a sprite var shapeContainer = c.CreateContainerShape(); // Rounded Rectangle - just the rounded rect properties var roundedRectangle = c.CreateRoundedRectangleGeometry(); roundedRectangle.CornerRadius = new Vector2(20); roundedRectangle.Size = new Vector2(400, 300); // Need to create a sprite shape from the rounded rect var roundedRectSpriteShape = c.CreateSpriteShape(roundedRectangle); roundedRectSpriteShape.FillBrush = c.CreateColorBrush(Colors.Red); roundedRectSpriteShape.StrokeBrush = c.CreateColorBrush(Colors.Green); roundedRectSpriteShape.StrokeThickness = 5; roundedRectSpriteShape.Offset = new Vector2(20); // Now we must add that share to the container shapeContainer.Shapes.Add(roundedRectSpriteShape); // Let's create another shape var roundedRectSpriteShape2 = c.CreateSpriteShape(roundedRectangle); roundedRectSpriteShape2.FillBrush = c.CreateColorBrush(Colors.Purple); roundedRectSpriteShape2.StrokeBrush = c.CreateColorBrush(Colors.Yellow); roundedRectSpriteShape2.StrokeThickness = 3; roundedRectSpriteShape2.Offset = new Vector2(90); roundedRectSpriteShape2.CenterPoint = new Vector2(200, 150); roundedRectSpriteShape2.RotationAngleInDegrees = 45; // Add it to the container - as it is added after the previous shape, it will appear on top shapeContainer.Shapes.Add(roundedRectSpriteShape2); // Create paths and animate them SetupPathAndAnimation(c, shapeContainer); // Now we need to create a ShapeVisual and add the ShapeContainer to it. var shapeVisual = c.CreateShapeVisual(); shapeVisual.Shapes.Add(shapeContainer); shapeVisual.Size = new Vector2(1000, 1000); // Display the shapeVisual ElementCompositionPreview.SetElementChildVisual(RootGrid, shapeVisual); }
  6. Add the empty (for now) SetupPathAndAnimation:

    private static void SetupPathAndAnimation(Compositor c, CompositionContainerShape shapeContainer) { // Empty for now! }
  7. Compile and run this - you will see the following. I believe this code is pretty self-explanatory.

  8. image

  9. Now we will add a path and animate it. Add the following classes and enum to the project (these are just some helpers I created):

    using System.Collections.Generic; using System.Linq; using System.Numerics; using Microsoft.Graphics.Canvas.Geometry; namespace YourNamespace { public static class PathBuilderExtensions { public static CanvasPathBuilder BuildPathWithLines( this CanvasPathBuilder builder, IEnumerable<Vector2> vectors, CanvasFigureLoop canvasFigureLoop) { var first = true; foreach (var vector2 in vectors) { if (first) { builder.BeginFigure(vector2); first = false; } else { builder.AddLine(vector2); } } builder.EndFigure(canvasFigureLoop); return builder; } public static CanvasPathBuilder BuildPathWithLines( this CanvasPathBuilder builder, IEnumerable<(float x, float y)> nodes, CanvasFigureLoop canvasFigureLoop) { var vectors = nodes.Select(n => new Vector2(n.x, n.y)); return BuildPathWithLines(builder, vectors, canvasFigureLoop); } } public class PathNode { private Vector2 _vector2; public PathNode(Vector2 vector2) { _vector2 = vector2; } } public enum NodeType { Line, Arc, CubicBezier, Geometry, QuadraticBezier } }
  10. Replace the SetupPathAndAnimation() method with:

    private static void SetupPathAndAnimation(Compositor c, CompositionContainerShape shapeContainer) { var startPathBuilder = new CanvasPathBuilder(new CanvasDevice()); // Use my helper to create a W shaped path startPathBuilder.BuildPathWithLines(new(float x, float y)[] { (10, 10), (30, 80), (50, 30), (70, 80), (90, 10) }, CanvasFigureLoop.Open); // Add another path startPathBuilder.BuildPathWithLines(new(float x, float y)[] { (105, 30), (105, 80) }, CanvasFigureLoop.Open); // Create geometry and path that represents the start position of an animation var startGeometry = CanvasGeometry.CreatePath(startPathBuilder); var startPath = new CompositionPath(startGeometry); // Now create the end state paths var endPathBuilder = new CanvasPathBuilder(new CanvasDevice()); endPathBuilder.BuildPathWithLines(new(float x, float y)[] { (10, 10), (30, 10), (50, 10), (70, 10), (90, 10) }, CanvasFigureLoop.Open); endPathBuilder.BuildPathWithLines(new(float x, float y)[] { (105, 30), (105, 80) }, CanvasFigureLoop.Open); var endGeometry = CanvasGeometry.CreatePath(endPathBuilder); var endPath = new CompositionPath(endGeometry); // Create a CompositionPathGeometery from the Win2D GeometeryPath var pathGeometry = c.CreatePathGeometry(startPath); // Create a CompositionSpriteShape from the path var pathShape = c.CreateSpriteShape(pathGeometry); pathShape.StrokeBrush = c.CreateColorBrush(Colors.Purple); pathShape.StrokeThickness = 5; pathShape.Offset = new Vector2(20); // Add the pathShape to the ShapeContainer that we used elsewhere // This will ensure it is rendered shapeContainer.Shapes.Add(pathShape); // Create an animation using the start and endpaths var animation = c.CreatePathKeyFrameAnimation(); animation.Target = "Geometry.Path"; animation.Duration = TimeSpan.FromSeconds(1); animation.InsertKeyFrame(0, startPath); animation.InsertKeyFrame(1, endPath); animation.IterationBehavior = AnimationIterationBehavior.Forever; animation.Direction = AnimationDirection.AlternateReverse; pathGeometry.StartAnimation(nameof(pathGeometry.Path), animation); }
  11. Compile and run the code - you will notice that two paths are now being rendered and the W is animating.

  12. image

This is obviously a very simple example of using the new Composition Geometery APIs, but should be enough to get you started.