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

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

时间:2024-08-22 23:26:36浏览次数:13  
标签:原生 窗口 自定义 样式 标题栏 使用 WS 亚克力 材质

  先看一下最终效果,左图为使用亚克力材质并添加组合颜色的效果;右图为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和原文网址,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

 

标签:原生,窗口,自定义,样式,标题栏,使用,WS,亚克力,材质
From: https://www.cnblogs.com/TwilightLemon/p/18374495

相关文章

  • 探索HarmonyOS中的列表组件及其自定义特性
    在现代移动应用中,List组件是数据列表的关键元素。HarmonyOS中的List组件不仅具备传统的列表功能,还提供了丰富的自定义选项,允许开发者根据需求灵活调整列表的行为和外观展示。本文将探讨HarmonyOS中列举组件的自定义特性,包括自定义项布局、动态加载数据、多列布局、拖拽排序......
  • 统一多层网关好处多,阿里云云原生 API 网关打造全能型网关
    作者:问思、望宸网关承载了业务开发和后端运维的诸多需求,例如路由管理、流量调度、API管理、入口安全管理等,另外网关侧也需要结合服务治理来保障全链路的稳定性。这就造成了大部分企业采用多层网关架构,对性能优化、成本管理、运维监控、故障排查都带来了诸多挑战。因此,多层网关的......
  • vant3升级vant4后,使用Toast、Dialog报样式不存在异常解决方法
    异常现象:vant3升级vant4,直接采用v4的方法使用showToast/showDialog,但直接就报错了,如下:[vite]Internalservererror:Failedtoresolveimport"E:/git_sh/project_code/node_modules/vant/es/show-confirm-dialog/style"from"src\service\index.ts".Doesthefile......
  • 原生js实现下滑到当前模块进度条填充
    <divstyle="height:1500px;"></div><divclass="progress-container"><divclass="progress-bar"data-progress="90%"><pclass="progress-text">GoogleAds在Google搜索引擎上覆盖超过90%......
  • antd5版本修改Table组件滚动条样式
    因为项目需求,要将Table组件的样式修改为UI图所给的效果,但是怎么写都不生效最后发现在 .ant-table的样式中设了scrollbar-color,只要把这里的样式设为scrollbar-color:auto;然后再改.ant-table-body里面滚动条的样式,就可以了.ant-table-body{&::-w......
  • QT自定义结构体的传递
    方法1:使用Q_DECLARE_METATYPE(TestResult)进行声明1.1声明#ifndefTESTRESULT_H#defineTESTRESULT_H#include<QDateTime>#include<QString>#include<QMetaType>//用于测试时更新内容,作为结果导出的依据classTestResult{public:int_nid=0;int_coun......
  • uniapp取消默认的原生导航栏
     pages.json:{ "pages":[ { "path":"pages/index/index", "style":{ "navigationBarTitleText":"uni-app", "navigationStyle":"custom" } } ], "glob......
  • 【python】PyQt5中的QFrame控件,控制图形的边框样式、阴影效果、形状等属性
    ✨✨欢迎大家来到景天科技苑✨✨......
  • 织梦dedeCMS怎么使用arclist标签调用自定义字段
    {dede:arclistrow='10'titlelen='24'orderby='pubdate'idlist=''col='2'}[field:textlink/]([field:pubdatefunction=MyDate('m-d',@me)/])<br/>{/dede:arclist}row=‘10’返回文档列表总数typeid=‘’栏目ID......
  • mybatis-plus配置自定义sqlInjector(使用InsertBatchSomeColumn),出现Invalid bound stat
    项目一开始未引入mybatis-plus,使用的是mybatis,配置文件为xml,有一个配置类中配置了SqlSessionFactory的相关内容。引入mybatis-plus后,想使用InsertBatchSomeColumn遇到Invalidboundstatement(notfound),多处配置发现没有效果并依旧报错,最终在刚才的配置类中的SqlSessionFact......