Add initial Composition based AttachedDropShadow support

This commit is contained in:
michael-hawker 2021-08-19 10:54:52 -07:00
Родитель 2096e5d5ad
Коммит 0459a898ad
16 изменённых файлов: 561 добавлений и 42 удалений

Просмотреть файл

@ -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);
}
}
}