Created
July 29, 2019 21:53
-
-
Save michael-hawker/8f348441198fd8d9a3ffe2fac55ad01a to your computer and use it in GitHub Desktop.
Inline XAML Shadow Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Licensed to the .NET Foundation under one or more agreements. | |
// The .NET Foundation licenses this file to you under the MIT license. | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Numerics; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Windows.UI; | |
using Windows.UI.Xaml; | |
using Windows.UI.Xaml.Controls; | |
using Windows.UI.Xaml.Hosting; | |
namespace Helpers | |
{ | |
/// <summary> | |
/// Provides a Basic Rectangular DropShadow effect on a FrameworkElement via an Attached Property. | |
/// Must be in a parent container that is larger than the provide element to cast a shadow on. | |
/// Note: Doesn't provide animation support. https://docs.microsoft.com/en-us/windows/uwp/composition/composition-shadows#animating | |
/// </summary> | |
public class FrameworkElementExtensions | |
{ | |
public static BasicDropShadowOptions GetBasicDropShadow(FrameworkElement obj) | |
{ | |
return (BasicDropShadowOptions)obj.GetValue(BasicDropShadowProperty); | |
} | |
public static void SetBasicDropShadow(FrameworkElement obj, BasicDropShadowOptions value) | |
{ | |
obj.SetValue(BasicDropShadowProperty, value); | |
} | |
// Using a DependencyProperty as the backing store for BasicDropShadow. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty BasicDropShadowProperty = | |
DependencyProperty.RegisterAttached("BasicDropShadow", typeof(BasicDropShadowOptions), typeof(FrameworkElementExtensions), new PropertyMetadata(null, BasicDropShadow_PropertyChanged)); | |
private static void BasicDropShadow_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
if (d is FrameworkElement element && e.NewValue is BasicDropShadowOptions options) | |
{ | |
element.Loaded += Element_Loaded; | |
} | |
// TODO: Handle if options removed later. | |
} | |
private static void Element_Loaded(object sender, RoutedEventArgs e) | |
{ | |
BasicDropShadowOptions options; | |
if (sender is FrameworkElement element && ((options = GetBasicDropShadow(element)) != null) && element.Parent is Panel parent) | |
{ | |
// Create a child host for shadow to insert in parent next to / behind element | |
var shadowHost = new ShadowHost() { Width = element.ActualWidth, Height = element.ActualHeight, Margin = element.Margin }; | |
// Position Shadow Host in Parent, TODO: Can we get around this? | |
switch (parent) | |
{ | |
case Canvas _: | |
Canvas.SetLeft(shadowHost, Canvas.GetLeft(element)); | |
Canvas.SetTop(shadowHost, Canvas.GetTop(element)); | |
Canvas.SetZIndex(shadowHost, Canvas.GetZIndex(element)); | |
break; | |
case Grid _: | |
Grid.SetRow(shadowHost, Grid.GetRow(element)); | |
Grid.SetColumn(shadowHost, Grid.GetColumn(element)); | |
Grid.SetRowSpan(shadowHost, Grid.GetRowSpan(element)); | |
Grid.SetColumnSpan(shadowHost, Grid.GetColumnSpan(element)); | |
break; | |
} | |
parent.Children.Insert(parent.Children.IndexOf(element), shadowHost); | |
// Get Parent's Visual to hold our shadow | |
var hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost); | |
var compositor = hostVisual.Compositor; | |
var shadowVisual = compositor.CreateSpriteVisual(); | |
shadowVisual.Size = new Vector2((float)element.ActualWidth, (float)element.ActualHeight); | |
////shadowVisual.RelativeSizeAdjustment = Vector2.One; // Set size of Visual | |
// Create Shadow | |
var shadow = compositor.CreateDropShadow(); | |
shadow.BlurRadius = (float)options.BlurRadius; | |
shadow.Color = options.Color; | |
if (options.Offset != null) | |
{ | |
shadow.Offset = options.Offset.ToVector3(); | |
} | |
shadow.Opacity = (float)options.Opacity; | |
shadowVisual.Shadow = shadow; | |
// Attach Shadow | |
ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual); | |
// Make sure size of shadow host and shadow visual always stay in sync | |
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size"); | |
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual); | |
shadowVisual.StartAnimation("Size", bindSizeAnimation); | |
} | |
} | |
} | |
public class BasicDropShadowOptions: DependencyObject | |
{ | |
public double BlurRadius | |
{ | |
get { return (double)GetValue(BlurRadiusProperty); } | |
set { SetValue(BlurRadiusProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for BlurRadius. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty BlurRadiusProperty = | |
DependencyProperty.Register(nameof(BlurRadius), typeof(double), typeof(BasicDropShadowOptions), new PropertyMetadata(9.0)); | |
public Color Color | |
{ | |
get { return (Color)GetValue(ColorProperty); } | |
set { SetValue(ColorProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for Color. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty ColorProperty = | |
DependencyProperty.Register(nameof(Color), typeof(Color), typeof(BasicDropShadowOptions), new PropertyMetadata(Colors.Black)); | |
public Vector3Container Offset { get; set; } | |
public double Opacity | |
{ | |
get { return (double)GetValue(OpacityProperty); } | |
set { SetValue(OpacityProperty, value); } | |
} | |
// Using a DependencyProperty as the backing store for Opacity. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty OpacityProperty = | |
DependencyProperty.Register(nameof(Opacity), typeof(double), typeof(BasicDropShadowOptions), new PropertyMetadata(1.0)); | |
} | |
public class ShadowHost : FrameworkElement | |
{ | |
} | |
[Windows.Foundation.Metadata.CreateFromString(MethodName = "Helpers.Vector3Container.ConvertToVector3")] | |
public class Vector3Container | |
{ | |
public float X; | |
// | |
// Summary: | |
// The Y component of the vector. | |
public float Y; | |
// | |
// Summary: | |
// The Z component of the vector. | |
public float Z; | |
public static Vector3Container ConvertToVector3(string rawString) | |
{ | |
string[] coords = rawString.Split(','); | |
var vector = new Vector3Container(); | |
if (coords.Length > 0) | |
{ | |
float.TryParse(coords[0], out vector.X); | |
} | |
if (coords.Length > 1) | |
{ | |
float.TryParse(coords[1], out vector.Y); | |
} | |
if (coords.Length > 2) | |
{ | |
float.TryParse(coords[2], out vector.Z); | |
} | |
return vector; | |
} | |
public Vector3 ToVector3() | |
{ | |
return new Vector3(X, Y, Z); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Page | |
x:Class="Helpers.MainPage" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:local="using:Helpers" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
mc:Ignorable="d" | |
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> | |
<Canvas x:Name="Container" Background="LightSalmon"> | |
<Rectangle Fill="Blue" Width="200" Height="200" Canvas.Left="200" Canvas.Top="50"> | |
<local:FrameworkElementExtensions.BasicDropShadow> | |
<local:BasicDropShadowOptions BlurRadius="25" Offset="30,30,30" Color="Purple" Opacity="0.9"/> | |
</local:FrameworkElementExtensions.BasicDropShadow> | |
</Rectangle> | |
<Rectangle Fill="Green" Width="400" Height="50" Canvas.Left="100" Canvas.Top="150" Canvas.ZIndex="-1"> | |
<local:FrameworkElementExtensions.BasicDropShadow> | |
<local:BasicDropShadowOptions BlurRadius="50" Offset="10,10,10" Color="Blue"/> | |
</local:FrameworkElementExtensions.BasicDropShadow> | |
</Rectangle> | |
</Canvas> | |
</Page> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment