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:
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()
{
var p = Mouse.GetPosition(this);
p.Offset(-Width / 2, -Height / 2);
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;
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.