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

news/2024/11/17 14:28:43/文章来源:https://www.cnblogs.com/TwilightLemon/p/18374495

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

 然后按照上述的方法添加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" 以在鼠标悬停时显示布局器

 

本文的所有效果均可通过诺尔大佬的WPF Suite快速实现,大家快去用呀!

Documentation | EleCho.WpfSuite

 

最后附上测试项目地址:

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和原文网址,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/785656.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis学习(一)

1.通用命令keys * del k1 exists k1 expipe k1 ttl k12.String类型String类型的常见命令 set k1 v1 添加键值对 get k1 v1 获得键值对对应的值 mset k1 v1 k2 v2 一次性设置多个值 mget k1 k2 k3 一次性获取多个键值对的值 incr k1 让k1自增 incrby k1 2 按步长2自增 dec…

C# WebSocket Fleck 源码解读

最近在维护公司旧项目,偶然发现使用Fleck实现的WebSocket主动推送功能,(由于前端页面关闭时WebSocket Server中执行了多次OnClone事件回调并且打印了大量的关闭日志,),后来我特地看了源码,这里做一些分享 github: https://github.com/statianzo/Fleck在源码中,作者在 Sam…

SuperCLUE整理

参考信息: git:https://github.com/CLUEbenchmark/SuperCLUE 官网:https://www.cluebenchmarks.com/superclue.html 23年7月论文 :https://arxiv.org/pdf/2307.15020: 24年4月报告:https://www.cluebenchmarks.com/superclue_2404 24年7月报告:https://www.cluebenchmar…

20240819编译 链接 单片机执行的第一个语句

2024年8月中旬在宝鸡出差,在B站上学习了下。 在linux操作系统中,gdb是调试器,gcc是编译器。 Linux 操作系统 将main.elf文件加载到内存中以后,操作系统为其分配进程,然后main函数就开始执行了。输入 layout asm 则显示出来汇编代码 键入 “starti”的时候 进入到程序里面的…

回溯part02

今天继续学习了回溯:组合求和的进阶 元素可以重复使用:backtracking(candidates, target, sum, i); // 不用i+1了,表示可以重复读取当前的数 数组去重:首先数组排序,然后使用used 分割回文子串问题,抽象为组合问题,注意如何判断是否是回文子串5. 39 组合总和(元素可重复…

2.系统定时器

系统定时器SYSTEM文件夹介绍1,sys文件夹介绍sys_nvic_set_vector_table():设置中断向量表地址sys_intx_enable():开启所有中断sys_intx_disable():关闭所有中断(但是不包括fault和NMI中断)sys_wfi_set():执行: WFI指令(执行完该指令进入低功耗状态)sys_standby():进入待机…

Terraform - 初解Terraform - 安装

开发环境配置 Note:安装环境为windows系统64位笔记本电脑 1.安装适用于 Windows 的 Terraform: https://developer.hashicorp.com/terraform/install 2.下载 Terraform: https://releases.hashicorp.com/terraform/1.9.4/terraform_1.9.4_windows_amd64.zip 3.从下载中,将…

python 05-标准库:csv、json、sqlite3、datetime模块

csv模块import csv# data.csv不存在时,会现在当前目录下创建一个data.csv文件 with open("data.csv", "w", encoding=utf-8) as file:writer = csv.writer(file)writer.writerow(["trasanction_id", "product_id", "price_id&qu…

初识方法

1.方法是什么 1.java方法是语句的合集,放在一起执行一个功能。 2.方法包含于类或对象中 3.方法在程序中被创建,在其他地方被引用 2.方法的原则:原子性 一个方法只能完成一个功能 3.在main方法外计算 1.先在main外面书写一个加法方法2.再在main里面直接输出加法方法总览:

Terraform - 初解Terraform - 简介

前言 云计算 主要提供计算服务,例如服务器、存储、数据库、网络等等; 对于云服务,通常只需使用多少支付多少,从而帮助降低运营成本,使基础设施更有效地运行,并能根据业务需求的变化调整对服务的使用。公有云: 多租户面向个人或企业由第三方云服务商托管和运营。 私有云: …

045、Vue3+TypeScript基础,pinia库中使用$subscribe订阅数据变动

01、main.js代码如下:// 引入createApp用于创建Vue实例 import {createApp} from vue // 引入App.vue根组件 import App from ./App.vue//第一步:引入pinia import {createPinia} from piniaconst app = createApp(App);//第二步:创建pinia实例 const pinia = createPinia()…