C# UWP 工具包 DropShadowPanel 内部阴影

C# UWP Toolkit DropShadowPanel inner shadow

在C# UWP中如何制作内阴影效果?

像这样:

我创建了一个只有边框的网格,但阴影填充了整个网格。

<controls:DropShadowPanel     BlurRadius="5"
                              ShadowOpacity="0.5"
                              OffsetX="0"
                              OffsetY="0"
                              Color="Black">
    <Grid BorderBrush="White" BorderThickness="5"/>
</controls:DropShadowPanel>

如何用这个控件制作内阴影效果?

请注意 DropShadowPanel 可以屏蔽 Rectangle,因此您可以创建 fill-less Rectangle 并将其放在 DropShadowPanel 仅为 Rectangle 边界 创建扩展阴影。然后,您只需将它放在 Grid 内并剪裁 Grid 以切断外部阴影。如果您想要背景色,只需将另一个 Rectangle 添加到 Grid 并将其放在 DropShadowPanel 之后。

示例代码

<Grid Width="400"
      Height="200"
      Margin="24">
    <Grid.Clip>
        <RectangleGeometry Rect="0,0,400,200" />
    </Grid.Clip>
    <Rectangle x:Name="BackgroundColor"
               Fill="LightSteelBlue" />
    <controls:DropShadowPanel x:Name="InnerShadow"
                              HorizontalContentAlignment="Stretch"
                              BlurRadius="15"
                              ShadowOpacity="0.5"
                              Color="Black">
        <Rectangle x:Name="BorderColor"
                   Stroke="LightGray"
                   StrokeThickness="10" />
    </controls:DropShadowPanel>
</Grid>

结果


关于裁剪

需要注意的一件事是,只要 Grid 的大小发生变化,您就需要手动更新 Rect 的大小。或者,您可以使用新的 Composition API 进行裁剪 -

var visual = ElementCompositionPreview.GetElementVisual(RootGrid);
var compositor = visual.Compositor;
visual.Clip = compositor.CreateInsetClip();
protected override CreateParams CreateParams
    {
        get
        {
            const int CS_DROPSHADOW = 0x20000;
            CreateParams cp = base.CreateParams;
            cp.ClassStyle |= CS_DROPSHADOW;
            return cp;
        }
    }

我遇到了同样的问题,最后我为它创建了自己的控件。我基本上创建了一个带有四个细边框的网格,每边一个(左、右、上、下),每个边框都有一个线性渐变画笔来创建阴影效果。像这样:

这是代码::

using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace TestInnerDropShadow
{
    [TemplatePart(Name = PARTNAME_LEFT_BORDER, Type = typeof(Border))]
    [TemplatePart(Name = PARTNAME_RIGHT_BORDER, Type = typeof(Border))]
    [TemplatePart(Name = PARTNAME_TOP_BORDER, Type = typeof(Border))]
    [TemplatePart(Name = PARTNAME_BOTTOM_BORDER, Type = typeof(Border))]
    [TemplatePart(Name = PARTNAME_LEFT_GRADIENT, Type = typeof(GradientStop))]
    [TemplatePart(Name = PARTNAME_RIGHT_GRADIENT, Type = typeof(GradientStop))]
    [TemplatePart(Name = PARTNAME_TOP_GRADIENT, Type = typeof(GradientStop))]
    [TemplatePart(Name = PARTNAME_BOTTOM_GRADIENT, Type = typeof(GradientStop))]
    public class InnerDropShadowPanel : ContentControl
    {
        internal const string PARTNAME_LEFT_BORDER = "PART_LeftBorder";
        internal const string PARTNAME_RIGHT_BORDER = "PART_RightBorder";
        internal const string PARTNAME_TOP_BORDER = "PART_TopBorder";
        internal const string PARTNAME_BOTTOM_BORDER = "PART_BottomBorder";

        internal const string PARTNAME_LEFT_GRADIENT = "PART_LeftGradient";
        internal const string PARTNAME_RIGHT_GRADIENT = "PART_RightGradient";
        internal const string PARTNAME_TOP_GRADIENT = "PART_TopGradient";
        internal const string PARTNAME_BOTTOM_GRADIENT = "PART_BottomGradient";

        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            UpdateShadowDimensions();
            UpdateShadowColor();
        }

        private static void OnPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            if (o is InnerDropShadowPanel thisControl)
            {
                var updateLeft = (e.Property == ShadowOpacityProperty || e.Property == BlurRadiusLeftProperty);
                var updateRight = (e.Property == ShadowOpacityProperty || e.Property == BlurRadiusRightProperty);
                var updateTop = (e.Property == ShadowOpacityProperty || e.Property == BlurRadiusTopProperty);
                var updateBottom = (e.Property == ShadowOpacityProperty || e.Property == BlurRadiusBottomProperty);
                thisControl.UpdateShadowDimensions(updateLeft, updateRight, updateTop, updateBottom);
            }
        }

        private void UpdateShadowDimensions(bool updateLeft = true, bool updateRight = true, bool updateTop = true, bool updateBottom = true)
        {
            if (updateLeft && GetTemplateChild(PARTNAME_LEFT_BORDER) is Border left)
            {
                left.Width = BlurRadiusLeft;
                left.Opacity = ShadowOpacity;
            }

            if (updateRight && GetTemplateChild(PARTNAME_RIGHT_BORDER) is Border right)
            {
                right.Width = BlurRadiusRight;
                right.Opacity = ShadowOpacity;
            }

            if (updateTop && GetTemplateChild(PARTNAME_TOP_BORDER) is Border top)
            {
                top.Height = BlurRadiusTop;
                top.Opacity = ShadowOpacity;
            }

            if (updateBottom && GetTemplateChild(PARTNAME_BOTTOM_BORDER) is Border bottom)
            {
                bottom.Height = BlurRadiusBottom;
                bottom.Opacity = ShadowOpacity;
            }
        }

        private void UpdateShadowColor()
        {
            if (GetTemplateChild(PARTNAME_LEFT_GRADIENT) is GradientStop left)
                left.Color = ShadowColor;

            if (GetTemplateChild(PARTNAME_RIGHT_GRADIENT) is GradientStop right)
                right.Color = ShadowColor;

            if (GetTemplateChild(PARTNAME_TOP_GRADIENT) is GradientStop top)
                top.Color = ShadowColor;

            if (GetTemplateChild(PARTNAME_BOTTOM_GRADIENT) is GradientStop bottom)
                bottom.Color = ShadowColor;
        }

        #region ShadowOpacity Dependency Property

        public static readonly DependencyProperty ShadowOpacityProperty = DependencyProperty.Register(
            nameof(ShadowOpacity),
            typeof(double),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(0d, OnPropertyChanged));

        public double ShadowOpacity
        {
            get => (double)GetValue(ShadowOpacityProperty);
            set => SetValue(ShadowOpacityProperty, value);
        }

        #endregion

        #region ShadowColor Dependency Property

        public static readonly DependencyProperty ShadowColorProperty = DependencyProperty.Register(
            nameof(ShadowColor),
            typeof(Color),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(Colors.Transparent, OnShadowColorChanged));

        public Color ShadowColor
        {
            get => (Color)GetValue(ShadowColorProperty);
            set => SetValue(ShadowColorProperty, value);
        }

        private static void OnShadowColorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            if (o is InnerDropShadowPanel thisControl)
            {
                thisControl.UpdateShadowColor();
            }
        }

        #endregion

        #region BlurRadiusLeft Dependency Property

        public static readonly DependencyProperty BlurRadiusLeftProperty = DependencyProperty.Register(
            nameof(BlurRadiusLeft),
            typeof(double),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(0d, OnPropertyChanged));

        public double BlurRadiusLeft
        {
            get => (double)GetValue(BlurRadiusLeftProperty);
            set => SetValue(BlurRadiusLeftProperty, value);
        }

        #endregion

        #region BlurRadiusRight Dependency Property

        public static readonly DependencyProperty BlurRadiusRightProperty = DependencyProperty.Register(
            nameof(BlurRadiusRight),
            typeof(double),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(0d, OnPropertyChanged));

        public double BlurRadiusRight
        {
            get => (double)GetValue(BlurRadiusRightProperty);
            set => SetValue(BlurRadiusRightProperty, value);
        }

        #endregion

        #region BlurRadiusTop Dependency Property

        public static readonly DependencyProperty BlurRadiusTopProperty = DependencyProperty.Register(
            nameof(BlurRadiusTop),
            typeof(double),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(0d, OnPropertyChanged));

        public double BlurRadiusTop
        {
            get => (double)GetValue(BlurRadiusTopProperty);
            set => SetValue(BlurRadiusTopProperty, value);
        }

        #endregion

        #region BlurRadiusBottom Dependency Property

        public static readonly DependencyProperty BlurRadiusBottomProperty = DependencyProperty.Register(
            nameof(BlurRadiusBottom),
            typeof(double),
            typeof(InnerDropShadowPanel),
            new PropertyMetadata(0d, OnPropertyChanged));

        public double BlurRadiusBottom
        {
            get => (double)GetValue(BlurRadiusBottomProperty);
            set => SetValue(BlurRadiusBottomProperty, value);
        }

        #endregion
    }
}

以及控件模板:

    <Style TargetType="local:InnerDropShadowPanel">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:InnerDropShadowPanel">
                    <Grid>

                        <ContentPresenter/>
                        
                        <Border x:Name="PART_LeftBorder"
                                Opacity="0.5"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Stretch">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                                    <GradientStop Color="Black" Offset="0" x:Name="PART_LeftGradient"/>
                                    <GradientStop Color="Transparent" Offset="0.5"/>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>

                        <Border x:Name="PART_RightBorder"
                                Opacity="0.5"
                                HorizontalAlignment="Right"
                                VerticalAlignment="Stretch">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="1,0" EndPoint="0,0">
                                    <GradientStop Color="Black" Offset="0" x:Name="PART_RightGradient"/>
                                    <GradientStop Color="Transparent" Offset="0.5"/>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>

                        <Border x:Name="PART_TopBorder"
                                Opacity="0.5"
                                HorizontalAlignment="Stretch"
                                VerticalAlignment="Top">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                    <GradientStop Color="Black" Offset="0" x:Name="PART_TopGradient"/>
                                    <GradientStop Color="Transparent" Offset="0.5"/>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>

                        <Border x:Name="PART_BottomBorder"
                                Opacity="0.5"
                                HorizontalAlignment="Stretch"
                                VerticalAlignment="Bottom">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                                    <GradientStop Color="Black" Offset="0" x:Name="PART_BottomGradient"/>
                                    <GradientStop Color="Transparent" Offset="0.5"/>
                                </LinearGradientBrush>
                            </Border.Background></Border>

                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

与 XAML 像这样:

    <local:InnerDropShadowPanel HorizontalAlignment="Center"
                                BlurRadiusLeft="6"
                                BlurRadiusTop="6"
                                ShadowOpacity="0.4"
                                ShadowColor="Black">
        <TextBlock Text="Test"
                   Foreground="Black"
                   FontSize="120"
                   Margin="50,20"/>
    </local:InnerDropShadowPanel>

你得到这样的结果:

您可以在任何一侧或全部添加阴影,并整体更改阴影的颜色和不透明度:

    <local:InnerDropShadowPanel HorizontalAlignment="Center"
                                BlurRadiusLeft="6"
                                BlurRadiusTop="6"
                                BlurRadiusBottom="50"
                                BlurRadiusRight="100"
                                ShadowOpacity="1"
                                ShadowColor="Red">
        <TextBlock Text="Test"
                   Foreground="Black"
                   FontSize="120"
                   Margin="50,20"/>
    </local:InnerDropShadowPanel>

给你这个:

我相信您可以修改此控件并通过添加自定义设置来发挥作用,例如,每一面都有不同的颜色或不透明度。不过,上述解决方案可能足以满足绝大多数情况。