Skip to content

Commit 0f1c9b7

Browse files
committed
Added the ability to control which edges the shadows are rendered on. Typically this is not an issue as you can simly lay another control over the top, but there are cases where it would be far simpler to simply have greater control over the shadow.
1 parent 092f551 commit 0f1c9b7

9 files changed

+177
-40
lines changed

MaterialDesignThemes.Wpf/Converters/ShadowConverter.cs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,19 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Globalization;
4-
using System.Linq;
5-
using System.Text;
6-
using System.Threading.Tasks;
7-
using System.Windows;
83
using System.Windows.Data;
94
using System.Windows.Media.Effects;
105

116
namespace MaterialDesignThemes.Wpf.Converters
127
{
138
public class ShadowConverter : IValueConverter
149
{
15-
private static readonly IDictionary<ShadowDepth, DropShadowEffect> ShadowsDictionary;
1610
public static readonly ShadowConverter Instance = new ShadowConverter();
1711

18-
static ShadowConverter()
19-
{
20-
var resourceDictionary = new ResourceDictionary { Source = new Uri("pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml", UriKind.Absolute) };
21-
22-
ShadowsDictionary = new Dictionary<ShadowDepth, DropShadowEffect>
23-
{
24-
{ ShadowDepth.Depth0, null },
25-
{ ShadowDepth.Depth1, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth1"] },
26-
{ ShadowDepth.Depth2, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth2"] },
27-
{ ShadowDepth.Depth3, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth3"] },
28-
{ ShadowDepth.Depth4, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth4"] },
29-
{ ShadowDepth.Depth5, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth5"] },
30-
};
31-
}
32-
3312
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
3413
{
3514
if (!(value is ShadowDepth)) return null;
3615

37-
return Clone(ShadowsDictionary[(ShadowDepth) value]);
16+
return Clone(ShadowInfo.GetDropShadow((ShadowDepth) value));
3817
}
3918

4019
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows;
4+
using System.Windows.Data;
5+
using System.Windows.Media;
6+
using System.Windows.Media.Effects;
7+
8+
namespace MaterialDesignThemes.Wpf.Converters
9+
{
10+
public class ShadowEdgeConverter : IMultiValueConverter
11+
{
12+
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
13+
{
14+
if (values?.Length != 4)
15+
{
16+
return Binding.DoNothing;
17+
}
18+
19+
if (!(values[0] is double) || !(values[1] is double) || !(values[2] is ShadowDepth) ||
20+
!(values[3] is ShadowEdges))
21+
{
22+
return Binding.DoNothing;
23+
}
24+
25+
double width = (double)values[0];
26+
double height = (double)values[1];
27+
if (double.IsNaN(width) || double.IsInfinity(width) || double.IsNaN(height) || double.IsInfinity(height))
28+
{
29+
return Binding.DoNothing;
30+
}
31+
DropShadowEffect dropShadow = ShadowInfo.GetDropShadow((ShadowDepth)values[2]);
32+
if (dropShadow == null)
33+
{
34+
return Binding.DoNothing;
35+
}
36+
37+
ShadowEdges edges = (ShadowEdges)values[3];
38+
double blurRadius = dropShadow.BlurRadius;
39+
40+
var rect = new Rect(0, 0, width, height);
41+
42+
if (edges.HasFlag(ShadowEdges.Left))
43+
{
44+
rect = new Rect(rect.X - blurRadius, rect.Y, rect.Width + blurRadius, rect.Height);
45+
}
46+
if (edges.HasFlag(ShadowEdges.Top))
47+
{
48+
rect = new Rect(rect.X, rect.Y - blurRadius, rect.Width, rect.Height + blurRadius);
49+
}
50+
if (edges.HasFlag(ShadowEdges.Right))
51+
{
52+
rect = new Rect(rect.X, rect.Y, rect.Width + blurRadius, rect.Height);
53+
}
54+
if (edges.HasFlag(ShadowEdges.Bottom))
55+
{
56+
rect = new Rect(rect.X, rect.Y, rect.Width, rect.Height + blurRadius);
57+
}
58+
59+
var size = new GeometryDrawing(new SolidColorBrush(Colors.White), new Pen(), new RectangleGeometry(rect));
60+
return new DrawingBrush(size)
61+
{
62+
Stretch = Stretch.None,
63+
TileMode = TileMode.None,
64+
Viewport = rect,
65+
ViewportUnits = BrushMappingMode.Absolute,
66+
Viewbox = rect,
67+
ViewboxUnits = BrushMappingMode.Absolute
68+
};
69+
}
70+
71+
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
72+
{
73+
throw new NotImplementedException();
74+
}
75+
}
76+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Windows;
4+
using System.Windows.Media.Effects;
5+
6+
namespace MaterialDesignThemes.Wpf.Converters
7+
{
8+
internal static class ShadowInfo
9+
{
10+
private static readonly IDictionary<ShadowDepth, DropShadowEffect> ShadowsDictionary;
11+
12+
static ShadowInfo()
13+
{
14+
var resourceDictionary = new ResourceDictionary { Source = new Uri("pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml", UriKind.Absolute) };
15+
16+
ShadowsDictionary = new Dictionary<ShadowDepth, DropShadowEffect>
17+
{
18+
{ ShadowDepth.Depth0, null },
19+
{ ShadowDepth.Depth1, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth1"] },
20+
{ ShadowDepth.Depth2, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth2"] },
21+
{ ShadowDepth.Depth3, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth3"] },
22+
{ ShadowDepth.Depth4, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth4"] },
23+
{ ShadowDepth.Depth5, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth5"] },
24+
};
25+
}
26+
27+
public static DropShadowEffect GetDropShadow(ShadowDepth depth)
28+
{
29+
return ShadowsDictionary[depth];
30+
}
31+
}
32+
}

MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@
292292
<Compile Include="Converters\NotZeroToVisibilityConverter.cs" />
293293
<Compile Include="Converters\PointValueConverter.cs" />
294294
<Compile Include="Converters\ShadowConverter.cs" />
295+
<Compile Include="Converters\ShadowEdgeConverter.cs" />
296+
<Compile Include="Converters\ShadowInfo.cs" />
295297
<Compile Include="Converters\SizeToRectConverter.cs" />
296298
<Compile Include="Converters\SnackbarMessageTypeConverter.cs" />
297299
<Compile Include="Converters\TimeToVisibilityConverter.cs" />

MaterialDesignThemes.Wpf/ShadowAssist.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Windows.Media;
44
using System.Windows.Media.Animation;
55
using System.Windows.Media.Effects;
6-
using System.Windows.Navigation;
76

87
namespace MaterialDesignThemes.Wpf
98
{
@@ -17,6 +16,17 @@ public enum ShadowDepth
1716
Depth5
1817
}
1918

19+
[Flags]
20+
public enum ShadowEdges
21+
{
22+
None = 0,
23+
Left = 1,
24+
Top = 2,
25+
Right = 4,
26+
Bottom = 8,
27+
All = Left | Top | Right | Bottom
28+
}
29+
2030
internal class ShadowLocalInfo
2131
{
2232
public ShadowLocalInfo(double standardOpacity)
@@ -111,5 +121,17 @@ public static CacheMode GetCacheMode(DependencyObject element)
111121
return (CacheMode)element.GetValue(CacheModeProperty);
112122
}
113123

124+
public static readonly DependencyProperty ShadowEdgesProperty = DependencyProperty.RegisterAttached(
125+
"ShadowEdges", typeof(ShadowEdges), typeof(ShadowAssist), new PropertyMetadata(ShadowEdges.All));
126+
127+
public static void SetShadowEdges(DependencyObject element, ShadowEdges value)
128+
{
129+
element.SetValue(ShadowEdgesProperty, value);
130+
}
131+
132+
public static ShadowEdges GetShadowEdges(DependencyObject element)
133+
{
134+
return (ShadowEdges) element.GetValue(ShadowEdgesProperty);
135+
}
114136
}
115137
}

MaterialDesignThemes.Wpf/Themes/Generic.xaml

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.SmartHint.xaml" />
3030
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Snackbar.xaml" />
3131
</ResourceDictionary.MergedDictionaries>
32-
32+
3333
<!-- set up default styles for our custom Material Design in XAML Toolkit controls -->
3434
<Style TargetType="{x:Type local:Clock}" BasedOn="{StaticResource MaterialDesignClock}" />
3535
<Style TargetType="{x:Type local:PopupBox}" BasedOn="{StaticResource MaterialDesignPopupBox}" />
3636
<Style TargetType="{x:Type local:TimePicker}" BasedOn="{StaticResource MaterialDesignTimePicker}" />
37-
37+
3838
<converters:BrushToRadialGradientBrushConverter x:Key="BrushToRadialGradientBrushConverter" />
3939
<converters:DrawerOffsetConverter x:Key="DrawerOffsetConverter" />
4040

@@ -46,7 +46,7 @@
4646
<Setter Property="VerticalAlignment" Value="Stretch" />
4747
<Setter Property="Background" Value="Transparent" />
4848
<Setter Property="IsTabStop" Value="False" />
49-
<Setter Property="ClipToBounds" Value="{Binding RelativeSource={RelativeSource Self}, Path=(local:RippleAssist.ClipToBounds)}" />
49+
<Setter Property="ClipToBounds" Value="{Binding RelativeSource={RelativeSource Self}, Path=(local:RippleAssist.ClipToBounds)}" />
5050
<Setter Property="Feedback" Value="{Binding RelativeSource={RelativeSource Self}, Path=(local:RippleAssist.Feedback)}" />
5151
<Setter Property="Template">
5252
<Setter.Value>
@@ -120,10 +120,10 @@
120120
<SineEase EasingMode="EaseOut" />
121121
</EasingDoubleKeyFrame.EasingFunction>
122122
</EasingDoubleKeyFrame>
123-
</DoubleAnimationUsingKeyFrames>
123+
</DoubleAnimationUsingKeyFrames>
124124
</Storyboard>
125-
</VisualTransition>
126-
</VisualStateGroup.Transitions>
125+
</VisualTransition>
126+
</VisualStateGroup.Transitions>
127127
<VisualState x:Name="Normal">
128128
<Storyboard>
129129
<DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="ScaleTransform" To="0"/>
@@ -179,7 +179,7 @@
179179
</Setter.Value>
180180
</Setter>
181181
</Style>
182-
182+
183183
<Style TargetType="{x:Type local:Underline}">
184184
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}"/>
185185
<Setter Property="SnapsToDevicePixels" Value="True"/>
@@ -404,27 +404,38 @@
404404
</Style>
405405

406406
<Style TargetType="{x:Type local:ColorZone}">
407+
<Style.Resources>
408+
<converters:ShadowEdgeConverter x:Key="ShadowEdgeConverter" />
409+
</Style.Resources>
407410
<Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}" />
408411
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" />
409412
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
410413
<Setter Property="VerticalContentAlignment" Value="Stretch" />
411414
<Setter Property="VerticalAlignment" Value="Top" />
412-
<Setter Property="IsTabStop" Value="False" />
415+
<Setter Property="IsTabStop" Value="False" />
413416
<Setter Property="Template">
414417
<Setter.Value>
415418
<ControlTemplate TargetType="{x:Type local:ColorZone}">
416419
<Grid Background="Transparent">
420+
<Grid.OpacityMask>
421+
<MultiBinding Converter="{StaticResource ShadowEdgeConverter}">
422+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualWidth"/>
423+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight"/>
424+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(local:ShadowAssist.ShadowDepth)" />
425+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(local:ShadowAssist.ShadowEdges)" />
426+
</MultiBinding>
427+
</Grid.OpacityMask>
417428
<AdornerDecorator CacheMode="{Binding RelativeSource={RelativeSource Self}, Path=(local:ShadowAssist.CacheMode)}">
418429
<Border Background="{TemplateBinding Background}"
419-
CornerRadius="{TemplateBinding CornerRadius}"
430+
CornerRadius="{TemplateBinding CornerRadius}"
420431
Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}">
421432
</Border>
422433
</AdornerDecorator>
423434
<Border Background="{TemplateBinding Background}"
424435
BorderBrush="{TemplateBinding BorderBrush}"
425436
BorderThickness="{TemplateBinding BorderThickness}"
426437
CornerRadius="{TemplateBinding CornerRadius}"
427-
ClipToBounds="True" >
438+
ClipToBounds="True">
428439
<ContentPresenter Content="{TemplateBinding Content}"
429440
ContentTemplate="{TemplateBinding ContentTemplate}"
430441
TextElement.Foreground="{TemplateBinding Foreground}"
@@ -988,7 +999,7 @@
988999
</ControlTemplate>
9891000
</Setter.Value>
9901001
</Setter>
991-
</Style>
1002+
</Style>
9921003

9931004
<Style TargetType="{x:Type local:PackIcon}">
9941005
<Setter Property="Height" Value="16" />
@@ -1015,7 +1026,7 @@
10151026
</Setter>
10161027
</Style>
10171028

1018-
<Style TargetType="{x:Type transitions:Transitioner}">
1029+
<Style TargetType="{x:Type transitions:Transitioner}">
10191030
<Setter Property="ClipToBounds" Value="True" />
10201031
<Setter Property="ItemsPanel">
10211032
<Setter.Value>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Card.xaml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,34 @@
1010
<converters:CardClipConverter x:Key="CardClipConverter" />
1111

1212
<ControlTemplate TargetType="{x:Type wpf:Card}" x:Key="CardTemplate">
13+
<ControlTemplate.Resources>
14+
<converters:ShadowEdgeConverter x:Key="ShadowEdgeConverter" />
15+
</ControlTemplate.Resources>
1316
<Grid Margin="{TemplateBinding Margin}" Background="Transparent">
17+
<Grid.OpacityMask>
18+
<MultiBinding Converter="{StaticResource ShadowEdgeConverter}">
19+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualWidth"/>
20+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight"/>
21+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowDepth)" />
22+
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowEdges)" />
23+
</MultiBinding>
24+
</Grid.OpacityMask>
1425
<AdornerDecorator CacheMode="{Binding RelativeSource={RelativeSource Self}, Path=(wpf:ShadowAssist.CacheMode)}">
1526
<Border Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"
1627
CornerRadius="{TemplateBinding UniformCornerRadius}">
1728
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"
1829
x:Name="PART_ClipBorder"
19-
Clip="{TemplateBinding ContentClip}" />
30+
Clip="{TemplateBinding ContentClip}" />
2031
</Border>
2132
</AdornerDecorator>
2233
<ContentPresenter
23-
x:Name="ContentPresenter"
34+
x:Name="ContentPresenter"
2435
Margin="{TemplateBinding Padding}"
2536
Clip="{TemplateBinding ContentClip}"
2637
Content="{TemplateBinding ContentControl.Content}"
2738
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
2839
ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"
29-
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}">
40+
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}">
3041
</ContentPresenter>
3142
</Grid>
3243
</ControlTemplate>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.GroupBox.xaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
<Grid>
3535
<Border Background="{TemplateBinding Background}" BorderBrush="{Binding Path=Background, ElementName=PART_ColorZone}" BorderThickness="{TemplateBinding BorderThickness}" />
3636
<DockPanel Background="{TemplateBinding Background}">
37-
<wpf:ColorZone UseLayoutRounding="True" x:Name="PART_ColorZone" DockPanel.Dock="Top" Padding="{TemplateBinding Padding}" Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}" Mode="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ColorZoneAssist.Mode)}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
37+
<wpf:ColorZone UseLayoutRounding="True" x:Name="PART_ColorZone" DockPanel.Dock="Top" Padding="{TemplateBinding Padding}"
38+
Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"
39+
wpf:ShadowAssist.ShadowEdges="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowEdges)}"
40+
Mode="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ColorZoneAssist.Mode)}"
41+
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
3842
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
3943
ContentStringFormat="{TemplateBinding HeaderStringFormat}"
4044
ContentTemplate="{TemplateBinding HeaderTemplate}"

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Shadows.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
44

5-
<!-- thanks: http://marcangers.com/material-design-shadows-in-wpf/ -->
5+
<!-- thanks: http://marcangers.com/material-design-shadows-in-wpf/ -->
66

77
<Color x:Key="MaterialDesignShadow">#AA000000</Color>
88
<SolidColorBrush x:Key="MaterialDesignShadowBrush" Color="{StaticResource MaterialDesignShadow}"/>

0 commit comments

Comments
 (0)