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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s