Shadows in Windows (UWP) XAML Applications – Part 2 – DropShadow

Following Part 1 – ThemeShadow (and Part 1b – Lists) in this post we’re going to look at a very simple example of creating your own shadow. I’m going to reuse my simple example of two overlapping rectangles.

The goal is to:

  • Add a shadow around the bottom-left rectangle
  • The shadow should elevate the rectangle off the background
  • The shadow should elevate the rectangle away from the other rectangle
  • The shadow should handle changing the corner radius to allow for rounded corners
  • The shadow should handle changes to the system theme (i.e. dark mode)

Ok, so let’s see how we can achieve this by creating our own DropShadow. As with the ThemeShadow there are two elements that participate in the creation of the shadow effect. There’s the item casting the shadow (in this case Rectangle2) and then there’s the surface where the shadow needs to be rendered. In this case, since we want the shadow to appear around the edge of Rectangle2, we’re going to create an additional Grid (named Host in the following XAML) that matches the size of Rectangle2:

<Grid Height="400"
        Width="400"
        Loaded="Grid_Loaded"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle x:Name="Rectangle1"
                Margin="50"
                Height="200"
                Width="200"
                Fill="Turquoise"
                VerticalAlignment="Top"
                HorizontalAlignment="Right" />
    <Grid Margin="50"
            Height="200"
            Width="200"
            VerticalAlignment="Bottom"
            HorizontalAlignment="Left">
        <Grid x:Name="Host" />
        <Rectangle x:Name="Rectangle2"
                    Fill="Turquoise" />
    </Grid>
</Grid>

To create and apply the shadow we’re handling the Loaded event on the parent Grid. The following logic creates the DropShadow, uses Rectangle2 as a mask and then attaches the DropShadow to the SpriteVisual for the Host Grid.

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    var shadowColor = (Resources["ApplicationForegroundThemeBrush"] as SolidColorBrush).Color;
    var compositor = ElementCompositionPreview.GetElementVisual(Host).Compositor;
            
    // Create the drop shadow
    var dropShadow = compositor.CreateDropShadow();
    dropShadow.Color = shadowColor;
    dropShadow.BlurRadius = 16;
    dropShadow.Opacity = 20.0f;

    // Use the shape of the element (in this case Rectangle2) to 
    // control shape of shadow
    var mask = Rectangle2.GetAlphaMask();
    dropShadow.Mask = mask;
            
    // Set the shadow on the visual
    var spriteVisual = compositor.CreateSpriteVisual();
    spriteVisual.Size = new Vector2((float)Host.ActualWidth, (float)Host.ActualHeight);
    spriteVisual.Shadow = dropShadow;
    ElementCompositionPreview.SetElementChildVisual(Host, spriteVisual);
}

The result of this is shown in the following image

Now let’s try rounding the corners of Rectangle2

<Rectangle x:Name="Rectangle2"
            Fill="Turquoise"
            RadiusX="40"
            RadiusY="40" />

Here’s how it looks

This is looking really nice but what about the dark mode support?

Even in dark mode, we’re still seeing the shadow being cast both against the background but also on Rectangle1. Going back to the XAML and code that creates this effect, it’s important to note that the background is set using the ApplicationPageBackgroundThemeBrush and that the color that’s set for the shadow is based on the ApplicationForegroundThemeBrush. Both these brushes are theme aware, meaning that as the device switches between light and dark mode, the brushes have the appropriate color. This means that there’s little else we need to do in order to support dark mode when creating the shadow.

The upshot of creating a shadow this way is that it’s relatively straight forward but does require custom logic to be written – this is less than ideal if you want to apply shadows easily in xaml for example.

In subsequent posts we’ll look at other options for applying shadows that hopefully will mean less custom code.

Leave a comment