在 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

[WPF] 实现 WPF 的 Inner Shadow的更多相关文章

  1. 【WPF】WPF截屏

    原文:[WPF]WPF截屏 引言 .NET的截图控件在网上流传得不多啊,难得发现一个精品截图控件( 传送门),但是无奈是winform的.后来又找到一个周银辉做的WPF截图(继续传送门),发现截屏是实 ...

  2. 【WPF】wpf用MultiBinding解决Converter需要动态传参的问题,以Button为例

    原文:[WPF]wpf用MultiBinding解决Converter需要动态传参的问题,以Button为例       用Binding并通过Converter转换的时候,可能偶尔会遇到传参的问题, ...

  3. 基于托管的C++来使用WPF - Using WPF with Managed C++

    基于托管的C++来使用WPF - Using WPF with Managed C++ Posted by Zeeshan Amjad This article was originally publ ...

  4. 【WPF】WPF中的List<T>和ObservableCollection<T>

    在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...

  5. 学习WPF——了解WPF中的XAML

    XAML的简单说明 XAML是用于实例化.NET对象的标记语言,主要用于构建WPF的用户界面 XAML中的每一个元素都映射为.NET类的一个实例,例如<Button>映射为WPF的Butt ...

  6. WPF Extended WPF Toolkit

    1.VS 2013 通过NUGet获取Extended WPF Toolkit 我自己的项目已安装 2.在自己页面引用Extended WPF Toolkit xmlns:xctk="htt ...

  7. 【WPF】WPF中调用Winform

    1.添加两个引用:WindowsFormsIntegration.dll(负责整合WPF和Windows).System.Windows.Forms.2.在 XAML文件中添加两个引用(粗体部分): ...

  8. 【转】【WPF】WPF 自定义快捷键命令(Command)

    命令简介 WPF 中的命令是通过实现 ICommand 接口创建的.ICommand 公开两个方法(Execute 及 CanExecute)和一个事件(CanExecuteChanged).Exec ...

  9. 【转】【WPF】WPF样式(Style)—触发器

    样式(Styles)由三部分构成:设置器(Setter).触发器(Triggers).资源(Resources). (1)触发器,让样式的使用更加准确.灵活和高效. (2)触发器(Triggers)主 ...

随机推荐

  1. 浅谈java中的四个核心概念--思途青岛

    Java已经成为一个庞大而复杂的技术平台,对于开发人员而言,要想更好的掌握Java技术,深入理解底层的技术处理细节必不可少. 现在介绍下java的四个核心概念: 1.Java虚拟机 Java虚拟机的主 ...

  2. Excel-给出指定数值的日期 date()

    DATE函数 函数名称:DATE 主要功能:给出指定数值的日期. 使用格式:DATE(year,month,day) 参数说明:year为指定的年份数值(小于9999):month为指定的月份数值(可 ...

  3. mysql 中@ 和 @@的区别

    @x 是 用户自定义的变量 (User variables are written as @var_name)@@x 是 global或session变量 (@@global @@session )@ ...

  4. 【模板】一般图最大匹配(带花树算法)/洛谷P6113

    题目链接 https://www.luogu.com.cn/problem/P6113 题目大意 给定一个 \(n\) 个点 \(m\) 条边的无向图,求该图的最大匹配. 题目解析 二分图最大匹配,一 ...

  5. pyqt5 的串口编写进度

    2020.12.18 今天遇到一个问题, 想用回车实现串口数据的发送. 下面这句话是让光标移动到文字的尾部,但是不能够实现. 对QTextEdit控件中的文字改写,或清除后,再调用下面的移动到尾部,就 ...

  6. IDEA2021.2安装与配置

    https://blog.csdn.net/qq_37242720/article/details/119349394

  7. android 下动态获取控件的id

    有时候我们需要动态的取得一个一个控件的id,然后进行操作,经过在网上查找,找到了一下方法getResources().getIdentifier("textView01", &qu ...

  8. App内容分享

    1.发送文本内容 发送简单的数据到其他应用,比如社交分分享的内容,意图允许用户快速而方便的共享信息. //分享简单的文本内容 public void btnShareText(View view) { ...

  9. @Value("#{}")与@Value("${}")

    开发过程中,有些配置信息是根据环境的不同而有不同的值.这个时候,我们需要获取配置文件中的值或者spring容器中的值,可以通过@value注解获取相关的值. @Value("#{}" ...

  10. MyBatis常用批量方法

    <!-- 批量添加派车单子表数据 --> <insert id="addBatch" parameterType="java.util.List&quo ...