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.

One of the many interesting projects we work on here at CustomMayd are the collaborations with the Microsoft Learning Team in the creation of developer training courses. 

As today is the start of a new quarter, the latest round of courses have been published:

The process of developing a course with Microsoft is an interesting one. It usually starts with a high-level “one liner” description such as ‘We want to help Windows Forms developers refresh their legacy applications’. We usually then engage in an iterative design phase where we consider a broader set of objectives that align with the design goal and the constraints of the format - it is typical for these courses to contain 4 modules and target 10-15 hours of activity for the learner.

Once we have sign-off on the design, we then start work on the detailed design. This usually entails the creation of a detailed course outline, demo apps and other resources with the intent of confirming the feasibility of delivering the objectives within the timeline and budget. The outcome of this stage is a detailed design.

Once that is signed off, we commence the creation of the modules. Our training materials are typically detailed hands-on style labs with a lot of detailed commentary explain not only “what” is to be done, but also “why” – best practices, etc. as well as collating external references for the learner that wishes to dive deeper into the subject matter. As we proceed through the labs, we take snapshots of source code, etc. so that a learner can easily detect any errors they may have made, etc. We create the courses using MarkDown and our preferred editor is Visual Studio Code.

2018-04-02 11_07_32-03-Connect App to Azure SQL - DEV332x_Extend_Your_Application_with_R 

Once the labs are complete, we usually record a number of supporting videos – either as screen captures or in the Microsoft recording studios as “talking heads”.

2018-04-02 11_09_56-Anatomy of a Module _ Module Syntax _ DEV314x Courseware _ edXPrescriptionManagerVideo

Recording the videos in the studio is usually entertaining – we tend to batch them into a number of days which can get pretty exhausting as well!

Take a look at the courses and let us know if you or your organization would like some custom training created. We actually have many more courses published on as well as Channel 9 and Microsoft Virtual Academy:

Channel 9

Microsoft Virtual Academy