C# UWP Toolkit DropShadowPanel inner shadow
Asked Answered
M

3

9

In C# UWP how to make inner shadow effect?

Like this:

I have created one grid with just a border, but shadow is populating the whole grid.

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

How to make inner shadow effect with this control?

Monty answered 29/7, 2017 at 23:11 Comment(0)
J
12

Note that DropShadowPanel can mask a Rectangle, so you can create a fill-less Rectangle and place it inside a DropShadowPanel to create a spreading shadow for the Rectangle's border only. Then, you just place it inside a Grid and clip the Grid to cut off the outer shadow. If you want a background color, simply add another Rectangle to the Grid and place it behind the DropShadowPanel.

Sample Code

<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>

Result enter image description here


About Clipping

One thing to note is that you will need to manually update the size of the Rect whenever the size of your Grid changes. Alternatively, you can use the new Composition API to do the clipping -

var visual = ElementCompositionPreview.GetElementVisual(RootGrid);
var compositor = visual.Compositor;
visual.Clip = compositor.CreateInsetClip();
Jimmiejimmy answered 30/7, 2017 at 0:43 Comment(0)
G
0

I had this same problem, and ended up creating my own control for it. I basically created a grid with four skinny borders, one on each side (left, right, top, bottom) and each with a linear gradient brush to create the shadow effect. Like this:

enter image description here

Here is the code::

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
    }
}

And the control template:

    <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>

With XAML like this:

    <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>

You get something like this:

Left and Top drop shadows

You can add the drop shadows on any side, or all of them, and change the color and opacity of the shadow as a whole:

    <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>

gives you this:

drop shadows every side

I'm sure you can modify this control and go wild by adding customizations, like each side having a different color or opacity, for example. This the above solution will probably be sufficient for a vast majority of cases, though.

Goodale answered 26/3, 2021 at 3:13 Comment(0)
C
-2
protected override CreateParams CreateParams
    {
        get
        {
            const int CS_DROPSHADOW = 0x20000;
            CreateParams cp = base.CreateParams;
            cp.ClassStyle |= CS_DROPSHADOW;
            return cp;
        }
    }
Chambless answered 26/3, 2018 at 13:1 Comment(1)
Thank you for this code snippet, which might provide some limited short-term help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you've madeDex

© 2022 - 2024 — McMap. All rights reserved.