WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类)


先看一下最终效果,左图为使用亚克力材质并添加组合颜色的效果;右图为MicaAlt材质的效果。两者都自定义了标题栏并且最大限度地保留了DWM提供的原生窗口效果(最大化最小化、关闭出现的动画、窗口阴影、拖拽布局器等)。接下来把各部分的实现一个个拆开来讲讲。
一、使用窗口材质特效
先粗略介绍一下目前win11和win10上的材质特效类型及一些特性:
Windows10 1903 ~ Windows11 lastest:Acrylic 亚克力材质 支持使用组合颜色 窗口失去焦点时不失效、有拖动窗口延迟 API: SetWindowCompositionAttribute
(1803版本开放此API 但是对Win32应用支持不好)
Windows11: API: SetWindowAttribute 其实现的材质特性:原生不支持组合颜色 对于非WindowStyle.None的窗口失焦失效 无拖动窗口延迟 提供了暗亮模式
Acrylic 亚克力材质 动态模糊,背景取决于窗口下方的内容
Mica 云母材质 背景仅取桌面壁纸(第三方动态壁纸软件无效) 颜色变化较为平缓 win11系统应用的窗口背景大部分使用此材质
MicaAlt 同Mica材质,区别是此材质的颜色变化更突出 文件管理器的标题栏使用此材质
(在win11上同样支持使用win10的SetWindowCompositionAttribute启用旧版API,只不过需要不同的使用条件)
前面的文章中介绍了在win11上启用材质的方法,但有不少弊端。之后诺尔大佬探究出从底层满足调用条件的方法,总结如下:
1. 无论调用哪个API,都需要设置AllowTransparency="True",弊端是带来性能问题和鼠标穿透(即使DWM已经绘制了底层颜色),改为调用:
1 var hwndSource = (HwndSource)PresentationSource.FromVisual(_window);
2 hwndSource.CompositionTarget.BackgroundColor = Colors.Transparent;
2. WindowChrome.GlassFrameThickness 对于SetWindowCompositionAttribute需要值为-1,另一者则需要为0,弊端是可能我们并不需要WindowsChrome来拓展整个客户区,改为调用DwmExtendFrameIntoClientArea,详细见后文示例程序。
如果你想动手实现一下,可以参照:[.NET,WPF] 窗体云母, 亚克力, 透明, 混合颜色, 模糊背景, 亮暗色主题全讲 (slimenull.com)
下面给出我封装好的附加属性:
使用方法很简单,在你的xaml中添加以下作为Window的子标签,并且将Window.Background设为Transparent.
<local:WindowMaterial.Material>
<local:WindowMaterial x:Name="windowMaterial"
IsDarkMode="True"
UseWindowComposition="False"
MaterialMode="MicaAlt"
CompositonColor="#CC6699FF" >
</local:WindowMaterial>
</local:WindowMaterial.Material>
属性解释: IsDarkMode: 暗色模式,主要对Mica(Alt)材质生效
UseWindowComposition: 在win10上无效,指示使用SetWindowCompositionAttribute,在win11上启用旧版的亚克力特效,一般用于创建无边框窗口的背景材质,此属性为True时会忽略MaterialMode
MaterialMode: 指示使用的材质类型 None / Acrylic / Mica / MicaAlt
CompositionColor: 指示使用混合颜色的值,仅对MaterialMode=Acrylic(直接设置Window.Background) 和 使用SetWindowCompositionAttribute时有效
幸运的话可以得到以下效果:

如果尝试使用亚克力材质并设置混合色的话,无论使用哪个API都会得到类似的效果:

区别在于:如果使用SetWindowAttribute提供的亚克力材质,可以调整IsDarkMode来配置背景色,但是效果不是很好。

使用附加的WindowChromeEx来将客户区拓展至标题栏
如果WindowChrome直接附加在窗口上会覆盖掉我们设置的GlassFrameThickness,故这里的设计是将WindowChrome附加在WindowMaterial上进行管理。你可以接着使用熟悉的WindowChrome提供的属性,然后把它作为资源提供给WindowMaterial.WindowChromeEx属性。
<Window.Resources>
<WindowChrome x:Key="windowChrome" ResizeBorderThickness="8"/>
</Window.Resources> <local:WindowMaterial.Material>
<local:WindowMaterial x:Name="windowMaterial"
IsDarkMode="True"
UseWindowComposition="False"
WindowChromeEx="{StaticResource windowChrome}"
MaterialMode="Acrylic"
CompositonColor="#CC6699FF" >
</local:WindowMaterial>
</local:WindowMaterial.Material>
然后就能得到:

MicaAlt (DarkMode) 以及 使用 WindowComposition 的亚克力材质效果:


二、自定义标题栏并保留DWM动画
简单地介绍以下我的实现: 在Windows所以窗口创建底层都是走的WinAPI,WPF也不例外。可以通过仅提供WS_CAPTION标签来创建一个没有三大按钮的标题栏:

1 [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
2 private static extern int SetWindowLong32(HandleRef hWnd, int nIndex, int dwNewLong);
3
4 [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
5 private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);
6
7 public const int GWL_STYLE = -16;
8 public const long WS_CAPTION = 0x00C00000L;
9
10 public static IntPtr SetWindowLongPtr(HandleRef hWnd, int nIndex, IntPtr dwNewLong)
11 => IntPtr.Size == 8
12 ? SetWindowLongPtr64(hWnd, nIndex, dwNewLong)
13 : new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
14
15 public static void EnableDwmAnimation(Window w)
16 {
17 var myHWND = new WindowInteropHelper(w).Handle;
18 IntPtr myStyle = new(WS_CAPTION);
19 SetWindowLongPtr(new HandleRef(null, myHWND), GWL_STYLE, myStyle);
20 }

然后按照上述的方法添加WindowChrome让客户区覆盖标题栏即可。 (这里提前绘制好了自定义的标题栏样式,你需要自行处理暗色模式的变化等等)

这样WindowStyle就会失效,并且在实现WindowStyle.None的效果同时带上WS_CAPTION标签让DWM认为这是一个“原生”标题栏窗口。经过测试,另外还需加上WS_THICKFRAME|WS_MAXIMIZEBOX|WS_MINIMIZEBOX 等标签让窗口行为更贴近原生。(添加WS_THICKFRAME 在移动窗口时出现系统的窗口布局器)
同样失效的还有ResizeMode.NoResize,如果你需要固定窗口大小,目前暂行的方法是设置WindowsChrome的ResizeBorderThickness="0",如果按照WinAPI的方法加上WS_SYSMENU则会同时带回原生标题栏的三大按钮。(为什么微软对它的解释是标题栏菜单..?)
使用我封装好的附加属性:
在Window标签中添加:(同样地这会使WindowStyle和ResizeMode失效)
local:DwmAnimation.EnableDwmAnimation="True"
三、添加更多窗口行为
以下Demo使用了诺尔大佬的WPF Suite来帮助简化流程。
Demo 1. 创建一个失焦不失效的亚克力材质圆角窗口 (Win11),所有设置如下:


关键部分在ws:WindowOption.Corner="Round"这一句,始终使用圆角窗口,并且拥有原生边框阴影 (右图为对照,无阴影)
手动实现参照官方文档:Apply rounded corners in desktop apps - Windows apps | Microsoft Learn
Demo 2. 添加win11的布局器
在Button中添加 ws:WindowOption.IsMaximumButton="True" 以在鼠标悬停时显示布局器

最后附上测试项目地址:
TwilightLemon/WindowEffectTest: 测试win10/11的模糊效果 (github.com)
参考文章:
[1]. 在 Windows 11 应用中使用的材料 - Windows apps | Microsoft Learn
[2]. [.NET,WPF] 窗体云母, 亚克力, 透明, 混合颜色, 模糊背景, 亮暗色主题全讲 (slimenull.com)
[3]. WPF在win10/11上启用模糊特效 适配Dark/Light Mode - TwilightLemon - 博客园 (cnblogs.com)
......

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名TwilightLemon和原文网址,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类)的更多相关文章
- WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)
WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...
- WPF 应用完全模拟 UWP 的标题栏按钮
WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...
- WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?
原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...
- 从淘宝 UWP 的新功能 -- 比较页面来谈谈 UWP 的窗口多开功能
前言 之前在 剁手党也有春天 -- 淘宝 UWP ”比较“功能诞生记 这篇随笔中介绍了一下 UWP 淘宝的“比较”新功能呱呱坠地的过程.在鲜活的文字背后,其实都是程序员不眠不休的血泪史(有血有泪有史) ...
- 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj
原文 将 WPF.UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj 写过 .NET Standard 类库或者 .NET Core 程序的 ...
- wpf 模拟抖音很火的罗盘时钟,附源码,下载就能跑
wpf 模拟抖音很火的罗盘时钟,附源码 前端时间突然发现,抖音火了个壁纸,就是黑底蕾丝~~~ 错错错,黑底白字的罗盘时钟! 作为程序员的我,也觉得很新颖,所以想空了研究下,这不,空下来了就用wpf, ...
- WPF 使用WindowChrome自定义窗体 保留原生窗体特性
本文大幅度借鉴dino.c大佬的文章 https://www.cnblogs.com/dino623/p/uielements_of_window.html https://www.cnblogs.c ...
- [UWP]使用Acrylic(亚克力)
原文:[UWP]使用Acrylic(亚克力) 1. 前言 在 如何使用Fluent Design System 这篇文章里已经简单介绍过Reveal的用法,这篇再详细介绍其它内容. 自Windows ...
- WPF模拟Office2010文件菜单的TabControl模板
原文:WPF模拟Office2010文件菜单的TabControl模板 这是Office2010中的文件菜单点开后的效果.本文我将以强大的WPF(www.itstrike.cn)来实现类似的效果.希望 ...
- 好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字
原文:好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字 版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csd ...
随机推荐
- Linux设备模型:1、设计思想
背景 搞Linux搞这么久,一直在调试各种各样的驱动.却发现对Linux驱动有太多不够了解的地方.因此转载了 蜗窝科技 的有关文章,作为学习. 内容有少量纠正,样式有做调整. 作者:wowo 发布于: ...
- “科来杯”第九届山东省大学生网络安全技能大赛决赛部分wp
1.损坏的流量包 wireshark打不开,丢进winhex里,找关键字flag 哎,没找到. 那就仔细看看,在最后发现一串类似base64的密文 base64解密 得到flag 2.签到题 一个 ...
- C语言的简单学习
C语言是编译型语言,先编译再运行,通常用gcc进行编译,于是安装了Ubuntu操作系统.至于编辑器,VS Code也能用,先sudo apt install build-essential gdb,再 ...
- C#使用Aspose将Word\HTML 转换成PDF文件
写在前面 Aspose 这个是收费的,直接使用是有水印的 需要用到的dll文件 ==> Aspose.Words.dll.Aspose.HTML.dll.Aspose.Total.lic(授权文 ...
- Ubuntu20.04中 ORBSLAM3的安装和测试
ORBSLAM3 安装以及测试教程(Ubuntu20.04) 1.前期准备工作 1.1安装相关依赖 sudo apt install git cmake gcc g++ mlocate 1.2下载OR ...
- Python获得操作日志的最后几行记录
该方法一般用于获得操作日志的最后几行记录 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 f = open('seek.txt', 'rb') 4 ...
- 基于 Impala 的高性能数仓建设实践之虚拟数仓
导读:本文主要介绍网易数帆 NDH 在 Impala 上实现的虚拟数仓特性,包括资源分组.水平扩展.混合分组和分时复用等功能,可以灵活配置集群资源.均衡节点负载.提高查询并发,并充分利用节点资源. 接 ...
- Python elasticsearch-py类库基础用法
实践环境 https://pypi.org/project/elasticsearch/ pip install elasticsearch==7.6.0 离线安装包及依赖包下载地址: https:/ ...
- python中的字符串和列表
name="1" name='1' name="""1""""" name='''1''' #都为正 ...
- CCStheia添加include路径
一.在系统内找到该路径 二.复制该路径,并更改写法 C:\Users\c1519\workspace_ccstheia\OLED\user_lib 改为: C:/Users/c1519/workspa ...