Skip to content

Commit 0459a89

Browse files
Add initial Composition based AttachedDropShadow support
1 parent 2096e5d commit 0459a89

File tree

16 files changed

+561
-42
lines changed

16 files changed

+561
-42
lines changed

Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@
507507
<Compile Include="SamplePages\MetadataControl\MetadataControlPage.xaml.cs">
508508
<DependentUpon>MetadataControlPage.xaml</DependentUpon>
509509
</Compile>
510+
<Compile Include="SamplePages\Shadows\AttachedDropShadowPage.xaml.cs">
511+
<DependentUpon>AttachedDropShadowPage.xaml</DependentUpon>
512+
</Compile>
510513
<Compile Include="SamplePages\TilesBrush\TilesBrushPage.xaml.cs">
511514
<DependentUpon>TilesBrushPage.xaml</DependentUpon>
512515
</Compile>
@@ -628,6 +631,7 @@
628631
<SubType>Designer</SubType>
629632
</Content>
630633
<Content Include="SamplePages\Shadows\AttachedShadowWin2DXaml.bind" />
634+
<Content Include="SamplePages\Shadows\AttachedShadowCompositionXaml.bind" />
631635
<Content Include="SamplePages\KeyDownTriggerBehavior\KeyDownTriggerBehaviorXaml.bind" />
632636
</ItemGroup>
633637
<ItemGroup>
@@ -984,6 +988,10 @@
984988
<Content Include="SamplePages\Primitives\SwitchPresenter.bind">
985989
<SubType>Designer</SubType>
986990
</Content>
991+
<Page Include="SamplePages\Shadows\AttachedDropShadowPage.xaml">
992+
<Generator>MSBuild:Compile</Generator>
993+
<SubType>Designer</SubType>
994+
</Page>
987995
<Page Include="SamplePages\TilesBrush\TilesBrushPage.xaml">
988996
<Generator>MSBuild:Compile</Generator>
989997
<SubType>Designer</SubType>

Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public static async Task<List<SampleCategory>> GetCategoriesAsync()
6060
{
6161
allCategories = await JsonSerializer.DeserializeAsync<List<SampleCategory>>(jsonStream.AsStream(), new JsonSerializerOptions
6262
{
63-
ReadCommentHandling = JsonCommentHandling.Skip
63+
ReadCommentHandling = JsonCommentHandling.Skip,
64+
AllowTrailingCommas = true,
6465
});
6566
}
6667

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.AttachedDropShadowPage"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
mc:Ignorable="d">
7+
8+
<!-- Shallow Copy in XamlOnlyPage -->
9+
</Page>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Microsoft.Toolkit.Uwp.UI;
6+
using Windows.UI.Xaml;
7+
8+
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
9+
{
10+
public sealed partial class AttachedDropShadowPage : IXamlRenderListener
11+
{
12+
public AttachedDropShadowPage()
13+
{
14+
InitializeComponent();
15+
}
16+
17+
public void OnXamlRendered(FrameworkElement control)
18+
{
19+
// This is done as we don't have x:Bind in live xaml, so we find and attach after.
20+
var castToTarget = control.FindChild("ShadowTarget");
21+
if (castToTarget != null)
22+
{
23+
if (control.Resources.TryGetValue("CommonShadow", out var resource) && resource is AttachedDropShadow shadow)
24+
{
25+
shadow.CastTo = castToTarget;
26+
}
27+
}
28+
}
29+
}
30+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<Page
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
7+
mc:Ignorable="d">
8+
9+
<!-- TODO: Animation-->
10+
<Page.Resources>
11+
<ui:AttachedDropShadow x:Key="CommonShadow" Offset="4"/><!-- CastTo="{x:Bind ShadowTarget}"/>-->
12+
13+
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
14+
<Setter Property="ui:Effects.Shadow" Value="{StaticResource CommonShadow}"/>
15+
<Setter Property="HorizontalAlignment" Value="Center"/>
16+
<!-- We set a solid color for the background button otherwise the shadow bleeds through as
17+
the new style is transparent. See AttachedCardShadow for proper element clipping. -->
18+
<Setter Property="Background" Value="Red"/>
19+
</Style>
20+
</Page.Resources>
21+
22+
<ScrollViewer>
23+
<Grid>
24+
<!-- The ShadowTarget Grid here is a *sibling* element behind where our elements which will cast
25+
shadows are located, this is important as otherwise if we used a parent element the
26+
shadows would appear on top of our elements instead!
27+
It is also placed within the ScrollViewer here so shadows move with their elements. -->
28+
<Grid x:Name="ShadowTarget"/>
29+
<StackPanel Spacing="32" VerticalAlignment="Center">
30+
<!-- All buttons on this page have the shadow from the common style!
31+
The Shadow definition is Shared! -->
32+
<Button Content="I Have a Shadow!"/>
33+
<!-- Can apply the same shadow to any type of element! -->
34+
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
35+
Height="100" Width="100"
36+
Source="ms-appx:///Assets/Photos/Owl.jpg"/>
37+
<!-- You can still apply a Shadow directly and even use binding with it to manipulate at runtime! -->
38+
<Rectangle RadiusX="32" RadiusY="32"
39+
Height="100" Width="100"
40+
Stroke="Blue" StrokeThickness="1">
41+
<Rectangle.Fill>
42+
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
43+
</Rectangle.Fill>
44+
<ui:Effects.Shadow>
45+
<ui:AttachedDropShadow BlurRadius="@[BlurRadius:DoubleSlider:8.0:0.0-10.0]"
46+
CornerRadius="32"
47+
Color="@[Color:Brush:Black]"
48+
Offset="@[Offset:Vector3:4,4]"
49+
Opacity="@[Opacity:DoubleSlider:1.0:0.0-1.0]"
50+
CastTo="{Binding ElementName=ShadowTarget}"/>
51+
</ui:Effects.Shadow>
52+
</Rectangle>
53+
<!-- This particular scenario of attaching directly to a raw element is easier than the Win2D equivelent. -->
54+
<Border Height="100" Width="100"
55+
CornerRadius="32"
56+
BorderBrush="White" BorderThickness="1">
57+
<Border.Background>
58+
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
59+
</Border.Background>
60+
<ui:Effects.Shadow>
61+
<ui:AttachedDropShadow CornerRadius="32"
62+
Offset="4,4"
63+
CastTo="{Binding ElementName=ShadowTarget}"/>
64+
</ui:Effects.Shadow>
65+
</Border>
66+
<!-- Note how even though this element is transparent, the shadow still shows through,
67+
to have this not occur use the AttachedCardShadow. -->
68+
<Rectangle ui:Effects.Shadow="{StaticResource CommonShadow}"
69+
Fill="#80FF0000"
70+
RadiusX="4" RadiusY="4"
71+
Width="200" Height="100"/>
72+
<!-- This is the same behavior as the old DropShadowPanel where the shadow bleeds through and
73+
the Shadow opacity is tied to the Rectangle itself -->
74+
<controls:DropShadowPanel xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
75+
OffsetX="4" OffsetY="4"
76+
BlurRadius="12"
77+
HorizontalAlignment="Center">
78+
<Rectangle Fill="#80FF0000"
79+
RadiusX="4" RadiusY="4"
80+
Width="200" Height="100"/>
81+
</controls:DropShadowPanel>
82+
<Button Content="I Also have a Shadow!"/>
83+
</StackPanel>
84+
</Grid>
85+
</ScrollViewer>
86+
</Page>

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@
5151
</Border.Background>
5252
</Border>
5353
<!-- We need to put the Shadow on a parent element here as otherwise the
54-
rounding of the border of the image above clips the shadow itself -->
55-
<!-- TODO: This specific scenario should be simpler in the Composition based one? -->
54+
rounding of the border of the image above clips the shadow itself.
55+
This is easier to perform with the Composition only based shadow as the
56+
Shadow is projected to another element. -->
5657
<ui:Effects.Shadow>
5758
<media:AttachedCardShadow CornerRadius="32"
5859
Offset="4,4"/>

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
</controls:SwitchPresenter>
5151
<controls:ConstrainedBox x:Key="ConstrainedBoxControl" />
5252
<media:AttachedCardShadow x:Key="AttachedShadow" />
53+
<ui:AttachedDropShadow x:Key="AttachedDropShadow" />
5354
<controls:DropShadowPanel x:Key="DropShadowPanel"
5455
ui:Effects.Shadow="{StaticResource AttachedShadow}" />
5556
</Page.Resources>

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,19 @@
186186
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/GridSplitter.md"
187187
},
188188
{
189-
"Name": "AttachedDropShadow (Win2D)",
189+
"Name": "AttachedDropShadow (Composition)",
190+
"Type": "AttachedDropShadowPage",
190191
"Subcategory": "Media",
191192
"About": "An AttachedDropShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
193+
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI/Shadows",
194+
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowCompositionXaml.bind",
195+
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
196+
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md"
197+
},
198+
{
199+
"Name": "AttachedCardShadow (Win2D)",
200+
"Subcategory": "Media",
201+
"About": "An AttachedCardShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
192202
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Shadows",
193203
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowWin2DXaml.bind",
194204
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
@@ -200,6 +210,8 @@
200210
"Name": "DropShadowPanel",
201211
"Subcategory": "Media",
202212
"About": "DropShadowPanel contol allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
213+
"BadgeUpdateVersionRequired": "DEPRECATED",
214+
"DeprecatedWarning": "This control will be removed in a future major release. Please use the AttachedDropShadow or AttachedCardShadow extensions instead.",
203215
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel",
204216
"XamlCodeFile": "/SamplePages/Shadows/DropShadowPanelXaml.bind",
205217
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",

Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel/DropShadowPanel.cs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@ private void UpdateShadowMask()
171171
// alpha mask even if Content happens to extend any of the other classes
172172
if (Content is IAlphaMaskProvider maskedControl)
173173
{
174-
mask = maskedControl.GetAlphaMask();
174+
if (maskedControl.WaitUntilLoaded && maskedControl is FrameworkElement element && !element.IsLoaded)
175+
{
176+
element.Loaded += CustomMaskedElement_Loaded;
177+
}
178+
else
179+
{
180+
mask = maskedControl.GetAlphaMask();
181+
}
175182
}
176183
else if (Content is Image)
177184
{
@@ -185,17 +192,6 @@ private void UpdateShadowMask()
185192
{
186193
mask = ((TextBlock)Content).GetAlphaMask();
187194
}
188-
else if (Content is ImageExBase imageExBase)
189-
{
190-
imageExBase.ImageExInitialized += ImageExInitialized;
191-
192-
if (imageExBase.IsInitialized)
193-
{
194-
imageExBase.ImageExInitialized -= ImageExInitialized;
195-
196-
mask = ((ImageExBase)Content).GetAlphaMask();
197-
}
198-
}
199195

200196
_dropShadow.Mask = mask;
201197
}
@@ -205,15 +201,14 @@ private void UpdateShadowMask()
205201
}
206202
}
207203

208-
private void ImageExInitialized(object sender, EventArgs e)
204+
private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e)
209205
{
210-
var imageExBase = (ImageExBase)Content;
211-
212-
imageExBase.ImageExInitialized -= ImageExInitialized;
213-
214-
CompositionBrush mask = ((ImageExBase)Content).GetAlphaMask();
206+
if (sender is FrameworkElement element)
207+
{
208+
element.Loaded -= CustomMaskedElement_Loaded;
215209

216-
_dropShadow.Mask = mask;
210+
_dropShadow.Mask = ((IAlphaMaskProvider)element).GetAlphaMask();
211+
}
217212
}
218213

219214
private void UpdateShadowOffset(float x, float y, float z)

Microsoft.Toolkit.Uwp.UI.Controls.Core/ImageEx/ImageExBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
1919
[TemplateVisualState(Name = UnloadedState, GroupName = CommonGroup)]
2020
[TemplateVisualState(Name = FailedState, GroupName = CommonGroup)]
2121
[TemplatePart(Name = PartImage, Type = typeof(object))]
22-
public abstract partial class ImageExBase : Control
22+
public abstract partial class ImageExBase : Control, IAlphaMaskProvider
2323
{
2424
private bool _isInViewport;
2525

@@ -58,6 +58,9 @@ public abstract partial class ImageExBase : Control
5858
/// </summary>
5959
protected object Image { get; private set; }
6060

61+
/// <inheritdoc/>
62+
public bool WaitUntilLoaded => true;
63+
6164
/// <summary>
6265
/// Initializes a new instance of the <see cref="ImageExBase"/> class.
6366
/// </summary>

0 commit comments

Comments
 (0)