TheCoder
TheCoder

Reputation: 343

How to add drop shadow to just one specific side in WPF?

I am trying to achieve a grid, with a shadow on just one side and no trace of any shadow on any of the other sides. I tried fiddling around with the direction property of the DropShadowEffect.

What I have tried:

<Grid Background="Transparent" Grid.Row="0" Grid.Column="1">
   <Grid Background="White"/>
      <Border CornerRadius="0,5,0,0" BorderBrush="White" BorderThickness="0" Background="White">
         <Border.Effect>
            <DropShadowEffect BlurRadius="5" Direction="355" RenderingBias="Quality" ShadowDepth="2"/>
         </Border.Effect>
      </Border>
   </Grid>
</Grid>

This is what happens with my code: Slight gray trail on the left side

I want to achieve a drop shadow only visible on the bottom side of the grid, and no trace of the shadow on any of the other sides. The above code leaves a thin gray trail on the left side, which wouldn't work for me.

Sorry if this is a silly question, I am kinda new to WPF.

Upvotes: 3

Views: 3438

Answers (2)

Fredrik Hedblad
Fredrik Hedblad

Reputation: 84657

A similar way to what TheCoder suggested. Alter what GetLayoutClip returns to make sure it only clipps horizontally.

public class HorizontalClipGrid : Grid
{
    static HorizontalClipGrid()
    {
        ClipToBoundsProperty.OverrideMetadata(typeof(HorizontalClipGrid), new FrameworkPropertyMetadata(true));
    }

    protected override Geometry? GetLayoutClip(Size layoutSlotSize)
    {
        if (ClipToBounds)
        {
            return new RectangleGeometry(new Rect(new Size(layoutSlotSize.Width, layoutSlotSize.Height + 8)));
        }
        return null;
    }
}

enter image description here

Using a regular Grid on the top and HorizontalClipGrid on the bottom. Modify the hard coded extended clipping height of "8" if required.

Usable like this

<local:HorizontalClipGrid>
    <Grid.Effect>
        <DropShadowEffect ShadowDepth="2"
                          Direction="270"
                          Color="Black"
                          Opacity="0.20"
                          BlurRadius="8"/>
    </Grid.Effect>
    <!-- Controls... -->
</local:HorizontalClipGrid>

Full Xaml used for the example in the screenshot..

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border Grid.RowSpan="4"
            Background="#FF1174E6"/>

    <Grid Grid.Row="1"
          Grid.Column="1"
          Panel.ZIndex="2"
          VerticalAlignment="Top">
        <Grid.Effect>
            <DropShadowEffect ShadowDepth="8"
                              Direction="270"
                              Color="Black"
                              Opacity="1.20"
                              BlurRadius="8"/>
        </Grid.Effect>
        <TextBox Text="Search: "
                  VerticalAlignment="Top"
                  VerticalContentAlignment="Center"
                  Height="28"/>
    </Grid>

    <local:HorizontalClipGrid Grid.Row="2"
                              Grid.Column="1"
                              Panel.ZIndex="2"
                              VerticalAlignment="Top">
        <Grid.Effect>
            <DropShadowEffect ShadowDepth="8"
                              Direction="270"
                              Color="Black"
                              Opacity="1.20"
                              BlurRadius="8"/>
        </Grid.Effect>
        <TextBox Text="Search: "
                 VerticalAlignment="Top"
                 VerticalContentAlignment="Center"
                 Height="28"/>
    </local:HorizontalClipGrid>
        
    <Border Grid.RowSpan="4"
            Grid.Column="2"
            Background="#FFF2F2F2"/>
</Grid>

Upvotes: 0

TheCoder
TheCoder

Reputation: 343

I don't think the DropShadowEffect has any functionality built-in for this sort of application, however, I managed to achieve the required result using a rectangle and filling it with a linear gradient.

 <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0.3">
    <Rectangle.Fill>
       <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
          <GradientStop Color="Black" Offset="0"/>
            <GradientStop Color="#00131313" Offset="1"/>
       </LinearGradientBrush>
    </Rectangle.Fill>
 </Rectangle>

Rectangle

To maintain the same width as the parent of the shadow, add them to the same grid and the same column, set the horizontal and vertical alignment to stretch and the shadow will look consistent.

Then I positioned the rectangle in place of the shadow. Seems a little wanky, but works nonetheless.

Edit: I found another solution which seems way more better, using the ClipToBounds property and the BorderThickness property.

<Border ClipToBounds="True" BorderBrush="White" BorderThickness="0,2,0,0">
   <Border.Effect>
      <DropShadowEffect ShadowDepth="2" BlurRadius="10"/>
   </Border.Effect>
</Border>

Border

Using a border and a drop shadow is easier than using a rectangle and tweaking it till it looks natural.

Usage of grids is advised to position the border perfectly.

Upvotes: 4

Related Questions