How to create a shadow drop effect around a wpf button like the google button
In the past, I implemented material elevation like this:
public static class UI
{
private static readonly DropShadowEffect[] sShadows = new[]
{
new DropShadowEffect()
{
BlurRadius = 5,
ShadowDepth = 1
},
new DropShadowEffect()
{
BlurRadius = 8,
ShadowDepth = 1.5
},
new DropShadowEffect()
{
BlurRadius = 14,
ShadowDepth = 4.5
},
new DropShadowEffect()
{
BlurRadius = 25,
ShadowDepth = 8
},
new DropShadowEffect()
{
BlurRadius = 35,
ShadowDepth = 13
}
};
public static readonly DependencyProperty ElevationProperty = DependencyProperty.RegisterAttached("Elevation", typeof(double), typeof(UI), new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender, null, OnElevationChanged));
public static double GetElevation(this UIElement element) => element.GetValue(ElevationProperty) as double? ?? default;
public static void SetElevation(this UIElement element, double elevation) => element.SetValue(ElevationProperty, elevation);
private static object OnElevationChanged(DependencyObject dependencyObject, object value)
{
if (dependencyObject is UIElement element && value is double elevation)
{
if (elevation == 0)
{
element.Effect = null;
}
else
{
var effect = CreateElevation(elevation, element.Effect);
if (effect != null)
element.Effect = effect;
}
}
return value;
}
private static void MixShadows(DropShadowEffect target, DropShadowEffect other, double balance)
{
target.BlurRadius = target.BlurRadius * (1 - balance) + other.BlurRadius * balance;
target.ShadowDepth = target.ShadowDepth * (1 - balance) + other.ShadowDepth * balance;
}
private static Effect CreateElevation(double elevation, Effect source)
{
if (elevation < 0 || elevation > 12)
throw new ArgumentOutOfRangeException("Elevation must be between 0 and 12.", nameof(elevation));
elevation = Math.Max(0, elevation / 12 * shadows.Length - 1);
var shadows = sShadows;
var prevIndex = (int)Math.Floor(elevation);
var index = (int)elevation;
var nextIndex = (int)Math.Ceiling(elevation);
var approx = elevation - index;
var shadow = shadows[index];
var modify = false;
if (approx != 0)
MixShadows(shadow, approx < 0 ? shadows[prevIndex] : shadows[nextIndex], Math.Abs(approx));
if (source is DropShadowEffect sourceShadow)
{
sourceShadow.BlurRadius = shadow.BlurRadius;
sourceShadow.ShadowDepth = shadow.ShadowDepth;
shadow = sourceShadow;
modify = true;
}
shadow.Direction = 270;
shadow.Color = Color.FromArgb(0xAA, 0, 0, 0);
shadow.Opacity = .42;
shadow.RenderingBias = RenderingBias.Performance;
return modify ? null : shadow;
}
}
Then,
<Button local:Elevation="2">Elevated button</Button>
Any value between 2 and 12 can be set as the elevation of any UIElement
.
You can produce a shadow drop ish effect by changing the BorderThickness some. Try something like this:
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="DarkGray" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderThickness" Value="1,1,2,2" />
</Trigger>
Note that doing it this way can mess up your layout a bit, since it changes the total width and height of your button, so other controls around it may "bump around" just a tad when hovering the button.
If you want to play around with proper drop shadow, you can add drop shadow to your button like this:
<Button>
<Button.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Direction="320" Softness="1" ShadowDepth="10" Opacity="0.5" />
</Button.BitmapEffect>
</Button>
EDIT:
As MrDosu commented, BitMapEffect is deprecated so you should probably use Effect instead.
Here's a sample using Effect:
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="DarkGray" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="3" BlurRadius="5" Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>