Encapsulate a AcrylicBrush for UWP in Creators Update

Finally we can achieve this effect by using HostBackdrop brush.

In the upcoming Fall Creators Update, we can use a special Brush named AcrylicBrush that takes care of all the work and used in a UIElement's background.

    <Page.Resources>
    	<AcrylicBrush x:Key="AppBackgroundBrush"/>
    </Page.Resources>
    <Grid Background="{StaticResource AppBackgroundBrush}"/>

This doesn't mean that you can't achieve this effect in Creators Update. In fact, since Anniversary Update, you have the Backdrop brush to sample the in-app content and blur it.

And finally in Creators Update, you can sample the content behind the app window and then set as background, which looks like the app window is transparent(which is NOT).

How to implement it?

Now I will talk about how to Encapsulate a AcrylicBrush.

First of all, you must ensure your project is targeted at Creators Update SDK(Build 15063).

We first build a CompositionBrushBuilder that helps create CompositionEffectBrush which can be applied as a brush in SpriteVisual. The builder pattern is common in Android since I mainly work on Android app.

        public enum BackdropBrushType
        {
            Backdrop,
            HostBackdrop
        }
    
        public class CompositionBrushBuilder
        {
            private const string SOURCE_KEY = "Source";
    
            private float _backdropFactor = 0.5f;
            private float _tintColorFactor = 0.5f;
            private float _blurAmount = 2f;
            private Color _tintColor = Colors.Black;
            private BackdropBrushType _brushType = BackdropBrushType.Backdrop;
    
            public CompositionBrushBuilder(BackdropBrushType type)
            {
                _brushType = type;
            }
    
            public CompositionBrushBuilder SetBackdropFactor(float factor)
            {
                _backdropFactor = factor;
                return this;
            }
    
            public CompositionBrushBuilder SetTintColorFactor(float factor)
            {
                _tintColorFactor = factor;
                return this;
            }
    
            public CompositionBrushBuilder SetTintColor(Color color)
            {
                _tintColor = color;
                return this;
            }
    
            public CompositionBrushBuilder SetBlurAmount(float blur)
            {
                _blurAmount = blur;
                return this;
            }
    
            private CompositionEffectBrush CreateBlurEffect(Compositor compositor)
            {
                var effect = new GaussianBlurEffect()
                {
                    BlurAmount = _blurAmount,
                    BorderMode = EffectBorderMode.Soft,
                    Source = new ArithmeticCompositeEffect()
                    {
                        MultiplyAmount = 0,
                        Source1Amount = _backdropFactor,
                        Source2Amount = _tintColorFactor,
                        Source1 = new CompositionEffectSourceParameter(SOURCE_KEY),
                        Source2 = new ColorSourceEffect()
                        {
                            Color = _tintColor
                        }
                    }
                };
    
                var effectFactory = compositor.CreateEffectFactory(effect);
                var effectBrush = effectFactory.CreateBrush();
                return effectBrush;
            }
    
            public CompositionEffectBrush Build(Compositor compositor)
            {
                var effectBrush = CreateBlurEffect(compositor);
                CompositionBackdropBrush backdropBrush;
                switch (_brushType)
                {
                    case BackdropBrushType.Backdrop:
                        backdropBrush = compositor.CreateBackdropBrush();
                        break;
                    case BackdropBrushType.HostBackdrop:
                    default:
                        backdropBrush = compositor.CreateHostBackdropBrush();
                        break;
                }
                effectBrush.SetSourceParameter(SOURCE_KEY, backdropBrush);
                return effectBrush;
            }
        }

Then we make a AcrylicBrushBase that inherits from XamlCompositionBrushBase.

There are a few properties we are interested in customizing:

            public Color TintColor
            {
                get { return (Color)GetValue(TintColorProperty); }
                set { SetValue(TintColorProperty, value); }
            }
    
            public static readonly DependencyProperty TintColorProperty =
                DependencyProperty.Register("TintColor", typeof(Color), typeof(AcrylicBrushBase),
                    new PropertyMetadata(null));
    
            public double BackdropFactor
            {
                get { return (double)GetValue(BackdropFactorProperty); }
                set { SetValue(BackdropFactorProperty, value); }
            }
    
            public static readonly DependencyProperty BackdropFactorProperty =
                DependencyProperty.Register("BackdropFactor", typeof(double), typeof(AcrylicBrushBase),
                    new PropertyMetadata(0.5d));
    
            public double TintColorFactor
            {
                get { return (double)GetValue(TintColorFactorProperty); }
                set { SetValue(TintColorFactorProperty, value); }
            }
    
            public static readonly DependencyProperty TintColorFactorProperty =
                DependencyProperty.Register("TintColorFactor", typeof(double), typeof(AcrylicBrushBase),
                    new PropertyMetadata(0.5d));
    
            public double BlurAmount
            {
                get { return (double)GetValue(BlurAmountProperty); }
                set { SetValue(BlurAmountProperty, value); }
            }
    
            public static readonly DependencyProperty BlurAmountProperty =
                DependencyProperty.Register("BlurAmount", typeof(double), typeof(AcrylicBrushBase),
                    new PropertyMetadata(2d));

And there are two fields which store the Compositor and the Brush we created for dispose purpose later.

    protected Compositor _compositor;
    protected CompositionEffectBrush _brush;

Override the OnConnected() and OnDisconnected() methods:

            protected override void OnConnected()
            {
                base.OnConnected();
                _compositor = Window.Current.Content.GetVisual().Compositor;
                _brush = new CompositionBrushBuilder(GetBrushType()).SetTintColor(TintColor)
                    .SetBackdropFactor((float)BackdropFactor)
                    .SetTintColorFactor((float)TintColorFactor)
                    .SetBlurAmount((float)BlurAmount)
                    .Build(_compositor);
                CompositionBrush = _brush;
            }
    
            protected override void OnDisconnected()
            {
                base.OnDisconnected();
                _brush.Dispose();
                _compositor.Dispose();
            }

You may notice that CompositionBrushBuilder receives a BackdropBrushType indicating what kind of Backdrop it should create. The difference between Backdrop and HostBackdrop is:

  • Backdrop samples content in app;
  • HostBackdrop samples content behind the window;

Hence, provide a method to indicate which backdrop it should create:

    protected abstract BackdropBrushType GetBrushType();

Subclass should implement this method:

        public class AcrylicHostBrush : AcrylicBrushBase
        {
            public AcrylicHostBrush()
            {
            }
    
            protected override BackdropBrushType GetBrushType()
            {
                return BackdropBrushType.HostBackdrop;
            }
        }

How to use?

That's quite simple, first declare the brush as a resource:

      <backdrop:AcrylicHostBrush x:Key="AppHostBackgroundBrush" 
                                   TintColor="#1B1B1B" 
                                   BlurAmount="20"/>

Then use it as a brush to background property:

    <Grid x:Name="RootGrid" Background="{StaticResource AppHostBackgroundBrush}"/>

You can find all the code above here.

That's all!