在 WPF 中,我們通常用 DropShadow 做陰影效果,但都是做外陰影。內陰影(Inner Shadow)的話其實也不是不可以,就是有些曲折。這篇文章介紹幾種做內引用的做法。
文章涉及到以下概念:
UIElement.ClipToBounds 屬性 (System.Windows)
UIElement.Clip 屬性 (System.Windows)
VisualBrush 類 (System.Windows.Media)
1. ClipToBounds
<Border>
<Border.Clip>
<RectangleGeometry Rect="0,0,100,100" />
</Border.Clip>
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Border.Effect>
<ContentControl HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Clip " />
</Border>
上面是一個普通的加上 DropShadowEffect 的 Border。要做內部陰影的話就只是將外部陰影裁剪掉,在 Border 上簡單地加上 ClipToBounds="True"
就可以實現這個效果:
ClipToBounds 屬性用於指示是否剪切此元素的內容(或來自此元素的子元素的內容)使其適合包含元素的大小。
但如果 Border 有圓角(最近微軟向圓角勢力屈服了,Windows 11 到處都是圓角)的話,那這個方案就有問題了,因為它不能裁剪圓角:
2. Clip
為了可以裁剪圓角內容,還是老老實實用 Clip 來裁剪,不過這就需要自己計算尺寸及圓角半徑:
<Border>
<Border.Clip>
<RectangleGeometry RadiusX="8"
RadiusY="8"
Rect="0,0,100,100" />
</Border.Clip>
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Border.Effect>
<ContentControl HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Clip " />
</Border>
這個方案的壞處很明顯,因為要寫死尺寸,真的要用這方案的話最好封裝一下在 SizeChanged 事件中重新計算裁剪區域。
3. OpacityMask
<Grid Width="100"
Height="100"
Margin="10">
<Rectangle x:Name="Rectangle2"
Fill="White"
RadiusX="8"
RadiusY="8" />
<Border Margin="0">
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Border.Effect>
<ContentControl HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="OpacityMask" />
</Border>
<Grid.OpacityMask>
<VisualBrush Stretch="None" Visual="{Binding ElementName=Rectangle2}" />
</Grid.OpacityMask>
</Grid>
這個方案用另一個元素的 VisualBrush 來做 OpacityMask,勝在夠靈活,就是 XAML 要寫多一些。
4. 更粗的內陰影
上面這些 Border 都應用了這個樣式:
<Style TargetType="Border">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Margin" Value="10" />
<Setter Property="BorderBrush" Value="SkyBlue" />
<Setter Property="BorderThickness" Value="1" />
</Style>
理所當然的,它們制造出來的陰影都是以這個 1 像素的邊框為基礎,如果需要更大更粗的內陰影,可以使用一個負數的 Margin 配合同樣粗細的 BorderThickness 實現。以 OpacityMask 的方案為例,用下面的代碼可以做個又粗又大的內陰影:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
ShadowElement.Margin = new Thickness(-e.NewValue);
ShadowElement.BorderThickness = new Thickness(e.NewValue);
(ShadowElement.Effect as DropShadowEffect).BlurRadius = e.NewValue * 2;
}