Add initial Composition based AttachedDropShadow support
This commit is contained in:
Родитель
2096e5d5ad
Коммит
0459a898ad
|
@ -507,6 +507,9 @@
|
|||
<Compile Include="SamplePages\MetadataControl\MetadataControlPage.xaml.cs">
|
||||
<DependentUpon>MetadataControlPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\Shadows\AttachedDropShadowPage.xaml.cs">
|
||||
<DependentUpon>AttachedDropShadowPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\TilesBrush\TilesBrushPage.xaml.cs">
|
||||
<DependentUpon>TilesBrushPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -628,6 +631,7 @@
|
|||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\Shadows\AttachedShadowWin2DXaml.bind" />
|
||||
<Content Include="SamplePages\Shadows\AttachedShadowCompositionXaml.bind" />
|
||||
<Content Include="SamplePages\KeyDownTriggerBehavior\KeyDownTriggerBehaviorXaml.bind" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -984,6 +988,10 @@
|
|||
<Content Include="SamplePages\Primitives\SwitchPresenter.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Page Include="SamplePages\Shadows\AttachedDropShadowPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\TilesBrush\TilesBrushPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -60,7 +60,8 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
{
|
||||
allCategories = await JsonSerializer.DeserializeAsync<List<SampleCategory>>(jsonStream.AsStream(), new JsonSerializerOptions
|
||||
{
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.AttachedDropShadowPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- Shallow Copy in XamlOnlyPage -->
|
||||
</Page>
|
|
@ -0,0 +1,30 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public sealed partial class AttachedDropShadowPage : IXamlRenderListener
|
||||
{
|
||||
public AttachedDropShadowPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
// This is done as we don't have x:Bind in live xaml, so we find and attach after.
|
||||
var castToTarget = control.FindChild("ShadowTarget");
|
||||
if (castToTarget != null)
|
||||
{
|
||||
if (control.Resources.TryGetValue("CommonShadow", out var resource) && resource is AttachedDropShadow shadow)
|
||||
{
|
||||
shadow.CastTo = castToTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- TODO: Animation-->
|
||||
<Page.Resources>
|
||||
<ui:AttachedDropShadow x:Key="CommonShadow" Offset="4"/><!-- CastTo="{x:Bind ShadowTarget}"/>-->
|
||||
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
|
||||
<Setter Property="ui:Effects.Shadow" Value="{StaticResource CommonShadow}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<!-- We set a solid color for the background button otherwise the shadow bleeds through as
|
||||
the new style is transparent. See AttachedCardShadow for proper element clipping. -->
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<Grid>
|
||||
<!-- The ShadowTarget Grid here is a *sibling* element behind where our elements which will cast
|
||||
shadows are located, this is important as otherwise if we used a parent element the
|
||||
shadows would appear on top of our elements instead!
|
||||
It is also placed within the ScrollViewer here so shadows move with their elements. -->
|
||||
<Grid x:Name="ShadowTarget"/>
|
||||
<StackPanel Spacing="32" VerticalAlignment="Center">
|
||||
<!-- All buttons on this page have the shadow from the common style!
|
||||
The Shadow definition is Shared! -->
|
||||
<Button Content="I Have a Shadow!"/>
|
||||
<!-- Can apply the same shadow to any type of element! -->
|
||||
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Height="100" Width="100"
|
||||
Source="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
<!-- You can still apply a Shadow directly and even use binding with it to manipulate at runtime! -->
|
||||
<Rectangle RadiusX="32" RadiusY="32"
|
||||
Height="100" Width="100"
|
||||
Stroke="Blue" StrokeThickness="1">
|
||||
<Rectangle.Fill>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Rectangle.Fill>
|
||||
<ui:Effects.Shadow>
|
||||
<ui:AttachedDropShadow BlurRadius="@[BlurRadius:DoubleSlider:8.0:0.0-10.0]"
|
||||
CornerRadius="32"
|
||||
Color="@[Color:Brush:Black]"
|
||||
Offset="@[Offset:Vector3:4,4]"
|
||||
Opacity="@[Opacity:DoubleSlider:1.0:0.0-1.0]"
|
||||
CastTo="{Binding ElementName=ShadowTarget}"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Rectangle>
|
||||
<!-- This particular scenario of attaching directly to a raw element is easier than the Win2D equivelent. -->
|
||||
<Border Height="100" Width="100"
|
||||
CornerRadius="32"
|
||||
BorderBrush="White" BorderThickness="1">
|
||||
<Border.Background>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Border.Background>
|
||||
<ui:Effects.Shadow>
|
||||
<ui:AttachedDropShadow CornerRadius="32"
|
||||
Offset="4,4"
|
||||
CastTo="{Binding ElementName=ShadowTarget}"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Border>
|
||||
<!-- Note how even though this element is transparent, the shadow still shows through,
|
||||
to have this not occur use the AttachedCardShadow. -->
|
||||
<Rectangle ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
<!-- This is the same behavior as the old DropShadowPanel where the shadow bleeds through and
|
||||
the Shadow opacity is tied to the Rectangle itself -->
|
||||
<controls:DropShadowPanel xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
OffsetX="4" OffsetY="4"
|
||||
BlurRadius="12"
|
||||
HorizontalAlignment="Center">
|
||||
<Rectangle Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
</controls:DropShadowPanel>
|
||||
<Button Content="I Also have a Shadow!"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Page>
|
|
@ -51,8 +51,9 @@
|
|||
</Border.Background>
|
||||
</Border>
|
||||
<!-- We need to put the Shadow on a parent element here as otherwise the
|
||||
rounding of the border of the image above clips the shadow itself -->
|
||||
<!-- TODO: This specific scenario should be simpler in the Composition based one? -->
|
||||
rounding of the border of the image above clips the shadow itself.
|
||||
This is easier to perform with the Composition only based shadow as the
|
||||
Shadow is projected to another element. -->
|
||||
<ui:Effects.Shadow>
|
||||
<media:AttachedCardShadow CornerRadius="32"
|
||||
Offset="4,4"/>
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
</controls:SwitchPresenter>
|
||||
<controls:ConstrainedBox x:Key="ConstrainedBoxControl" />
|
||||
<media:AttachedCardShadow x:Key="AttachedShadow" />
|
||||
<ui:AttachedDropShadow x:Key="AttachedDropShadow" />
|
||||
<controls:DropShadowPanel x:Key="DropShadowPanel"
|
||||
ui:Effects.Shadow="{StaticResource AttachedShadow}" />
|
||||
</Page.Resources>
|
||||
|
|
|
@ -186,9 +186,19 @@
|
|||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/GridSplitter.md"
|
||||
},
|
||||
{
|
||||
"Name": "AttachedDropShadow (Win2D)",
|
||||
"Name": "AttachedDropShadow (Composition)",
|
||||
"Type": "AttachedDropShadowPage",
|
||||
"Subcategory": "Media",
|
||||
"About": "An AttachedDropShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI/Shadows",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowCompositionXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md"
|
||||
},
|
||||
{
|
||||
"Name": "AttachedCardShadow (Win2D)",
|
||||
"Subcategory": "Media",
|
||||
"About": "An AttachedCardShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Shadows",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowWin2DXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
|
@ -200,6 +210,8 @@
|
|||
"Name": "DropShadowPanel",
|
||||
"Subcategory": "Media",
|
||||
"About": "DropShadowPanel contol allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "This control will be removed in a future major release. Please use the AttachedDropShadow or AttachedCardShadow extensions instead.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/DropShadowPanelXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
|
|
|
@ -171,7 +171,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
// alpha mask even if Content happens to extend any of the other classes
|
||||
if (Content is IAlphaMaskProvider maskedControl)
|
||||
{
|
||||
mask = maskedControl.GetAlphaMask();
|
||||
if (maskedControl.WaitUntilLoaded && maskedControl is FrameworkElement element && !element.IsLoaded)
|
||||
{
|
||||
element.Loaded += CustomMaskedElement_Loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = maskedControl.GetAlphaMask();
|
||||
}
|
||||
}
|
||||
else if (Content is Image)
|
||||
{
|
||||
|
@ -185,17 +192,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
{
|
||||
mask = ((TextBlock)Content).GetAlphaMask();
|
||||
}
|
||||
else if (Content is ImageExBase imageExBase)
|
||||
{
|
||||
imageExBase.ImageExInitialized += ImageExInitialized;
|
||||
|
||||
if (imageExBase.IsInitialized)
|
||||
{
|
||||
imageExBase.ImageExInitialized -= ImageExInitialized;
|
||||
|
||||
mask = ((ImageExBase)Content).GetAlphaMask();
|
||||
}
|
||||
}
|
||||
|
||||
_dropShadow.Mask = mask;
|
||||
}
|
||||
|
@ -205,15 +201,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
}
|
||||
|
||||
private void ImageExInitialized(object sender, EventArgs e)
|
||||
private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var imageExBase = (ImageExBase)Content;
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.Loaded -= CustomMaskedElement_Loaded;
|
||||
|
||||
imageExBase.ImageExInitialized -= ImageExInitialized;
|
||||
|
||||
CompositionBrush mask = ((ImageExBase)Content).GetAlphaMask();
|
||||
|
||||
_dropShadow.Mask = mask;
|
||||
_dropShadow.Mask = ((IAlphaMaskProvider)element).GetAlphaMask();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShadowOffset(float x, float y, float z)
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
[TemplateVisualState(Name = UnloadedState, GroupName = CommonGroup)]
|
||||
[TemplateVisualState(Name = FailedState, GroupName = CommonGroup)]
|
||||
[TemplatePart(Name = PartImage, Type = typeof(object))]
|
||||
public abstract partial class ImageExBase : Control
|
||||
public abstract partial class ImageExBase : Control, IAlphaMaskProvider
|
||||
{
|
||||
private bool _isInViewport;
|
||||
|
||||
|
@ -58,6 +58,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// </summary>
|
||||
protected object Image { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool WaitUntilLoaded => true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageExBase"/> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -16,7 +16,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media
|
|||
{
|
||||
private const float MaxBlurRadius = 72;
|
||||
private static readonly TypedResourceKey<CompositionGeometricClip> ClipResourceKey = "Clip";
|
||||
private static readonly bool SupportsCompositionVisualSurface;
|
||||
|
||||
private static readonly TypedResourceKey<CompositionPathGeometry> PathGeometryResourceKey = "PathGeometry";
|
||||
private static readonly TypedResourceKey<CompositionRoundedRectangleGeometry> RoundedRectangleGeometryResourceKey = "RoundedGeometry";
|
||||
|
@ -35,11 +34,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media
|
|||
typeof(AttachedCardShadow),
|
||||
new PropertyMetadata(4d, OnDependencyPropertyChanged)); // Default WinUI ControlCornerRadius is 4
|
||||
|
||||
static AttachedCardShadow()
|
||||
{
|
||||
SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the roundness of the shadow's corners.
|
||||
/// </summary>
|
||||
|
@ -55,13 +49,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media
|
|||
/// <inheritdoc/>
|
||||
protected override bool SupportsOnSizeChangedEvent => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnElementContextUninitialized(AttachedShadowElementContext context)
|
||||
{
|
||||
context.ClearAndDisposeResources();
|
||||
base.OnElementContextUninitialized(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper to add a composition based drop shadow to a <see cref="FrameworkElement"/>.
|
||||
/// </summary>
|
||||
public class AttachedDropShadow : AttachedShadowBase
|
||||
{
|
||||
private const float MaxBlurRadius = 72;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsSupported => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override bool SupportsOnSizeChangedEvent => true;
|
||||
|
||||
private static readonly TypedResourceKey<ContainerVisual> ShadowContainerResourceKey = "ShadowContainer";
|
||||
|
||||
private static readonly TypedResourceKey<CompositionPathGeometry> PathGeometryResourceKey = "PathGeometry";
|
||||
private static readonly TypedResourceKey<CompositionRoundedRectangleGeometry> RectangleGeometryResourceKey = "RectGeometry";
|
||||
private static readonly TypedResourceKey<CompositionGeometricClip> ClipResourceKey = "Clip";
|
||||
|
||||
private static readonly TypedResourceKey<CompositionRoundedRectangleGeometry> RoundedRectangleGeometryResourceKey = "RoundedGeometry";
|
||||
private static readonly TypedResourceKey<CompositionSpriteShape> ShapeResourceKey = "Shape";
|
||||
private static readonly TypedResourceKey<ShapeVisual> ShapeVisualResourceKey = "ShapeVisual";
|
||||
private static readonly TypedResourceKey<CompositionSurfaceBrush> SurfaceBrushResourceKey = "SurfaceBrush";
|
||||
private static readonly TypedResourceKey<CompositionVisualSurface> VisualSurfaceResourceKey = "VisualSurface";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the panel uses an alpha mask to create a more precise shadow vs. a quicker rectangle shape.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Turn this off to lose fidelity and gain performance of the panel.
|
||||
/// </remarks>
|
||||
public bool IsMasked
|
||||
{
|
||||
get { return (bool)GetValue(IsMaskedProperty); }
|
||||
set { SetValue(IsMaskedProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsMasked"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsMaskedProperty =
|
||||
DependencyProperty.Register(nameof(IsMasked), typeof(bool), typeof(AttachedDropShadow), new PropertyMetadata(true, OnDependencyPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the roundness of the shadow's corners.
|
||||
/// </summary>
|
||||
public double CornerRadius
|
||||
{
|
||||
get => (double)GetValue(CornerRadiusProperty);
|
||||
set => SetValue(CornerRadiusProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DependencyProperty"/> for <see cref="CornerRadius"/>
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty CornerRadiusProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(CornerRadius),
|
||||
typeof(double),
|
||||
typeof(AttachedDropShadow),
|
||||
new PropertyMetadata(4d, OnDependencyPropertyChanged)); // Default WinUI ControlCornerRadius is 4
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Panel"/> to be used as a backdrop to cast shadows on.
|
||||
/// </summary>
|
||||
public FrameworkElement CastTo
|
||||
{
|
||||
get { return (FrameworkElement)GetValue(CastToProperty); }
|
||||
set { SetValue(CastToProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DependencyProperty"/> for <see cref="CastTo"/>
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty CastToProperty =
|
||||
DependencyProperty.Register(nameof(CastTo), typeof(FrameworkElement), typeof(AttachedDropShadow), new PropertyMetadata(null, OnCastToPropertyChanged)); // TODO: Property Change
|
||||
|
||||
private ContainerVisual _container;
|
||||
|
||||
private static void OnCastToPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is AttachedDropShadow shadow)
|
||||
{
|
||||
if (e.OldValue is FrameworkElement element)
|
||||
{
|
||||
ElementCompositionPreview.SetElementChildVisual(element, null);
|
||||
element.SizeChanged -= shadow.CastToElement_SizeChanged;
|
||||
}
|
||||
|
||||
if (e.NewValue is FrameworkElement elementNew)
|
||||
{
|
||||
var prevContainer = shadow._container;
|
||||
|
||||
var child = ElementCompositionPreview.GetElementChildVisual(elementNew);
|
||||
if (child is ContainerVisual visual)
|
||||
{
|
||||
shadow._container = visual;
|
||||
}
|
||||
else
|
||||
{
|
||||
var compositor = ElementCompositionPreview.GetElementVisual(shadow.CastTo).Compositor;
|
||||
shadow._container = compositor.CreateContainerVisual();
|
||||
|
||||
ElementCompositionPreview.SetElementChildVisual(elementNew, shadow._container);
|
||||
}
|
||||
|
||||
// Need to remove all old children from previous container if it's changed
|
||||
if (prevContainer != null && prevContainer != shadow._container)
|
||||
{
|
||||
foreach (var context in shadow.ShadowElementContextTable)
|
||||
{
|
||||
if (context.Value.IsInitialized &&
|
||||
prevContainer.Children.Contains(context.Value.SpriteVisual))
|
||||
{
|
||||
prevContainer.Children.Remove(context.Value.SpriteVisual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all child shadows are hooked into container
|
||||
foreach (var context in shadow.ShadowElementContextTable)
|
||||
{
|
||||
if (context.Value.IsInitialized)
|
||||
{
|
||||
shadow.SetElementChildVisual(context.Value);
|
||||
}
|
||||
}
|
||||
|
||||
elementNew.SizeChanged += shadow.CastToElement_SizeChanged;
|
||||
|
||||
// Re-trigger updates to all shadow locations for new parent
|
||||
shadow.CastToElement_SizeChanged(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CastToElement_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
// Don't use sender or 'e' here as related to container element not
|
||||
// element for shadow, grab values off context. (Also may be null from internal call.)
|
||||
foreach (var context in ShadowElementContextTable)
|
||||
{
|
||||
if (context.Value.IsInitialized)
|
||||
{
|
||||
// TODO: Should we use ActualWidth/Height instead of RenderSize?
|
||||
OnSizeChanged(context.Value, context.Value.Element.RenderSize, context.Value.Element.RenderSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override void OnElementContextUninitialized(AttachedShadowElementContext context)
|
||||
{
|
||||
if (_container != null && _container.Children.Contains(context.SpriteVisual))
|
||||
{
|
||||
_container.Children.Remove(context.SpriteVisual);
|
||||
}
|
||||
|
||||
base.OnElementContextUninitialized(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetElementChildVisual(AttachedShadowElementContext context)
|
||||
{
|
||||
if (_container != null && !_container.Children.Contains(context.SpriteVisual))
|
||||
{
|
||||
_container.Children.InsertAtTop(context.SpriteVisual);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override CompositionBrush GetShadowMask(AttachedShadowElementContext context)
|
||||
{
|
||||
CompositionBrush mask = null;
|
||||
|
||||
if (DesignTimeHelpers.IsRunningInLegacyDesignerMode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (context.Element != null)
|
||||
{
|
||||
if (IsMasked)
|
||||
{
|
||||
// We check for IAlphaMaskProvider first, to ensure that we use the custom
|
||||
// alpha mask even if Content happens to extend any of the other classes
|
||||
if (context.Element is IAlphaMaskProvider maskedControl)
|
||||
{
|
||||
if (maskedControl.WaitUntilLoaded && !context.Element.IsLoaded)
|
||||
{
|
||||
context.Element.Loaded += CustomMaskedElement_Loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = maskedControl.GetAlphaMask();
|
||||
}
|
||||
}
|
||||
else if (context.Element is Image)
|
||||
{
|
||||
mask = ((Image)context.Element).GetAlphaMask();
|
||||
}
|
||||
else if (context.Element is Shape)
|
||||
{
|
||||
mask = ((Shape)context.Element).GetAlphaMask();
|
||||
}
|
||||
else if (context.Element is TextBlock)
|
||||
{
|
||||
mask = ((TextBlock)context.Element).GetAlphaMask();
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a mask and have specified rounded corners, we'll generate a simple quick mask.
|
||||
// This is the same code from link:AttachedCardShadow.cs:GetShadowMask
|
||||
if (mask == null && SupportsCompositionVisualSurface && CornerRadius > 0)
|
||||
{
|
||||
// Create rounded rectangle geometry and add it to a shape
|
||||
var geometry = context.GetResource(RoundedRectangleGeometryResourceKey) ?? context.AddResource(
|
||||
RoundedRectangleGeometryResourceKey,
|
||||
context.Compositor.CreateRoundedRectangleGeometry());
|
||||
geometry.CornerRadius = new Vector2((float)CornerRadius);
|
||||
|
||||
var shape = context.GetResource(ShapeResourceKey) ?? context.AddResource(ShapeResourceKey, context.Compositor.CreateSpriteShape(geometry));
|
||||
shape.FillBrush = context.Compositor.CreateColorBrush(Colors.Black);
|
||||
|
||||
// Create a ShapeVisual so that our geometry can be rendered to a visual
|
||||
var shapeVisual = context.GetResource(ShapeVisualResourceKey) ??
|
||||
context.AddResource(ShapeVisualResourceKey, context.Compositor.CreateShapeVisual());
|
||||
shapeVisual.Shapes.Add(shape);
|
||||
|
||||
// Create a CompositionVisualSurface, which renders our ShapeVisual to a texture
|
||||
var visualSurface = context.GetResource(VisualSurfaceResourceKey) ??
|
||||
context.AddResource(VisualSurfaceResourceKey, context.Compositor.CreateVisualSurface());
|
||||
visualSurface.SourceVisual = shapeVisual;
|
||||
|
||||
// Create a CompositionSurfaceBrush to render our CompositionVisualSurface to a brush.
|
||||
// Now we have a rounded rectangle brush that can be used on as the mask for our shadow.
|
||||
var surfaceBrush = context.GetResource(SurfaceBrushResourceKey) ?? context.AddResource(
|
||||
SurfaceBrushResourceKey,
|
||||
context.Compositor.CreateSurfaceBrush(visualSurface));
|
||||
|
||||
geometry.Size = visualSurface.SourceSize = shapeVisual.Size = context.Element.RenderSize.ToVector2();
|
||||
|
||||
mask = surfaceBrush;
|
||||
}
|
||||
}
|
||||
|
||||
// Position our shadow in the correct spot to match the corresponding element.
|
||||
context.SpriteVisual.Size = context.Element.RenderSize.ToVector2();
|
||||
context.SpriteVisual.Offset = context.Element.CoordinatesFrom(CastTo).ToVector3();
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var context = GetElementContext(sender as FrameworkElement);
|
||||
|
||||
context.Element.Loaded -= CustomMaskedElement_Loaded;
|
||||
|
||||
UpdateShadowClip(context);
|
||||
UpdateShadowMask(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/*protected override CompositionClip GetShadowClip(AttachedShadowElementContext context)
|
||||
{
|
||||
var rectGeom = context.GetResource(RectangleGeometryResourceKey) ??
|
||||
context.AddResource(RectangleGeometryResourceKey, context.Compositor.CreateRoundedRectangleGeometry());
|
||||
|
||||
rectGeom.Offset = Offset.ToVector2();
|
||||
rectGeom.Size = new Vector2((float)context.Element.ActualWidth, (float)context.Element.ActualHeight);
|
||||
rectGeom.CornerRadius = new Vector2((float)CornerRadius, (float)CornerRadius);
|
||||
|
||||
var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectGeom));
|
||||
|
||||
return clip;*/
|
||||
|
||||
/*var pathGeom = context.GetResource(PathGeometryResourceKey) ??
|
||||
context.AddResource(PathGeometryResourceKey, context.Compositor.CreatePathGeometry());
|
||||
|
||||
// Create rounded rectangle geometry at a larger size that compensates for the size of the stroke,
|
||||
// as we want the inside edge of the stroke to match the edges of the element.
|
||||
// Additionally, the inside edge of the stroke will have a smaller radius than the radius we specified.
|
||||
// Using "(StrokeThickness / 2) + Radius" as our rectangle's radius will give us an inside stroke radius that matches the radius we want.
|
||||
var radius = (MaxBlurRadius / 2) + (float)CornerRadius;
|
||||
var canvasRectangle = context.Compositor.CreateRoundedRectangleGeometry();
|
||||
canvasRectangle.Offset = new Vector2(-MaxBlurRadius / 2, -MaxBlurRadius / 2);
|
||||
canvasRectangle.Size = new Vector2((float)context.Element.ActualWidth + MaxBlurRadius, (float)context.Element.ActualHeight + MaxBlurRadius);
|
||||
canvasRectangle.CornerRadius = new Vector2(radius, radius);
|
||||
|
||||
var rectangleShape = context.Compositor.CreateSpriteShape(canvasRectangle);
|
||||
rectangleShape.StrokeThickness = MaxBlurRadius;
|
||||
|
||||
var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectangleShape.Geometry));
|
||||
|
||||
return clip;*/
|
||||
|
||||
//// return null;
|
||||
////}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize)
|
||||
{
|
||||
var sizeAsVec2 = newSize.ToVector2();
|
||||
|
||||
context.SpriteVisual.Size = sizeAsVec2;
|
||||
context.SpriteVisual.Offset = context.Element.CoordinatesFrom(CastTo).ToVector3();
|
||||
|
||||
UpdateShadowClip(context);
|
||||
|
||||
base.OnSizeChanged(context, newSize, previousSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue)
|
||||
{
|
||||
if (property == IsMaskedProperty)
|
||||
{
|
||||
UpdateShadowMask(context);
|
||||
}
|
||||
else if (property == CornerRadiusProperty)
|
||||
{
|
||||
//var geometry = context.GetResource(RectangleGeometryResourceKey);
|
||||
//if (geometry != null)
|
||||
//{
|
||||
// geometry.CornerRadius = new Vector2((float)(double)newValue);
|
||||
//}
|
||||
|
||||
UpdateShadowClip(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnPropertyChanged(context, property, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -17,6 +18,16 @@ namespace Microsoft.Toolkit.Uwp.UI
|
|||
/// </summary>
|
||||
public abstract class AttachedShadowBase : DependencyObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not Composition's VisualSurface is supported.
|
||||
/// </summary>
|
||||
protected static readonly bool SupportsCompositionVisualSurface;
|
||||
|
||||
static AttachedShadowBase()
|
||||
{
|
||||
SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DependencyProperty"/> for <see cref="BlurRadius"/>.
|
||||
/// </summary>
|
||||
|
@ -53,7 +64,7 @@ namespace Microsoft.Toolkit.Uwp.UI
|
|||
/// <summary>
|
||||
/// Gets or sets the collection of <see cref="AttachedShadowElementContext"/> for each element this <see cref="AttachedShadowBase"/> is connected to.
|
||||
/// </summary>
|
||||
private ConditionalWeakTable<FrameworkElement, AttachedShadowElementContext> ShadowElementContextTable { get; set; }
|
||||
protected ConditionalWeakTable<FrameworkElement, AttachedShadowElementContext> ShadowElementContextTable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blur radius of the shadow.
|
||||
|
@ -149,6 +160,7 @@ namespace Microsoft.Toolkit.Uwp.UI
|
|||
/// <param name="context">The <see cref="AttachedShadowElementContext"/> that is being uninitialized.</param>
|
||||
protected internal virtual void OnElementContextUninitialized(AttachedShadowElementContext context)
|
||||
{
|
||||
context.ClearAndDisposeResources();
|
||||
ElementCompositionPreview.SetElementChildVisual(context.Element, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -275,12 +275,15 @@ namespace Microsoft.Toolkit.Uwp.UI
|
|||
/// </summary>
|
||||
public void ClearAndDisposeResources()
|
||||
{
|
||||
foreach (var kvp in _resources)
|
||||
if (_resources != null)
|
||||
{
|
||||
(kvp.Value as IDisposable)?.Dispose();
|
||||
}
|
||||
foreach (var kvp in _resources)
|
||||
{
|
||||
(kvp.Value as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
_resources.Clear();
|
||||
_resources.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,11 @@ namespace Microsoft.Toolkit.Uwp.UI
|
|||
/// </summary>
|
||||
public interface IAlphaMaskProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the AlphaMask needs to be retrieved after the element has loaded.
|
||||
/// </summary>
|
||||
bool WaitUntilLoaded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This method should return the appropiate alpha mask to be used in the shadow of this control
|
||||
/// </summary>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Point = Windows.Foundation.Point;
|
||||
using Rect = Windows.Foundation.Rect;
|
||||
|
@ -54,5 +55,17 @@ namespace Microsoft.Toolkit.Uwp
|
|||
{
|
||||
return new Rect(point, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Vector3"/> of the specified point with 0 for the <see cref="Vector3.Z"/> coordinate.
|
||||
/// </summary>
|
||||
/// <param name="point"><see cref="Point"/> to transform to a <see cref="Vector3"/>.</param>
|
||||
/// <returns>New <see cref="Vector3"/> representing the X,Y position of the <see cref="Point"/>.</returns>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 ToVector3(this Point point)
|
||||
{
|
||||
return new Vector3(point.ToVector2(), 0f);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче