65.9K
CodeProject is changing. Read more.
Home

Creating a dynamic drop shadow

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (12 votes)

May 24, 2012

CPOL

1 min read

viewsIcon

37908

downloadIcon

961

How to create and style a container control that can be used to create completely ridiculous drop shadows on any contained control.

DynamicDropshadow test application

Introduction

This article illustrates how to create and style a container control that can be used to create completely ridiculous drop shadows on any contained control. It also shows the principles of how to create a custom WPF control. The shadows might just bring that extra bit of attention to your application or perhaps ruin it - if used on all controls.

Background

I saw a jQuery example displaying dynamic drop shadows and thought it would look much better in WPF. Therefore I decided to try it out...

The Solution

I have created a single project containing the custom WPF container control. The class MainWindow holds a DependencyProperty MousePosition. All of the container controls bind to this property for updates on the mouse position.

The class diagram:

The class diagram


Using the DynamicDropshadow control:

<Window x:Class="Dk.Nmt.Development.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:my="clr-namespace:Dk.Nmt.Development"
    Title="MainWindow" Height="449" Width="567">
  <Grid>
    <my:DynamicDropshadow Height="131" HorizontalAlignment="Left" Margin="325,63,0,0" 
        x:Name="dynamicDropshadow1" VerticalAlignment="Top" Width="178" 
        MousePosition="{Binding Path=MousePosition, RelativeSource={RelativeSource AncestorType=Window}}">
      <Label>Wrapped control goes here...</Label>
    </my:DynamicDropshadow>
...

The style of the DynamicDropshadow implements a control template that uses a border to place the drop shadow on:

<Style x:Key="DynamicDropshadowStyle" TargetType="{x:Type Development:DynamicDropshadow}">
    <Setter Property="OverridesDefaultStyle" Value="True" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="FontFamily" Value="Tahoma" />
    <Setter Property="FontSize" Value="11px" />
    <Setter Property="Padding" Value="6" />
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type Development:DynamicDropshadow}">
            <Border x:Name="PART_DropShadowBorder" CornerRadius="5" 
                Background="{TemplateBinding Background}">
                <Border.Effect>
                    <DropShadowEffect 
                        Color="{Binding ShadowColor, RelativeSource={RelativeSource TemplatedParent}}" 
                        Opacity="{Binding ShadowOpacity, RelativeSource={RelativeSource TemplatedParent}}"
                        Direction="{Binding Direction, RelativeSource={RelativeSource TemplatedParent}}" 
                        ShadowDepth="{Binding ShadowDepth, RelativeSource={RelativeSource TemplatedParent}}"
                        BlurRadius="{Binding ShadowBlur, RelativeSource={RelativeSource TemplatedParent}}"/> 
                </Border.Effect>
                <ContentPresenter x:Name="PART_DropShadowContent" 
                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            </Border>
        </ControlTemplate>
    </Setter.Value>
    </Setter>
</Style>

Calculating the dropshadow is done when the mouse is moved. The calculation is done on the basis of the direction and distance to the mouse pointer:

public void CalcDropShadow()
{
    // Get the position to the mouse, relative to the center of the control
    var p = Mouse.GetPosition(this); 
    p.Offset(-Width / 2, -Height / 2);
    // Calculate the angle to the mouse position
    if (p.X > 0 && p.Y < 0)
    Direction = Math.Atan(-p.Y / p.X) * (180f / Math.PI) - 180;
    if (p.X < 0)
    Direction = Math.Atan(-p.Y / p.X) * (180f / Math.PI);
    if (p.X > 0 && p.Y > 0)
    Direction = Math.Atan(-p.Y/p.X)*(180f/Math.PI) + 180;
    // Calculate distance, depth and blur
    double distance = Math.Sqrt(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2));
    ShadowDepth = 1 + .1 * distance;
    ShadowBlur = Math.Max(20 - .1 * distance,5); 
}

I chose to let the shadow become deeper and less blurry with the distance to the mouse.

Points of Interest

Some controls like Calendar apparently lose ClearType when effects are applied to them.

History

This is version 1.0.0.0.