先看一下最终效果,左图为使用亚克力材质并添加组合颜色的效果;右图为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)

  下面给出我封装好的附加属性:

  WindowEffectTest/WindowEffectTest/WindowMaterial.cs at master · TwilightLemon/WindowEffectTest (github.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则会同时带回原生标题栏的三大按钮。(为什么微软对它的解释是标题栏菜单..?)

  使用我封装好的附加属性:

  WindowEffectTest/WindowEffectTest/DwmAnimation.cs at master · TwilightLemon/WindowEffectTest (github.com)

  在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动画 (附我封装好的类)的更多相关文章

  1. WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)

    WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...

  2. WPF 应用完全模拟 UWP 的标题栏按钮

    WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...

  3. WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

    原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...

  4. 从淘宝 UWP 的新功能 -- 比较页面来谈谈 UWP 的窗口多开功能

    前言 之前在 剁手党也有春天 -- 淘宝 UWP ”比较“功能诞生记 这篇随笔中介绍了一下 UWP 淘宝的“比较”新功能呱呱坠地的过程.在鲜活的文字背后,其实都是程序员不眠不休的血泪史(有血有泪有史) ...

  5. 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj

    原文 将 WPF.UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj 写过 .NET Standard 类库或者 .NET Core 程序的 ...

  6. wpf 模拟抖音很火的罗盘时钟,附源码,下载就能跑

    wpf 模拟抖音很火的罗盘时钟,附源码 前端时间突然发现,抖音火了个壁纸,就是黑底蕾丝~~~  错错错,黑底白字的罗盘时钟! 作为程序员的我,也觉得很新颖,所以想空了研究下,这不,空下来了就用wpf, ...

  7. WPF 使用WindowChrome自定义窗体 保留原生窗体特性

    本文大幅度借鉴dino.c大佬的文章 https://www.cnblogs.com/dino623/p/uielements_of_window.html https://www.cnblogs.c ...

  8. [UWP]使用Acrylic(亚克力)

    原文:[UWP]使用Acrylic(亚克力) 1. 前言 在 如何使用Fluent Design System 这篇文章里已经简单介绍过Reveal的用法,这篇再详细介绍其它内容. 自Windows ...

  9. WPF模拟Office2010文件菜单的TabControl模板

    原文:WPF模拟Office2010文件菜单的TabControl模板 这是Office2010中的文件菜单点开后的效果.本文我将以强大的WPF(www.itstrike.cn)来实现类似的效果.希望 ...

  10. 好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字

    原文:好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字 版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csd ...

随机推荐

  1. nginx 如何利用gzip压缩配置来优化网站访问速度

    前言: 最近公司设计的网站前端是基于nuxt架构的,部署到nginx上后,首页的访问以及二级页面的访问极慢,f12观察后发现主要是一些js页面加载极慢拉低了网站的访问速度,于是便想到利用nginx里的 ...

  2. 全志T113-i+玄铁HiFi4开发板硬件说明书(2)

    前 言 本文档主要介绍开发板硬件接口资源以及设计注意事项等内容,测试板卡为全志T113-i+玄铁HiFi4开发板,由于篇幅问题,本篇文章共分为上下两集,点击账户可查看更多内容详情,开发问题欢迎留言,感 ...

  3. 使用kafka作为生产者生产数据_到_hbase

    配置文件: agent.sources = r1agent.sinks = k1agent.channels = c1 ## sources configagent.sources.r1.type = ...

  4. GaussDB(DWS)性能调优,解决DM区大内存占用问题

    本文分享自华为云社区<GaussDB(DWS)性能调优:DM区优化案例--维度表关联条件存在会计期>,作者: O泡果奶~. 当前DM(P1.P3.CBGDM)存在维度表与主表关联时使用会计 ...

  5. AT_abc215F 题解

    考虑二分答案. 假设当前二分的答案为 \(k\),那么对于每个点,距离大于等于 \(k\) 的点构成了平面上 \(4\) 个子平面. 那么只需查询子平面中是否存在点即可,类似于窗口的星星,把问题转换成 ...

  6. Java-C3P0和Druid连接池的运用

    1.概念 其实就是一个容器(集合),存放数据库连接的容器 当系统初始化好后,容器被创建,容器会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器. 好 ...

  7. adorner 使用示例

    模块介绍 adorner 是一个现代轻量级的 Python 装饰器辅助模块. 目前该模块仅实现了 4 个类,对应着 4 个功能:制造装饰器.执行计时.函数缓存.捕获重试. 仓库地址:https://g ...

  8. element-plus如何隐藏el-row

    在 Element Plus 中,el-row 是用于布局的组件,如果你想要隐藏 el-row,你可以使用 CSS 的 display 属性将其设置为 none.以下是一个简单的示例: <tem ...

  9. [oeasy]python0070_动态类型_静态类型_编译_运行_匈牙利命名法

    动态类型_静态类型 回忆上次内容   上次了解了 帮助文档的 生成 开头的三引号注释 可以生成 帮助文档 文档 可以写成网页   python3 本身 也有 在线的帮助手册   目前的程序 提高了 可 ...

  10. oeasy 教您玩转linux 010304 图形界面 xfce

    我们来回顾一下 上一部分我们都讲了什么? 讲了文件管理器和命令行终端互相交互 用命令nautilus在文件管理器打开某路径 这次我们来看看 图形用户界面(GUI)的情况 图形界面和发行版的关系 一个发 ...