Category Archives: Composition API

BackDrop: a control for dynamic blur in UWP

This article presents a lightweight UWP control that creates a configurable blur and tint effect and applies it on the pixels behind it. It’s intended to be used as background of panels, flyouts and dialogs. Here’s how the control looks like in action – it’s the semitransparent red panel in the middle of the page:

Properties

I did not create the control myself, all credits should go the team that built the Composition API Samples on GitHub. When you open the menu of the sample gallery app, you immediately notice the SplitView Pane’s background. Mainly because it is freshly green, but also because it blurs whatever’s underneath it:

SplitViewMenu

When diving into the source code, I discovered the BackDrop control and immediately decided to give it a spin.

The BackDrop control is XAML-wise a very lightweight control: it derives from Control and has no UI elements of its own – so there’s no style or template for it.

public class BackDrop : Control
{
	// ...
}

The BackDrop control uses the Composition API. The dynamic blur effect comes from a CompositionBrush that is applied to a SpriteVisual. This visual is hooked in the control’s (empty) XAML tree with SetElementChildVisual. While a ‘regular’ brush applies to the pixels of its host, this one is defined using CreateBackDropBrush. So it applies to all pixels underneath the surface of its host control:

public BackDrop()
{
    var rootVisual = ElementCompositionPreview.GetElementVisual(this);
    _compositor = rootVisual.Compositor;
    _blurVisual = _compositor.CreateSpriteVisual();

    var brush = BuildBlurBrush();
    brush.SetSourceParameter("Source", _compositor.CreateBackdropBrush());
    _blurBrush = brush;
    _blurVisual.Brush = _blurBrush;

    ElementCompositionPreview.SetElementChildVisual(this, _blurVisual);
}

The main brush is a CompositionEffectBrush that combines not two but three effects. There’s obviously a GaussianBlur and a ColorSource effect involved. On top of these there’s a Saturation effect that enhances the background color by moving the colors away from grey. Here’s a screenshot from panels with a BackDrop with (on the right) and without (on the left) the saturation effect:

SaturationEffect

[If you prefer the panel on the left: the saturation intensity is configurable, and a value of ‘1’ means ‘no effect’.]

I modified the original code of the BackDrop control to expose all relevant parameters of its brush as dependency properties, so you can two-way-bind to these through XAML and C#:

BlurAmount double Gets or sets the amount of blur to apply on the background.
TintColor Color Gets or sets the color for the ColorSource effect. The Alpha value will be ignored.
TintAlpha int Gets or sets the transparency of the TintColor.
SaturationIntensity double Gets or sets the saturation. 0 is monochrome.

Here’s how the main brush is built up, with BlurAmount, Color, and Saturation as so-called animatable properties:

private CompositionEffectBrush BuildBlurBrush()
{
    var blurEffect = new GaussianBlurEffect()
    {
        Name = "Blur",
        BlurAmount = 0.0f,
        BorderMode = EffectBorderMode.Hard,
        Optimization = EffectOptimization.Balanced,
        Source = new CompositionEffectSourceParameter("Source")
    };

    var blendEffect = new BlendEffect
    {
        Background = blurEffect,
        Foreground = new ColorSourceEffect()
        {
            Name = "Color",
            Color = Color.FromArgb(90, 255, 255, 255)
        },
        Mode = BlendEffectMode.SoftLight
    };

    var saturationEffect = new SaturationEffect
    {
        Name = "Saturation",
        Source = blendEffect,
        Saturation = 1.75f
    };

    var factory = _compositor.CreateEffectFactory(
        saturationEffect,
        new[] { "Blur.BlurAmount", "Color.Color", "Saturation.Saturation" });

    return factory.CreateBrush();
}

Here are the PropertyChangedCallback delegates that update the effect parameters:

private static void OnBlurAmountChanged(
	DependencyObject d, 
	DependencyPropertyChangedEventArgs e)
{
    var backDrop = d as BackDrop;

    if (backDrop == null) return;

    backDrop._blurBrush.Properties.InsertScalar(
	"Blur.BlurAmount", 
	(float)(double)e.NewValue);
}

private static void OnTintColorChanged(
	DependencyObject d, 
	DependencyPropertyChangedEventArgs e)
{
    var backDrop = d as BackDrop;

    if (backDrop == null) return;

    var color = (Color)e.NewValue;
    color.A = (byte)backDrop.TintAlpha;

    backDrop._blurBrush.Properties.InsertColor("Color.Color", color);
}

private static void OnTintAlphaChanged(
	DependencyObject d, 
	DependencyPropertyChangedEventArgs e)
{
    var backDrop = d as BackDrop;

    if (backDrop == null) return;

    var color = backDrop.TintColor;
    color.A = (byte)(int)e.NewValue;

    backDrop._blurBrush.Properties.InsertColor("Color.Color", color);
}

The home page of my sample app allows you to play with all of the parameters. While experimenting, don’t forget to resize the app to observe the dynamic blur effect:

Properties

The main purpose of the BackDrop control is to be used as background for different panels, e.g. a Flyout. Here’s a screenshot of the Flyout page of the sample app. This page demonstrates the impact of BackDrop control. It shows the same content in two panels: the panel on the left has a BackDrop in its XAML, the panel on the right doesn’t.

Press the button to open a real Flyout and observe a cumulated blur and tint effect:

FlyOut

Here’s part of the XAML for the fixed panel on the left. To use a BackDrop, just put the control inside a transparent container:

<Grid Background="Transparent"">
    <controls:BackDrop BlurAmount="25"
                        TintColor="BlanchedAlmond" />
    <Grid>
        <StackPanel Padding="10">
        <!-- ... -->
        </StackPanel>
    </Grid>
</Grid>

Here’s how to hook a BackDrop control in a real Flyout. Don’t forget the Style Setter that makes the background transparent:

<Flyout>
    <Flyout.FlyoutPresenterStyle>
        <Style TargetType="FlyoutPresenter">
            <Setter Property="Background"
                    Value="Transparent" />
            <Setter Property="Padding"
                    Value="0" />
        </Style>
    </Flyout.FlyoutPresenterStyle>
    <Grid Background="Transparent"
            Width="300">
        <controls:BackDrop BlurAmount="25"
                            TintColor="BlanchedAlmond" />
        <Grid>
        <!-- ... -->
        </Grid>
    </Grid>
</Flyout>

While it’s easy to use the BackDrop control as a background for panels and Flyouts, using it inside a ContentDialog is a bit more challenging. The ContentDialog control is protected, in the sense that you can’t create a template for it in Visual Studio or Blend. You’re only supposed to modify the TitleTemplate and/or the ContentTemplate:

ContentDialogTemplate

To restyle an entire ContentDialog control, you can copy its style from the main Generic.xaml (which you find in \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic) and use a modified version of it as a XAML resource:

<Page.Resources>
    <!-- Custom style for Windows.UI.Xaml.Controls.ContentDialog -->
    <!-- Gives it a transparent background and adds a BackDrop -->
    <Style TargetType="ContentDialog">
        <Setter Property="Foreground"
                Value="{ThemeResource SystemControlPageTextBaseHighBrush}" />
        <Setter Property="Background"
                Value="Transparent" />
        <!-- ... -->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentDialog">
                    <Border x:Name="Container">
                        <Grid x:Name="LayoutRoot">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <controls:BackDrop BlurAmount="15"
                                                TintColor="BurlyWood" />
                            <Border x:Name="BackgroundElement"
                                    Background="{TemplateBinding Background}"
                                    ...
                                    MinHeight="{TemplateBinding MinHeight}">
                                <Grid x:Name="DialogSpace"
                                        VerticalAlignment="Stretch"
                                        Padding="0 20">
                                <!-- ... -->
                                </Grid>
                            </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

This is the result – a modal dialog box with a blurred and tinted background:

Dialog

Of course I wanted to stress test the BackDrop control in my favorite control torture chamber: the SquareOfSquares. Here’s how a group of 21 differently configured BackDrop controls look like on a single page:

Squares

Here’s the same page on my phone:

BackDrop_Phone

When you look at the Memory Usage Diagnostic Tool in Visual Studio, you’ll notice that this page indeed consumes some extra memory, but it’s not a really significant amount of megabytes and most of it (not all!) is rapidly released. Anyway, this single page has more BackDrop controls than you’ll ever need in an app, so I assume it’s pretty safe to use it.

I didn’t feel the need to animate the effect. If you want to add your own animation (e.g. in the Loaded event), just take a look at the source page of this sample page from the Composition API Sample app:

BackDropSample

My version of the BackDrop control and its sample app live here on GitHub. The control is in its own library, for easy reuse. Make sure to have Anniversary Update or higher, and reference the Win2D UWP NuGet package.

Enjoy!

Advertisements

Building a UWP Rating Control using XAML and the Composition API

 

In this article we’ll build a XAML and C# Rating Control for the Windows Universal Platform which will be entirely drawn by the Composition API. It’s a custom Control that comes with the following dependency properties:

  • Maximum (int): the number of stars (or other images) to display,
  • StepFrequency (double): the rounding interval for the Value (a percentage, e.g. 0.25)
  • Value (double): the current value (from 0 to Maximum)
  • ItemHeight (int): height (and width) of each image in device independent pixels
  • ItemPadding (int): the number of pixels between images
  • FilledImage (uri): path to the filled image
  • EmptyImage (uri): path to the empty image
  • IsInteractive (bool): whether or not the control responds to user input (tapping or sliding)

The names of the core properties (Maximum, StepFrequency, and Value) are borrowed from the Slider class because after all –just like the slider- a Rating control is just a control to set and display a value within a range.

The Rating control’s behavior is also inspired by the slider:

  • tap on an image to set a value, and
  • slide horizontally over the control to decrease and increase the value with StepFrequency steps.

Here are some instances of the control in action:

WithoutLossOfImageQuality

An almost empty XAML template

The UI of the control is drawn entirely using the Composition API, so I kept the XAML template as simple as possible. I was tempted to use an ItemsControl as basis, but went for a Panel.. If the control were purely XAML, then a horizontal StackPanel would suffice as ControlTemplate. The star (or other) images will be displayed using the Composition API, in a layer on top of that StackPanel. This layer makes the panel itself unable to detect Tapped or ManipulationDelta events. The template contains extra Grid controls to put a ‘lid’ on the control.

The control template makes the distinction between the part that displays the images (PART_Images), and the part that deals with user input (PART_Interaction) through touch, pen, mouse or something else (like X-Box controller or Kinect – remember it’s a UWP app).

Here’s the default style definition in Themes/Generic.xaml:

<Style TargetType="local:Rating">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:Rating">
                <Grid>
                    <!-- Holds the images. -->
                    <StackPanel x:Name="PART_Items"
                                Orientation="Horizontal"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center" />
                    <!-- Interacts with touch and mouse and so. -->
                    <Grid x:Name="PART_Interaction"
                            ManipulationMode="TranslateX"
                            Background="Transparent" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The code behind

Set-up

By convention, all named elements in the style start their name with “PART_” and are decorated with a TemplatePart:

[TemplatePart(Name = ItemsPartName, Type = typeof(StackPanel))]
[TemplatePart(Name = InteractionPartName, Type = typeof(UIElement))]
public class Rating : Control
{
    private const string ItemsPartName = "PART_Items";
    private const string InteractionPartName = "PART_Interaction";

    // ...
}

All properties are defined as Dependency Property, which allows two-way binding in XAML and automatic property change notification. All of the properties also have a default value, so that the control can be used immediately without specifying mandatory property values. And last but not least, all of the properties have a PropertyChanged callback in their PropertyMetadata, so the UI of the control is updated automatically at runtime when one of the properties changes. The dependency property registrations use the nameof() expression instead of a hard-coded string [which is still in the propdp code snippet].

Here’s how the ItemHeight property is registered:

public static readonly DependencyProperty ItemHeightProperty = 
DependencyProperty.Register(
    nameof(ItemHeight),
    typeof(int),
    typeof(Rating),
    new PropertyMetadata(12, OnStructureChanged));

In the OnApplyTemplate the control is drawn by a call to OnStructureChanged – the property changed callback that also redraws the control at runtime – and the event handlers for touch interaction –Tapped and ManipulationDelta– are registered:

protected override void OnApplyTemplate()
{
    // Ensures that ActualWidth is actually the actual width.
    HorizontalAlignment = HorizontalAlignment.Left;

    OnStructureChanged(this);

    var surface = this.GetTemplateChild(InteractionPartName) as UIElement;
    if (surface != null)
    {
        surface.Tapped += Surface_Tapped;
        surface.ManipulationDelta += Surface_ManipulationDelta;
    }

    base.OnApplyTemplate();
}

The OnstructureChanged method is called when the control is rendered initially, or whenever one of the main UI characteristics is changed (things like item height or padding, maximum, ore one of the images).

The method starts with verifying if the user provided custom images. If not, a default empty and full star image is taken from the control’s own resources. To my surprise, the initialization of the default image path did not work in the dependency property registration, nor in OnApplyTemplate:

private static void OnStructureChanged(DependencyObject d)
{
    Rating c = (Rating)d;

    if (c.EmptyImage == null)
    {
        c.EmptyImage = new Uri(
	"ms-appx:///XamlBrewer.Uwp.RatingControl/Assets/defaultStar_empty.png");
    }

    if (c.FilledImage == null)
    {
        c.FilledImage = new Uri(
	"ms-appx:///XamlBrewer.Uwp.RatingControl/Assets/defaultStar_full.png");
    }

    // ...
}

The next step in OnStructureChanged is to make sure that the StepFrequency falls in the expected range, which is greater than zero but maximum one:

if ((c.StepFrequency <= 0) || (c.StepFrequency > 1))
{
    c.StepFrequency = 1;
}

Loading the images

Then it’s time to load the two images. In the current version of the Composition API you’ll need some extra help for this. My favorite helper is the Microsoft.UI.Composition.Toolkit, a small C++ project that comes with the Windows UI Dev Labs samples on GitHub:

MSUICompositionToolkit

Every image is loaded once into a CompositionSurfaceBrush that we’ll reuse for each item in the list of rating images. Here’s the code that creates the two brushes:

var panel = c.GetTemplateChild(ItemsPartName) as StackPanel;
if (panel != null)
{
    // ...

    // Load images.
    var root = panel.GetVisual();
    var compositor = root.Compositor;
    var options = new CompositionImageOptions()
    {
        DecodeWidth = c.ItemHeight,
        DecodeHeight = c.ItemHeight
    };
    var imageFactory = 
	CompositionImageFactory.CreateCompositionImageFactory(compositor);
    var image = imageFactory.CreateImageFromUri(c.EmptyImage, options);
    var emptyBrush = compositor.CreateSurfaceBrush(image.Surface);
    image = imageFactory.CreateImageFromUri(c.FilledImage, options);
    var fullBrush = compositor.CreateSurfaceBrush(image.Surface);

    // ...
}

The reason why I prefer to use the Composition Toolkit for loading images is the fact that you can control the DecodeWidth and DecodeHeight. Alternatively, you can use the C# CompositionImageLoader project, also on GitHub. It comes with a NuGet package:

CompositionImageLoader

Here’s how the code looks like when you use this library:

// Load images.
var root = panel.GetVisual();
var compositor = root.Compositor;
var imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);
var surface = imageLoader.LoadImageFromUri(c.EmptyImage);
var emptyBrush = compositor.CreateSurfaceBrush(surface);
surface = imageLoader.LoadImageFromUri(c.FilledImage);
var fullBrush = compositor.CreateSurfaceBrush(surface);

I had the intention to copy relevant code of the CompositionImageLoader into my project in order to create a full C# control with as few as possible external dependencies (only Win2D). But then I noticed a loss in image quality when using CompositionImageLoader. It looks like there’s a loss in DPI, even if you specify the size of the target image on load:

surface = imageLoader.LoadImageFromUri(
	c.FilledImage, 
	new Size(c.ItemHeight, c.ItemHeight));

Here’s a screenshot of the sample app using CompositionImageLoader:

LossOfImageQuality

And here’s the same app using Micsosoft.Composition.UI.Toolkit:

WithoutLossOfImageQuality

There’s a significant loss of quality in the devil and 3D star images. To see it, you may need to click on the screenshots to see them in full size, or try another monitor – the difference is not always obvious. Anyway, it made me hit the undo button in Source Control…

Rendering the control

The two composition surface brushes are loaded into SpriteVisual instances that are hooked to a padded Grid that is created for each item in the list of rating images. The full image will be drawn on top of the empty one. Based on the Value, we’ll calculate the clipping rectangle for each ‘full’ image. Here’s a 3D view on the structure. The yellow surface represents the StackPanel from the control’s template, the green rectangles are the root Grid elements for each image, and the images are … well … the images:

RatingStructure

At runtime, we’ll change the InsetClip values of the images on top, so the control maintains the references to these:

private List<InsetClip> Clips { get; set; } = new List<InsetClip>();

Here’s the code that creates all the layers – the full images are right-clipped at zero, so they don’t appear:

var rightPadding = c.ItemPadding;
c.Clips.Clear();

for (int i = 0; i < c.Maximum; i++)
{
    if (i == c.Maximum - 1)
    {
        rightPadding = 0;
    }

    // Create grid.
    var grid = new Grid
    {
        Height = c.ItemHeight,
        Width = c.ItemHeight,
        Margin = new Thickness(0, 0, rightPadding, 0)
    };
    panel.Children.Add(grid);
    var gridRoot = grid.GetVisual();

    // Empty image.
    var spriteVisual = compositor.CreateSpriteVisual();
    spriteVisual.Size = new Vector2(c.ItemHeight, c.ItemHeight);
    gridRoot.Children.InsertAtTop(spriteVisual);
    spriteVisual.Brush = emptyBrush;

    // Filled image.
    spriteVisual = compositor.CreateSpriteVisual();
    spriteVisual.Size = new Vector2(c.ItemHeight, c.ItemHeight);
    var clip = compositor.CreateInsetClip();
    c.Clips.Add(clip);
    spriteVisual.Clip = clip;
    gridRoot.Children.InsertAtTop(spriteVisual);
    spriteVisual.Brush = fullBrush;
}

We’re at the end of the OnstructureChanged code now. The control is rendered or re-rendered with the correct number of the correct images at the correct size and padding. It’s time to update the value:

OnValueChanged(c);

Changing the value

When the Value of the control is changed, we calculate the InsetClip for each image in the top layer (the ‘full’ stars). The images left of the value will be fully shown (clipped to the full width), the images right of the value will be hidden (clipped to zero). For the image in the middle, we calculate the number of pixels to be shown:

private static void OnValueChanged(DependencyObject d)
{
    Rating c = (Rating)d;

    var panel = c.GetTemplateChild(ItemsPartName) as StackPanel;
    if (panel != null)
    {
        for (int i = 0; i < c.Maximum; i++)
        {
            if (i <= Math.Floor(c.Value - 1))
            {
                // Filled image.
                c.Clips[i].RightInset = 0;
            }
            else if (i > Math.Ceiling(c.Value - 1))
            {
                // Empty image.
                c.Clips[i].RightInset = c.ItemHeight;
            }
            else
            {
                // Curtain.
                c.Clips[i].RightInset = 
	(float)(c.ItemHeight * (1 +  Math.Floor(c.Value) - c.Value));
            }
        }
    }
}

The images come from reusable brushes and are never reloaded at runtime, so I think that this rating control is very efficient in its resource usage.

The behavior

The Value property changes by sliding over the image. We have to round it to the nearestStepFrequency fraction. Here’s the rounding routine:

public static double RoundToFraction(double number, double fraction)
{
    // We assume that fraction is a value between 0 and 1.
    if (fraction <= 0) { return 0; }
    if (fraction > 1) { return number; }

    double modulo = number % fraction;
    if ((fraction - modulo) <= modulo)
        modulo = (fraction - modulo);
    else
        modulo *= -1;

    return number + modulo;
}

The behavior of the rating control is defined by two interactions:

  • tapping for fast initialization, and
  • sliding to adjust more precisely.

As already mentioned, the event handlers for the control’s interaction are defined on the entire control surface, not on each image. So when an image is tapped or clicked, we need to detect which one was actually hit. We then set the control to a new value which is rounded to the integer, so the whole tapped/clicked images becomes selected:

private void Surface_Tapped(object sender, TappedRoutedEventArgs e)
{
    if (!IsInteractive)
    {
        return;
    }

    Value = (int)(e.GetPosition(this).X / (ActualWidth + ItemPadding) * Maximum) + 1;
}

The calculation for deriving the Value from the the horizontal sliding manipulation is a bit more complex because we want the ‘curtain’ to closely follow the finger/pointer. We don’t change the control’s Value while sliding between the images, which creates a very natural user experience:

private void Surface_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    if (!IsInteractive)
    {
        return;
    }

    // Floor.
    var value = Math.Floor(e.Position.X / (ActualWidth + ItemPadding) * Maximum);

    // Step.
    value += Math.Min(RoundToFraction(
	((e.Position.X - (ItemHeight + ItemPadding) * (value)) / (ItemHeight)), StepFrequency), 1);

    // Keep within range.
    if (value < 0)
    {
        value = 0;
    }
    else if (value > Maximum)
    {
        value = Maximum;
    }

    Value = value;
}

Using the Rating Control

When you want to use the rating control in your app, just declare its namespace in the XAML:

xmlns:controls="using:XamlBrewer.Uwp.Controls"

Then draw a Rating control and set its properties – as already mentioned: all of the properties have a default value:

<controls:Rating x:Name="Devils"
                    Maximum="4"
                    ItemHeight="60"
                    ItemPadding="24"
                    StepFrequency=".1"
                    EmptyImage="ms-appx:///Assets/RatingImages/devil_empty.png"
                    FilledImage="ms-appx:///Assets/RatingImages/devil_full.png" />

That’s all there is.

It’s a UWP control, so it runs on PC’s, tablets, Raspberry Pi, Xbox, and Hololens. Since I don’t own all of these (yet), here’s a screenshot from the phone:

RatingOnPhone

Source code

The XAML-and-Composition Rating Control for UWP lives here on GitHub, together with the sample app.

Enjoy!

UWP Composition Effects: Chaining

The number of UWP Composition Effects is relatively limited -especially compared to the total list of Win2D effects. But you can roll your own custom effect by building an effect factory that combines two or more effects.  In this short article we’ll explain how a XAML UWP app can combine –or chain- multiple Composition Effects on a single image, and update properties on (one of) these effects dynamically.

Here are some screenshots of the corresponding sample app.

  • The image on the left shows a configurable SaturationEffect. This effect allows you adapt the saturation of the image – how far the colors are from any shade of grey. The saturation can be changed by the slider underneath.
  • The middle image shows the result of an InvertEffect that … inverts the colors of the same base image.
  • The image on the right combines the two effects:

Chaining0

Chaining1

The steps to load an image, apply any of the Composition Effects on it and display the result inside a XAML element, were explained in previous blog posts. So allow me to fast-forward through the process. The same steps are also explained on MSDN, but the sample app executes them in a totally different order. As long as you build a decent effect graph, it will display properly.

Here’s how the sample app does it:

_leftSpriteVisual = _compositor.CreateSpriteVisual();
var side = (float)Math.Min(LeftPresenter.ActualWidth, LeftPresenter.ActualHeight);
_leftSpriteVisual.Size = new Vector2(side, side);
_leftRoot.Children.InsertAtTop(_leftSpriteVisual);
// Create CompositionSurfaceBrush
_imageBrush = _compositor.CreateSurfaceBrush();

// Create an image source to load
var imageSource = _imageFactory.CreateImageFromUri(
	new Uri("ms-appx:///Assets/flowers.jpg"));
_imageBrush.Surface = imageSource.Surface;

Here’s the code for the saturation effect, with the saturation value as a dynamic property:

// Create and apply the first effect.
_firstEffect = new SaturationEffect
{
    Name = "firstEffect",
    Source = new CompositionEffectSourceParameter("source")
};
var firstEffectFactory = _compositor.CreateEffectFactory(
	_firstEffect, 
	new[] { "firstEffect.Saturation" });
_effectBrush1 = firstEffectFactory.CreateBrush();
_leftSpriteVisual.Brush = _effectBrush1;

Here’s the code for the invert effect, without any configuration:

// Create and apply the second effect.
var secondEffect = new InvertEffect
{
    Name = "secondEffect",
    Source = new CompositionEffectSourceParameter("source")
};
var secondEffectFactory = _compositor.CreateEffectFactory(secondEffect);
_effectBrush2 = secondEffectFactory.CreateBrush();
_middleSpriteVisual.Brush = _effectBrush2;

And finally, here’s the code to create the combined effect. Observe that it is just an InvertEffect that takes the SaturationEffect as Source:

// Create and apply the combined effect.
_combinedEffect = new InvertEffect
{
    Name = "chained",
    Source = _firstEffect
};
var combinedEffectFactory = _compositor.CreateEffectFactory(
	_combinedEffect, 
	new[] { "firstEffect.Saturation" });
_combinedBrush = combinedEffectFactory.CreateBrush();
_rightSpriteVisual.Brush = _combinedBrush;

Observe that we reuse the same image brush instance in all three images:

// Chain the brushes
_effectBrush1.SetSourceParameter("source", _imageBrush);
_effectBrush2.SetSourceParameter("source", _imageBrush);
_combinedBrush.SetSourceParameter("source", _imageBrush);

The image that is loaded into the first effect (the source of the combined effect) is tied to the first effect factory, NOT the combined effect factory. So you do have to load the image into the combined brush.

  • Update the dynamic properties. These are the properties that you will set on-the-fly, with or without animation. Here’s the code behind the slider:
private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    if (_effectBrush1 != null)
    {
        // Apply parameter to brushes.
        _effectBrush1.Properties.InsertScalar(
	"firstEffect.Saturation", 
	(float)e.NewValue);
        _combinedBrush.Properties.InsertScalar(
	"firstEffect.Saturation", 
	(float)e.NewValue);
    }
}

Again, the dynamic properties for the first effect brush are tied to the first effect factory, and hence do not apply to the combined effect. So you have to update the dynamic properties on the combined brush. The sample app needs to update two brushes, because it displays the two separate images.

That app lives here on GitHub.

Enjoy!

UWP Composition Effects: Temperature and Tint

In this article we discuss TemperatureAndTint, the Composition API effect that -unsurprisingly- allows you to adjust the temperature and/or tint of an image.

The Temperature property affects the balance between the blue and yellow colors in an image. Decreasing the color temperature makes an image look bluish. Increasing the temperature intensifies the green and red colors – in RGB-space this is the way to increase yellow. Here’s what happens to an image of a color wheel (image in the middle) when you decrease (image on the left) or increase (image on the right) the color temperature:

Temperature1 Temperature2 Temperature3

The Temperature property of the TemperatureAndTint effect does not stand for the color temperature of the image itself, for the relative change in color that is caused by the effect. It is defined as a float between –1 and 1.

The Tint property affects the balance between the green and magenta colors in an image. Increasing the tint increases the intensity of red and blue pixels, while decreasing increases the amount of green. Here’s the effect of changing the Tint in a color wheel image:

Tint1 Temperature2 Tint2

The Tint property is also a value in the range from -1 to 1.

What is Color Temperature?

The standard unit measure of Color Temperature is degrees Kelvin (K) -a variation of Centigrade a.k.a. degrees Celsius- and it ranges from 1000 to 10000. It was discovered in the late 1800’s at a Scottish barbecue. Physicist and mathematician Lord William Kelvin heated a block of carbon and observed its glow producing a range of different colors at different temperatures. The heated black cube first produced a dim red light, increasing to a brighter yellow as the temperature went up, and eventually produced a bright blue glow at the highest temperatures. These observations led to a color temperature scale based on a theoretical “ideal black body”.

Funny enough, the bluish light -which corresponds to the higher color temperature- is described as cool. So warm and cool refer to the color, not to the temperature.

Here’s how Kelvin’s color temperature scale looks like:

ColorTemperatures

Most computer screens and televisions have a color temperature of 6500 K. That’s why you see a blue glow through the windows when you drive by someone’s house at night.

Let’s see how we can use the notion of Color Temperature on the Universal Windows Platform, and find some use cases for it.

How to apply the effect with the Composition API

In Visual Studio, make sure that your UWP project references the Microsoft UI Composition Toolkit project, and install the Win2D UWP Nuget package. For more info on the configuration and initialization of the different Composition API citizens, read my previous article.

When we create the effect, we give it a name. When we create an EffectFactory for the effect, we parameterize the Temperature and Tint properties:

// Create the effect, but don't specify the properties yet.
var temperatureAndTintEffect = new TemperatureAndTintEffect
{
    Name = "temperatureAndtint",
    Source = new CompositionEffectSourceParameter("source")
};

// Compile the effect
var effectFactory = _compositor.CreateEffectFactory(
    temperatureAndTintEffect,
    new[] { "temperatureAndtint.Temperature", "temperatureAndtint.Tint" });

Then we create a brush and apply it to the SpriteVisual:

// Create and apply the brush.
_brush = effectFactory.CreateBrush();
_spriteVisual.Brush = _brush;

There’s no base image loaded yet, so here’s how to load one in a CompositionSurfaceBrush  and plug that brush in the effect brush:

// Create CompositionSurfaceBrush
var surfaceBrush = _compositor.CreateSurfaceBrush();

// Create an image source to load
CompositionImage imageSource = _imageFactory.CreateImageFromUri(uri);
surfaceBrush.Surface = imageSource.Surface;

_brush.SetSourceParameter("source", surfaceBrush);

When you know the values that you want to apply to the Temperature and/or Tint properties of the effect (e.g. from a Slider control or data binding), then call InsertScalar:

private void ChangeTemperature(float temperature)
{
    // Apply parameter to brush.
    _brush.Properties.InsertScalar("temperatureAndtint.Temperature", temperature);
}

private void ChangeTint(float tint)
{
    // Apply parameter to brush.
    _brush.Properties.InsertScalar("temperatureAndtint.Tint", tint);
}

warning_thumb3  Tip: You can avoid hardcoded strings by using the nameof() operator.

Some of the Composition API effect related methods require parameters in the name-of-the-effect.name-of-the-property format, like “temperatureAndTint.Temperature”. With the nameof() operator you can get to the name of a variable, type, or member at runtime. That allows you to avoid the hard-coded strings to better survive refactorings.

Here’s how you could store the Temperature parameter and use it in the EffectFactory constructor:

// Strongly typed version of the "temperatureAndtint.Temperature" string
_temperatureParameter = temperatureAndTintEffect.Name 
	+ "." 
	+ nameof(temperatureAndTintEffect.Temperature);
var effectFactory = _compositor.CreateEffectFactory(
    temperatureAndTintEffect,
    new[] { _temperatureParameter, "temperatureAndtint.Tint" });

The value was stored in a field, so it can be reused when we apply the value:

_brush.Properties.InsertScalar(_temperatureParameter, temperature);

Using Color Temperature to adjust the lighting in photos

The most common use of Color Temperature in software is white-balancing pictures. Our brain automatically adapts to different lighting colors: we perceive a white T-shirt as white whether we are indoors and using warm artificial lighting or outdoors in sunlight. Our eyes and brain recalibrate what we read as white, based on our experience and points of reference, but a camera does not. When taking a picture, the camera needs to be properly configured to the correct color temperature setting. If it’s not well done, the image needs to post-processed.

Many photo editor apps come with a slider for the Temperature in the Red-White-Blue scale, and a slider for the Tint in the Green to Magenta scale to do minor adjustments.

That’s exactly what the sample app does, so let’s move to other usages of Color Temperature.

Using Color Temperature to adapt a display to the time of day

The TemperatureAndTint effects provides you with an opportunity to make text better readable and interacting with your app more comfortable and less fatiguing. If you are reading a book, the pages will always appear white – no matter what light source you’re under. But pure white is not always the ideal background. For some apps, it makes sense to adjust the background color based on your user’s environment (indoors or outdoors) or the time of day (day or night): blue is a better background in full sunlight, and soft yellow-to-orange tones are better for reading bed-time stories. There already is a huge range of apps available that monitor the light conditions (or use history data) and adapt the screen setting accordingly. For a good example, take a look at f.lux.

Apps on the Universal Windows Platforms have no write access to screen settings and system colors. But nothing prevents you from using a pale background image in your app and changing its color temperature through the Composition API.

Here’s what happens when you load a white gradient bitmap into the sample app and change the color temperature:

White1 White2 White3

Using Color Temperature to highlight an area

The Color Temperature effect is not only useful on a white background. The following screenshots show the effect in action against a colorful (blue and red) background:

Colorful1 Colorful2 Colorful3

In the image on the left, the blue area is nicely highlighted. In the image on the right, the red area gets all the attention. So adjusting the Color Temperature (or Tint) can be used to temporarily highlight different zones in your UWP app, and this can be done with animation.

The Code

The sample app lives here on GitHub. The Assets folder in the main project has other sample images for you to experiment with: people, flowers, buildings…

Enjoy!

UWP Composition Effects: Hue Rotation

In this article we play around with the Hue Rotation effect, which is one of the UWP Composition Effects. This effect allows you to apply a rotation to all of the colors of an image in real-time. The effect takes one parameter -the rotation angle- which is a float value between 0 and 2π.

The best way to demonstrate the Hue Rotation, is to apply it on an image of a color wheel. By changing the angle of the effect, the image seems to rotate physically. Here are some screenshots of the sample app that I wrote. It applies the Hue Rotation effect on some images. The rotation angle of the effect is controlled by a slider:

ColorWheel_1

ColorWheel_2

ColorWheel_3

When you move the slider slowly from its minimum to its maximum value, you’ll see that the image appears to rotate clockwise. It looks like a RotateTransform, but we’re actually just shifting the colors of the bitmap. The color shift is not linear however: for some rotation angles the yellow range entirely disappears – like in the screenshot in the middle. That’s because in color theory nothing is linear: everything is based on non-linear physical characteristics as well as on biological ones (how the human eye works). There is an awesome and well-readable introduction to all of this right here.

So rotating colors may look easy at first sight, but it’s definitely not. The Hue Rotation effect actually applies a color matrix in the red-green-blue space. The matrix depends on the rotation angle.

Enough introduction, let’s fire up Visual Studio now.

Preparing the project

The core components of the Composition API are built into the UWP platform. On top of that, the sample app makes use of the Microsoft UI Composition Toolkit. That’s a small helper library, written in C++, that facilitates loading bitmap images into composition visuals:

CompositionToolkit

When you want to use one or more of the Composition API effects in your UWP app, then you also need to add the Win2D UWP Nuget package:

Win2D_uwp_Nuget

One line of XAML

The Composition API sits between XAML and DirecX, so XAML-wise there is not much to do: it suffices to define a host element for the image. A named Grid control will do:

<Grid x:Name="Container" Margin="0" />

Creating and applying the effect

For the C# part, we start with defining some fields in the View’s code-behind. We define

private Compositor _compositor;
private ContainerVisual _root;
private CompositionImageFactory _imageFactory;
private SpriteVisual _spriteVisual;
private CompositionEffectBrush _brush;

When the page is loaded, we initialize the infrastructure:

// Initialize Composition UI infrastructure.
_root = Container.GetVisual();
_compositor = _root.Compositor;
_imageFactory = 
	CompositionImageFactory.CreateCompositionImageFactory(_compositor);

We make use of the following extension method to create the placeholder:

public static class UIElementExtensions
{
    public static ContainerVisual GetVisual(this UIElement element)
    {
        var hostVisual = ElementCompositionPreview.GetElementVisual(element);
        var root = hostVisual.Compositor.CreateContainerVisual();
        ElementCompositionPreview.SetElementChildVisual(element, root);
        return root;
    }
}

Here’s how to create the sprite visual, and hook it into the XAML visual tree:

// Hook the sprite visual into the XAML visual tree.
_spriteVisual = _compositor.CreateSpriteVisual();
var side = (float)Math.Min(
	Presenter.ActualWidth, Presenter.ActualHeight);
_spriteVisual.Size = new Vector2(side, side);
_root.Children.InsertAtTop(_spriteVisual);

All the previous code is further explained in this article of mine on the basics of the Composition API.

Let’s now dive into the effect related code. The next couple of steps create the Hue Rotation effect:

  • We first define the effect, where we specify the name of the image brush that will be used as Source, but we omit the Angle.
  • Then we compile it with CreateEffectFactory() where we specify the parameterized properties (i.c. the Angle) in an EffectName.PropertyName syntax, and
  • create a brush that holds the effect, through CreateBrush().
  • Finally we apply that brush to the sprite visual:
// Create the effect, but don't specify the Angle yet.
var hueRotationEffect = new HueRotationEffect
{
    Name = "hueRotation",
    Source = new CompositionEffectSourceParameter("source")
};

// Compile the effect
var effectFactory = _compositor.CreateEffectFactory(
	hueRotationEffect, 
	new[] { "hueRotation.Angle" });

// Create and apply the brush.
_brush = effectFactory.CreateBrush();
_spriteVisual.Brush = _brush;

An effect needs pixels to work on, so it is always chained to a CompositionSurfaceBrush which can hold a color, a bitmap, or an effect.

Here’s how to load a bitmap into a brush:

private void LoadImage(Uri uri)
{
    // Create CompositionSurfaceBrush
    var surfaceBrush = _compositor.CreateSurfaceBrush();

    // Create an image source to load
    CompositionImage imageSource = _imageFactory.CreateImageFromUri(uri);
    surfaceBrush.Surface = imageSource.Surface;

    _brush.SetSourceParameter("source", surfaceBrush);
}

The last line of the previous snippet  –the SetSourceParameter() call- connects the effect brush to the image brush.

When we created the effect, we parameterized the hue rotation angle. This allows animation and programmatic access. When the rotation angle changes, there’s no need to reload or redraw the bitmap. We only need to apply a new value to the angle. :

private void RotateHue(float angle)
{
    // Apply parameter to brush.
    _brush.Properties.InsertScalar("hueRotation.Angle", angle);
}

We now have a nice playground to test the effect on different images.

What about black and white?

The Hue Rotation effect should not recolor black, grey, or white pixels, since these are not on the color wheel. And indeed, when we load a black and white gradient image into the sprite visual and apply the effect, nothing changes:

BlackAndWhite

A real life example

Here’s an example of where the Hue Rotation effect could be used in an app. Let’s take a look at the Color Picker from WinRT XAML Toolkit in action:

xamlcolorpicker

For a human it’s hard to find or define a color by just using sliders for the amount of red, green, and blue (RGB). Most color pickers use another standard to describe the same set of colors: HSL or HSV, where

  • H stands for Hue. This is the color’s position on the color wheel, generally expressed in degrees from 0° to 359° where 0° represents red and 180° corresponds to red’s opposite color – cyan.
  • S stands for Saturation, the purity of the color, or how far it is from any shade of grey. It is expressed as a percentage.
  • L and V stand for Lightness and Value (a.k.a Brightness). These are two different ways of specifying how far away the color is from black. Both are expressed as a percentage.

For more definitions (chroma, luma, luminance, colorfulness and so on) and more exact definitions and formulas, start reading here.

The color wheel on the outside of the color picker, allows you to easily pick the base color (the Hue). The triangle on the inside manages the two other properties: Brightness and Saturation.

If you dive into the code of the WinRT XAML Toolkit Color Picker Sample, you’ll notice that after every Hue change, the inner triangle is entirely redrawn pixel by pixel. The control  calls the RenderColorPickerSaturationLightnessTriangleCore() method in WriteableBitmapColorPickerExtensions. When the size of the control changes, the same algorithm is applied and everything is redrawn pixel by pixel.

All in all, that’s a pretty heavy operation, and I can imagine that some hardware –like a cheap phone or an IoT device- will choke on this.

This is a scenario where the Composition API and the Hue Rotation effect in particular can come to the rescue. The following set of screenshots show the impact of Hue Rotation on a monochromatic image. The main color of the image is changed but the white, black, and grey tones are unaffected, and that’s exactly what the color picker needs:

BrightnessAndSaturation_1

BrightnessAndSaturation_2

BrightnessAndSaturation_3

The image is never redrawn, only its colors are shifted. I can confirm that it runs smoothly on the phone:

HueRotationPhone

The code

The sample app is here on GitHub. It will be extended with demos of other Composition API effects.

Enjoy!

Building a custom UWP control with XAML and the Composition API

In this article we’ll build a custom UWP control that will be drawn partly by the XAML engine and partly by the Composition API. We’ll start from scratch … well almost. We’ll build yet another new version of the Modern Radial Gauge. Here’s how it will look like. The ‘old’ full-XAML gauge is on the left, the new version is on the right:

CompositionGauge_Comparison

The design of this radial gauge is timeless, thanks to Arturo Toledo. Unfortunately its implementations are not as timeless. The ‘old’ gauge goes back to early versions of Windows 8 and the continuous upgrades to more recent platforms have stretched its limits. Literally. Over the last couple of years, device screens have improved and the XAML engine was adapted to run on more and more platforms. We noticed that on some screens, the rotations and translations of the XAML elements are starting to suffer from rounding errors. As a result the radial gauge does not look crisp anymore in every size and resolution. The screenshot proves why it’s time for a new version, and why we could use some help from the Composition API:

CompositionGauge_Errors

Anatomy of a Radial Gauge

A gauge is an indicator that displays a value within a range – so it needs to have things like Minimum, Maximum, Value, and probably a Unit. A radial gauge is a gauge that looks like an analog clock – so it comes with things like a Scale, Ticks, and a Needle.

Here’s an overview of the important UI parts of the Modern Radial Gauge:

CompositionGauge_Anatomy

Here’s the list of properties that are defined in the actual control:

  • Minimum: minimum value on the scale (double, default 0)
  • Maximum: maximum value on the scale (double, default 100)
  • Value: the value to represent (double, default 0)
  • ValueStringFormat: StringFormat to apply to the displayed value (string)
  • Unit: unit measure to display (string)
  • TickSpacing: spacing -in value units- between ticks (int)
  • NeedleBrush: color of the needle (SolidColorBrush)
  • TickBrush: color of the outer ticks (SolidColorBrush)
  • ScaleWidth: thickness of the scale in pixels – relative to the control’s default size (double, default 26)
  • ScaleBrush: background color of the scale (Brush)
  • ScaleTickBrush: color of the ticks on the scale (SolidColorBrush)
  • TrailBrush: color of the trail following the needle (Brush)
  • ValueBrush: color of the value text (Brush)
  • UnitBrush: color of the unit measure text (Brush)

Using the gauge on a page should be as easy as the following one-liner:

<controls:RadialGauge Value="{Binding Temperature}" Unit="°C" />

But the user of the control should be able to fully customize the looks – like this:

<controls:RadialGauge Value="85"
                        Unit="bottles of beer on the wall"
                        TickBrush="Transparent"
                        ScaleTickBrush="Transparent"
                        NeedleBrush="{StaticResource DarkBrown}"
                        TrailBrush="{StaticResource DarkBrown}"
                        UnitBrush="{StaticResource VeryDarkBrown}"
                        ValueBrush="{StaticResource DarkBrown}"
                        Margin="4">
    <controls:RadialGauge.ScaleBrush>
        <SolidColorBrush Color="#FFFFD9AA"
                            Opacity=".6" />
    </controls:RadialGauge.ScaleBrush>
</controls:RadialGauge>

On top of that, the control is templatable: a developer can hook the gauge to a custom template (provided or not by a designer) as long as some requirements are met.

Project Structure

In Visual Studio 2015, we created a class library and added a new item of the type ‘templated control’. The RadialGauge class inherits from Control, and comes with a Style definition the Themes/Generic.xaml file. Here’s how that looks like in the IDE:

CompositionGauge_VS

Control template

The generic.xaml file is a XAML ResourceDictionary that contains the default style with the ControlTemplate for the custom control – it can be overridden. We defined the radial gauge control as a 200 by 200 pixels grid, inside a ViewBox. All internal calculations are conveniently done against that 200×200 grid, but the ViewBox will stretch the gauge to any size:

<Style TargetType="local:RadialGauge">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:RadialGauge">
                <Viewbox>
                    <Grid x:Name="PART_Container"
                            Height="200"
                            Width="200">

Here’s how to decide what elements to put in the style. For the elements that are drawn by the Composition API, it’s simple: there’s no markup available for them (yet?), so they will always be drawn in code-behind. Any rectangle that is filled with a solid color or an image is a candidate for rendering through the Composition API and hence will not appear in the control template. For the radial gauge these are the ticks inside and outside the scale, and the needle.

If you’re not into DirectX then the remaining parts will be drawn in XAML. For these elements you still have the choice. You can declare them in the style, or program them in code-behind. For the radial gauge, the XAML parts are [the border of] two ArcSegment instances and the text blocks that display the value and the unit.

Here’s a visual overview of the radial gauge parts by technology:

CompositionGauge_Parts 

We can not define the arc segment fully in XAML: the width of the background scale is configurable, and the end point of the trail depends on the current value. So both arc segments need to be drawn programmatically. The corresponding Path elements are declared in the XAML control template, but only with their color properties set – no path or dimensions. The Stroke and StrokeThickness are bound through a TemplateBinding to properties that are defined in the class itself.

Here’s the XAML load of the control template:

<!-- Scale -->
<Path Name="PART_Scale"
        Stroke="{TemplateBinding ScaleBrush}"
        StrokeThickness="{TemplateBinding ScaleWidth}" />

<!-- Trail -->
<Path Name="PART_Trail"
        Stroke="{TemplateBinding TrailBrush}"
        StrokeThickness="{TemplateBinding ScaleWidth}" />

<!-- Value and Unit -->
<StackPanel VerticalAlignment="Bottom"
            HorizontalAlignment="Center">
    <TextBlock Name="PART_ValueText"
                Foreground="{TemplateBinding ValueBrush}"
                FontSize="20"
                FontWeight="SemiBold"
                Text="{TemplateBinding Value}"
                TextAlignment="Center"
                Margin="0 0 0 2" />
    <TextBlock Foreground="{TemplateBinding UnitBrush}"
                FontSize="16"
                TextAlignment="Center"
                Text="{TemplateBinding Unit}"
                Margin="0" />
</StackPanel>

Observe that all named elements follow the PART_Xxx naming convention which is typical for templated controls.

Class Definition

The C# class definition is decorated with TemplatePart attributes for those same named elements. Again, this is just a convention. It indicates to the template (re-)designer that the code will break when these parts are not provided by the template:

[TemplatePart(Name = ScalePartName, Type = typeof(Path))]
[TemplatePart(Name = TrailPartName, Type = typeof(Path))]
[TemplatePart(Name = ValueTextPartName, Type = typeof(TextBlock))]
public class RadialGauge : Control
{

Dependency Properties

The class definition also contains the properties that are exposed by the gauge: the Minimum, Maximum and current Value, and a set of colors. These are implemented as Dependency Properties, which makes them automagically available for data binding, animation, and change notification. A dependency property registration declares a name, a type, and optionally a default value and a change event handler.

Here’s part of the Value property definition:

public static readonly DependencyProperty ValueProperty = 
DependencyProperty.Register(
    "Value",
    typeof(double),
    typeof(RadialGauge),
    new PropertyMetadata(0.0, OnValueChanged));

public double Value
{
    get { return (double)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static void OnValueChanged(DependencyObject d, 
DependencyPropertyChangedEventArgs e)
{
    // Redraw trail, rotate needle, and update value text.
    // ...
}

The rest of the properties have similar declarations. They’re all generated by the same built-in code snippet: ‘propdp’.

Initial look

At runtime the OnApplyTemplate override is the first place where we have programmatic access to the fully templated control. All XAML elements are available there. If necessary, we can now refine the initial look. We will draw the scale and the ticks, and also the needle in its initial position. For each part, we call GetTemplateChild() to get a reference to the element in the template. It’s good practice to check if the part actually exists. After all, as a custom control developer we only provide a default template. We’re never sure of the actual template that will be applied.

Here’s the code for drawing the scale – we’ll spare you the details of the ArcSegment drawing routine:

var scale = this.GetTemplateChild(ScalePartName) as Path;
if (scale != null)
{
    var pg = new PathGeometry();
    var pf = new PathFigure();
    // ArcSegment details omitted.
    // ...
    pf.Segments.Add(seg);
    pg.Figures.Add(pf);
    scale.Data = pg;
}

So far for the XAML part, let’s move to the Composition API. In that same OnApplyTemplate() method we draw the ticks outside and inside the scale, and the needle. For an introduction to drawing with the Composition API, please check this article. The code presented there draws an analog clock with ticks and hands, so it is extremely similar to the radial gauge. With the Composition API you’ll always need

Here’s the code that draws the needle. The ticks and scale ticks are drawn the same way – they’re all just colored rectangles:

var container = this.GetTemplateChild(ContainerPartName) as Grid;
_root = container.GetVisual();
_compositor = _root.Compositor;

_needle = _compositor.CreateSpriteVisual();
_needle.Size = new Vector2(NeedleWidth, NeedleHeight);
_needle.Brush = _compositor.CreateColorBrush(NeedleBrush.Color);
_needle.CenterPoint = new Vector3(NeedleWidth / 2, NeedleHeight, 0);
_needle.Offset = new Vector3(100 - NeedleWidth / 2, 100 - NeedleHeight, 0);
_root.Children.InsertAtTop(_needle);

At the end of OnApplyTemplate() the gauge is properly initialized and ready to shine.

Changing the value

When the Value property changes, it fires the event handler that was registered in the dependency property definition. That handler is a static method –as imposed by the dependency property infrastructure- so you can’t use ‘this’  in it. The reference to the gauge comes in as a parameter of the type DependencyObject, so you have to cast it. I don’t remember why I called the corresponding variable ‘c’  in the following code, but it’s actually a common practice to call it ‘_this’, or ‘that’.

Here’s the code to update needle’s rotation, draw the trail, and update the text to its new value. The pattern is always the same: get a reference to the UI element from the control template, check if it’s really there, and then update it:

private static void OnValueChanged(DependencyObject d)
{
    RadialGauge c = (RadialGauge)d;
    if (!Double.IsNaN(c.Value))
    {
        var middleOfScale = 100 - ScalePadding - c.ScaleWidth / 2;
        c.ValueAngle = c.ValueToAngle(c.Value);

        // Needle
        if (c._needle != null)
        {
            c._needle.RotationAngleInDegrees = (float)c.ValueAngle;
        }

        // Trail
        var trail = c.GetTemplateChild(TrailPartName) as Path;
        if (trail != null)
        {
            if (c.ValueAngle > MinAngle)
            {
                trail.Visibility = Visibility.Visible;
                var pg = new PathGeometry();
                var pf = new PathFigure();
                // ArcSegment drawing omitted.
                // ...
                pf.Segments.Add(seg);
                pg.Figures.Add(pf);
                trail.Data = pg;
            }
            else
            {
                trail.Visibility = Visibility.Collapsed;
            }
        }

        // Value Text
        var valueText = c.GetTemplateChild(ValueTextPartName) as TextBlock;
        if (valueText != null)
        {
            valueText.Text = c.Value.ToString(c.ValueStringFormat);
        }
    }
}

In a production version of this gauge, it would make sense to also listen to changes in other properties. If you want to use this gauge to follow up the daily evolution of a stock quote, then the Minimum and Maximum values will change at runtime. This version assumes that all properties except Value are assigned in the XAML, and do not change after that.

Cleaning up the code

In the code snippets, we omitted the beef of the calculations. I admit that in earlier versions these calculations were hard to understand, and hence hard to maintain. In this version of the gauge, we took the time to finally factor out all relevant parameters, and defined them as constants with a decent name: 

private const double MinAngle = -150.0;
private const double MaxAngle = 150.0;
private const float TickHeight = 18.0f;
private const float TickWidth = 5.0f;
private const float ScalePadding = 23.0f;
private const float ScaleTickWidth = 2.5f;
private const float NeedleWidth = 5.0f;
private const float NeedleHeight = 100.0f;

Not only does this make the calculations easier to understand and more maintainable, it also reveals good candidate dependency properties for future versions of the control. I definitely want to experiment with a scale from –180 to +90 degrees, like the ones in this car:

A5_gauges

[For the record: this is *not* my car – my speedometer stops at 280 and I have more fuel]

Testing the looks

The SquareOfSquares control is an ideal container to check the looks of the radial gauge with different settings for size, colors, and scale width. Here’s how the gauge looks like in a variety of partially random configurations:

CompositionGauge_Square

Of course it also makes sense to create a gallery with some representative looks, like this:

CompositionGauge_Gallery

You may consider the SquareOfSquares and the Gallery as a kind of ‘visual unit tests’.

Keep it Universal

Since the control is meant to be Universal, we must test it on all relevant device families. Here’s how the pages of the sample app look like on the phone:

CompositionGauge_Comparison_Phone CompositionGauge_Parts_Phone CompositionGauge_Square_Phone CompositionGauge_Gallery_Phone

Source Code

The upgraded version of the Modern Radial Gauge looks crisp again in any size and resolution. Its Visual Tree is a lot smaller, and the needle rotation is not done by the XAML engine anymore. That should result in better performance. That may be important e.g. in a dashboard with a huge amount of gauge instances. Last but not least, we identified some useful enhancements for future versions.

All source code is available. The sample app and all related controls live here on GitHub.

Enjoy!

Using the Composition API in UWP apps

In this article we’ll explore the Windows.UI.Composition API. The Composition API is a visual layer that sits between the Windows 10 XAML framework and DirectX. It gives Universal Windows Platform apps an easy access to the lower level Windows drawing stacks. The API focuses on drawing rectangles and images –with or without a XAML surface- and applying animations and effects on these. Here’s an illustration from the official documentation:

Composition_API

To discover the API, I created a sample app with different versions of a user control representing a clock. The clock is just an empty XAML element, the ticks and hands are drawn by the Composition API.

The first version of the clock is only composed of rectangles:

clock

The XAML surface of the clock control is a Canvas, wrapped in a ViewBox for easy scaling. The Canvas has a name –Container- so we can access it in the code behind:

<Viewbox Stretch="Uniform">
    <Canvas x:Name="Container"
            Background="Transparent"
            Height="200"
            Width="200">
    </Canvas>
</Viewbox>

The rest of the clock is drawn and animated through Composition.

Core classes

The core of the Composition API is the Compositor, a factory class that spawns all the drawing objects, like sprites and brushes. The clock UI is drawn as a ContainerVisual, a composite image. We need some fields or variables to hold a reference to an instance of these two classes:

private Compositor _compositor;
private ContainerVisual _root;

The clock is drawn inside a XAML Canvas. The link between any XAML element and the DirectX drawing layer is established through static methods on the ElementCompositionPreview class. I packaged these calls in a reusable extension method:

public static ContainerVisual GetVisual(this UIElement element)
{
    var hostVisual = ElementCompositionPreview.GetElementVisual(element);
    var root = hostVisual.Compositor.CreateContainerVisual();
    ElementCompositionPreview.SetElementChildVisual(element, root);
    return root;
}

Here’s how the clock control initializes the Composition layer and hooks it to its XAML Canvas (remember: Container is the name for the Canvas):

_root = Container.GetVisual();
_compositor = _root.Compositor;

All visuals are created by the _compositor, and added to the _root.

Drawing Rectangles

Visuals are just rectangles, if you want to fill the rectangle with a solid color, an image, or an effect (three types of CompositionBrush), then SpriteVisual is what you need – a Visual with a Brush. Here’s how the clock draws its twelve hour ticks. For each tick, the compositor creates a new SpriteVisual, the rectangle is properly configured (size, brush, position, center of rotation, and rotation angle), and finally it’s added to the ContainerVisual:

SpriteVisual tick;
for (int i = 0; i < 12; i++)
{
    tick = _compositor.CreateSpriteVisual();
    tick.Size = new Vector2(4.0f, 20.0f);
    tick.Brush = _compositor.CreateColorBrush(Colors.Silver);
    tick.Offset = new Vector3(98.0f, 0.0f, 0);
    tick.CenterPoint = new Vector3(2.0f, 100.0f, 0);
    tick.RotationAngleInDegrees = i * 30;
    _root.Children.InsertAtTop(tick);
}

The hour, minute, and second hands are created in exactly the same way. Here’s the code for the hour hand, its initial position is twelve-o-clock:

_hourhand = _compositor.CreateSpriteVisual();
_hourhand.Size = new Vector2(4.0f, 100.0f);
_hourhand.Brush = _compositor.CreateColorBrush(Colors.Black);
_hourhand.CenterPoint = new Vector3(2.0f, 80.0f, 0);
_hourhand.Offset = new Vector3(98.0f, 20.0f, 0);
_root.Children.InsertAtTop(_hourhand);

The hour and minute hands are not animated, we just update their rotation angle every now and then:

private void SetHoursAndMinutes()
{
    var now = DateTime.Now;
    _hourhand.RotationAngleInDegrees = (float)now.TimeOfDay.TotalHours * 30;
    _minutehand.RotationAngleInDegrees = now.Minute * 6;
}

Defining Animations

For the second hand we defined an animation on its rotation angle. We let the Compositor factory create a ScalarKeyFrameAnimation, configured it by inserting key frames and giving it a duration, an then started the animation:

var now = DateTime.Now;
var animation = _compositor.CreateScalarKeyFrameAnimation();
var seconds = (float)(int)now.TimeOfDay.TotalSeconds;
animation.InsertKeyFrame(0.00f, seconds * 6);
animation.InsertKeyFrame(1.00f, (seconds + 1) * 6);
animation.Duration = TimeSpan.FromMilliseconds(900);
// _secondhand.StartAnimation("RotationAngleInDegrees", animation);
_secondhand.StartAnimation(nameof(_secondhand.RotationAngleInDegrees), animation);

We did the calculation in the app’s code, but you may delegate some of that work to the Composition API. Every CompositionAnimation instance allows you to define different types of parameters. These parameters can be used in string expressions, such as the expressions that calculate the value of a key frame. Here’s an alternative for (part of) the previous code snippet:

animation.SetScalarParameter("start", seconds * 6);
animation.InsertExpressionKeyFrame(0.00f, "start");
animation.SetScalarParameter("delta", 6.0f);
animation.InsertExpressionKeyFrame(1.00f, "start + delta");

The animation is triggered every second by a timer, and lasts for 900 milliseconds, so the hand stops briefly at every second tick. In that short interval between the two animations, we update the rotation angle of the hour and minute hands. We do this not only because it looks nice, but also because we wanted an excuse to introduce you to the CompositionScopedBatch class.

Using a Scoped Batch

An individual animation has no Ended event. But you can create a batch with a group of animations. The batch contains all the animations that are defined between its creation and a call to its End() method. When all animations are completed, the batch raises its Completed event. In the clock control, we wrapped the second hand animation in a batch, and registered an event handler to update the hour and minute hands:

_batch = _compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
var animation = _compositor.CreateScalarKeyFrameAnimation();
// animation configuration
// ...
_secondhand.StartAnimation("RotationAngleInDegrees", animation);
_batch.End();
_batch.Completed += Batch_Completed;
private void Batch_Completed(object sender, CompositionBatchCompletedEventArgs args)
{
    _batch.Completed -= Batch_Completed;

    SetHoursAndMinutes();
}

When you run the app in Visual Studio you’ll see a fully functional clock. When you open the Live Visual Tree of the clock, you’ll see that the Container canvas has no XAML children.

Mixing XAML and Composition Visuals

Any XAML that you add inside the Canvas –declaratively or programmatically- will be displayed underneath the Composition API’s visual layer. Here’s the declaration of the Face – the circular background of the clock:

<Viewbox Stretch="Uniform">
    <Canvas x:Name="Container"
            Background="Transparent"
            Height="200"
            Width="200">
        <Ellipse x:Name="Face"
                    Height="200"
                    Width="200"
                    Canvas.Left="0"
                    Canvas.Top="0" />
    </Canvas>
</Viewbox>

And here’s how a background image is programmatically added as a XAML Image. Both Face and BackgroundImage become part of the XAML Visual Tree:

public Brush FaceColor { get; set; } = new SolidColorBrush(Colors.Transparent);
public ImageSource BackgroundImage { get; set; }

private void Clock_Loaded(object sender, RoutedEventArgs e)
{
    Face.Fill = FaceColor;

    // Composition API stuff.
    // ...

    // Add XAML element.
    if (BackgroundImage != null)
    {
        var xaml = new Image();
        xaml.Source = BackgroundImage;
        xaml.Height = 200;
        xaml.Width = 200;
        Container.Children.Add(xaml);
    }
}

So you can combine Composition API drawings and XAML elements into the same surface.

This is the declaration of the clocks on the main page:

<controls:Clock />
<controls:Clock ShowTicks="False"
                BackgroundImage="ms-appx:///Assets/modern_face.png"
                FaceColor="LightGoldenrodYellow" />

And here’s the corresponding UI:

Clock_Modern

Adding Images

A second version of the Clock control allows you to use images for the clock face and the hour and minute hands, such as these:

ClockElements

I provided some properties for the images, each with a default Uri:

public Uri FaceImage { get; set; } = new Uri("ms-appx:///Assets/roman_face.jpg");
public Uri HourHandImage { get; set; } = new Uri("ms-appx:///Assets/hour_hand.png");
public Uri MinuteHandImage { get; set; } = new Uri("ms-appx:///Assets/minute_hand.png");

When you want to work with image files in the Composition API, your project needs a reference to the Windows.UI.Composition.Toolkit. There’s no NuGet package for this [I assume that the functionality will become part of the API in the near future] so I copied the C++ source from the official Composition API Samples on GitHub.

CompositionToolkit

The toolkit comes with a CompositionImageFactory that allows you to read and decode images from a Uri, and use these as a SurfaceBrush for a SpriteVisual. Here’s the sample app code for loading the clock’s background image:

_background = _compositor.CreateSpriteVisual();
_background.Size = new Vector2(200.0f, 200.0f);
var _imageFactory = CompositionImageFactory.CreateCompositionImageFactory(_compositor);
CompositionImageOptions options = new CompositionImageOptions()
{
    DecodeWidth = 400,
    DecodeHeight = 400,
};
var _image = _imageFactory.CreateImageFromUri(FaceImage, options);
_background.Brush = _compositor.CreateSurfaceBrush(_image.Surface);
_root.Children.InsertAtTop(_background);

Here’s the code for the hour hand:

options = new CompositionImageOptions()
{
    DecodeWidth = 72,
    DecodeHeight = 240,
};

_hourhand = _compositor.CreateSpriteVisual();
_hourhand.Size = new Vector2(24.0f, 80.0f);
_image = _imageFactory.CreateImageFromUri(HourHandImage, options);
_hourhand.Brush = _compositor.CreateSurfaceBrush(_image.Surface);
_hourhand.CenterPoint = new Vector3(12.0f, 70.0f, 0);
_hourhand.Offset = new Vector3(88.0f, 30.0f, 0);
_root.Children.InsertAtTop(_hourhand);

Make sure that such images have a transparent background. Paint.NET is an excellent tool for this: click with the magic wand tool on the background, and press ‘delete’.  But of course you may also opt for PhotoShop, and a designer.

Here’s an example of two instances of the same clock control, each with a different set of images:

Clock_Classic

Adding some fun

When looking for clock images for the sample app, I came across this awesome DIY Silly Walk clock. It has four layers of images: the background, the two hands (well … legs in this case), and the body. Instead of calculating the individual positions, the decoding heights and widths, and the rotation center in code, I gave all four images the same size, and positioned them correctly.

The Silly Walk clock comes with a lot less code than the previous ones, but it brings a lot more fun. Here’s how it looks like on the PC:

Clock_SillyWalk

Here’s how all of the clocks in the sample app look like on a Windows Phone 10 (emulator):

Clock_Modern_Phone Clock_Classic_Phone Clock_SillyWalk_Phone

The Source

All of the source code, including image assets and a version of the C++ Composition Toolkit, lives here on GitHub.

Enjoy!