[WPF] 實現 WPF 的 Inner Shadow

在 WPF 中,我們通常用 DropShadow 做陰影效果,但都是做外陰影。內陰影(Inner Shadow)的話其實也不是不可以,就是有些曲折。這篇文章介紹幾種做內引用的做法。

文章涉及到以下概念:

UIElement.ClipToBounds 屬性 (System.Windows)

UIElement.Clip 屬性 (System.Windows)

UIElement.OpacityMask 屬性

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

5. 源碼

https://github.com/DinoChan/wpf_design_and_animation_lab

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章