[WPF] 实现 WPF 的 Inner Shadow
在 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;
}

5. 源码
https://github.com/DinoChan/wpf_design_and_animation_lab
[WPF] 实现 WPF 的 Inner Shadow的更多相关文章
- 【WPF】WPF截屏
原文:[WPF]WPF截屏 引言 .NET的截图控件在网上流传得不多啊,难得发现一个精品截图控件( 传送门),但是无奈是winform的.后来又找到一个周银辉做的WPF截图(继续传送门),发现截屏是实 ...
- 【WPF】wpf用MultiBinding解决Converter需要动态传参的问题,以Button为例
原文:[WPF]wpf用MultiBinding解决Converter需要动态传参的问题,以Button为例 用Binding并通过Converter转换的时候,可能偶尔会遇到传参的问题, ...
- 基于托管的C++来使用WPF - Using WPF with Managed C++
基于托管的C++来使用WPF - Using WPF with Managed C++ Posted by Zeeshan Amjad This article was originally publ ...
- 【WPF】WPF中的List<T>和ObservableCollection<T>
在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...
- 学习WPF——了解WPF中的XAML
XAML的简单说明 XAML是用于实例化.NET对象的标记语言,主要用于构建WPF的用户界面 XAML中的每一个元素都映射为.NET类的一个实例,例如<Button>映射为WPF的Butt ...
- WPF Extended WPF Toolkit
1.VS 2013 通过NUGet获取Extended WPF Toolkit 我自己的项目已安装 2.在自己页面引用Extended WPF Toolkit xmlns:xctk="htt ...
- 【WPF】WPF中调用Winform
1.添加两个引用:WindowsFormsIntegration.dll(负责整合WPF和Windows).System.Windows.Forms.2.在 XAML文件中添加两个引用(粗体部分): ...
- 【转】【WPF】WPF 自定义快捷键命令(Command)
命令简介 WPF 中的命令是通过实现 ICommand 接口创建的.ICommand 公开两个方法(Execute 及 CanExecute)和一个事件(CanExecuteChanged).Exec ...
- 【转】【WPF】WPF样式(Style)—触发器
样式(Styles)由三部分构成:设置器(Setter).触发器(Triggers).资源(Resources). (1)触发器,让样式的使用更加准确.灵活和高效. (2)触发器(Triggers)主 ...
随机推荐
- CF1444C Team-Building
考虑我们判定二分图染色的经典算法: 染色. 我们把所有不同颜色块之间的边都保存下来. 只在图中保留相同颜块之间的边,并对其染色. 我们考虑记\(g_i\)为一个点的所在联通块编号,\(f_i\)为他在 ...
- 63. Binary Tree Level Order Traversal II
Binary Tree Level Order Traversal II My Submissions QuestionEditorial Solution Total Accepted: 79742 ...
- MariaDB——简介
一.MariaDB跟MySQL在绝大多数方面是兼容的,对于开发者来说,几乎感觉不到任何不同.是MySQL的代替品. MariaDB虽然被视为MySQL数据库的替代品,但它在扩展功能.存储引擎以及一些新 ...
- mac系统升级导致VirtualBox报Kernel driver not installed (rc=-1908)
一.背景 最近将我的Mac升级成了Monterey版本,结果发现之前的安装的VirtualBox虚拟机无法启动,报了如下错误. Kernel driver not installed (rc=-190 ...
- 在Telegraf上报的监控数据中添加固定的标签列
Telegraf作为InfluxData提供的TICK工具栈(由Telegraf, InfluxDB, Chronograf, Kapacitor四个工具的首字母组成)中收集监控数据的一环,功能非常强 ...
- Kubernetes-存储(一)
前言 本篇是Kubernetes第十二篇,大家一定要把环境搭建起来,看是解决不了问题的,必须实战. Kubernetes系列文章: Kubernetes介绍 Kubernetes环境搭建 Kubern ...
- 学习java 7.2
学习内容:案例一:斐波那契数列从1开始作为第一个数,求第20个数 public class Test { public static void main(String[ ] args){ int[ ] ...
- 日常Java(测试 (二柱)修改版)2021/9/22
题目: 一家软件公司程序员二柱的小孩上了小学二年级,老师让家长每天出30道四则运算题目给小学生做. 二柱一下打印出好多份不同的题目,让孩子做了.老师看了作业之后,对二柱赞许有加.别的老师闻讯, 问二柱 ...
- kafka安装(单机版)
一.安装kafka(单机版) 因为现在的kafka安装包都自带zookeeper,所以如果是安装本地单机版kafka,不需要额外去安装zookeeper,使用自带的就可以了. 1.下载kafka 2. ...
- 内存管理——array new,array delete
1.array new array new就是申请一个数组空间,所以在delete的时候一定不能忘记在delete前加[] delete加上[]符号以后,就相当于告诉系统"我这里是数组对象, ...