在 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. JDK 动态代理与 CGLIB 动态代理,它俩真的不一样

    摘要:一文带你搞懂JDK 动态代理与 CGLIB 动态代理 本文分享自华为云社区<一文带你搞懂JDK 动态代理与 CGLIB 动态代理>,作者: Code皮皮虾 . 两者有何区别 1.Jd ...

  2. 力扣 - 剑指 Offer 46. 把数字翻译成字符串

    题目 剑指 Offer 46. 把数字翻译成字符串 思路1(递归,自顶向下) 这题和青蛙跳台阶很类似,青蛙跳台阶说的是青蛙每次可以跳一层或者两层,跳到第 n 层有多少种解法,而这题说的是讲数字翻译成字 ...

  3. SpringSecurity过滤器原理

    SpringSecurity原理 主要过滤器链 SpringSecurity的功能主要是由一系列的过滤器链相互配合完成的.验证一个过滤器之后放行到下一个过滤器链,然后到最后. 认证流程 过滤器作用 S ...

  4. Atcoder Regular Contest 096 C - Everything on It(组合数学)

    Atcoder 题面传送门 & 洛谷题面传送门 简单题,由于这场 arc 的 F 是 jxd 作业而我不会做,所以只好来把这场的 E 水掉了. 我们记 \(f(i)\) 为钦定 \(i\) 个 ...

  5. Linux下脚本文件第一行的作用

    Linux下脚本文件第一行的作用 在Linux/Unix系统中,你可以在脚本hello.py顶部添加以下命令让Python脚本可以像SHELL脚本一样可直接执行: #! /usr/bin/env py ...

  6. 【R】clusterProfiler的GO/KEGG富集分析用法小结

    前言 关于clusterProfiler这个R包就不介绍了,网红教授宣传得很成功,功能也比较强大,主要是做GO和KEGG的功能富集及其可视化.简单总结下用法,以后用时可直接找来用. 首先考虑一个问题: ...

  7. 蛋白质组DIA深度学习之谱图预测

    目录 1. 简介 2. 近几年发表的主要工具 1.DeepRT 2.Prosit 3. DIANN 4.DeepDIA 1. 简介 基于串联质谱的蛋白质组学大部分是依赖于数据库(database se ...

  8. 关于写SpringBoot+Mybatisplus+Shiro项目的经验分享二:问题1

    框架: SpringBoot+Mybatisplus+Shiro 简单介绍:关于写SpringBoot+Mybatisplus+Shiro项目的经验分享一:简单介绍 添加时,如果失败,不能正确跳转 c ...

  9. mysql数据定义语言DDL

    库的管理 创建 create 语法:create database 库名 [character set 字符集] # 案例:创建库 create database if not exists book ...

  10. day04 Linux基础命令

    day04 Linux基础命令 查看帮助信息命令 1.man命令:man命令的功能是查看指定命令的详细解释. 格式:man [具体需要被查看的命令] [root@localhost ~]# man r ...