翼度科技»论坛 编程开发 .net 查看内容

WPF中为Popup和ToolTip使用WindowMaterial特效 win10/win11

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
先看效果图:

大致思路是:通过反射获取Popup内部的原生窗口句柄,然后通过前文已经实现的WindowMaterial类来应用窗口特效;对于ToolTip,为了保持其易用性,我使用了附加属性+全局样式的方式来实现,ToolTip也是一个特殊的Popup.
前文链接:WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类)
本文的Demo:
TwilightLemon/WindowEffectTest: 测试win10/11的模糊效果 (github.com)
一、获取原生窗口句柄

通过查阅.NET源码得知,Popup内部通过一个类型为PopupSecurityHelper的私有字段_secHelper来管理窗口hWnd,并且在创建完成之时会触发Popup.Opened事件。
通过反射来获取窗口句柄:
  1. const BindingFlags privateInstanceFlag = BindingFlags.NonPublic | BindingFlags.Instance;
  2. public static IntPtr GetNativeWindowHwnd(this Popup popup)
  3. {
  4.     //获取Popup内部的_secHelper字段Info
  5.     var field = typeof(Popup).GetField("_secHelper", privateInstanceFlag);
  6.     if (field != null)
  7.     {
  8.         //获取popup的_secHelper字段值
  9.         if (field.GetValue(popup) is { } _secHelper)
  10.         {
  11.             //获取_secHelper的Handle属性Info
  12.             if (_secHelper.GetType().GetProperty("Handle", privateInstanceFlag) is { } prop)
  13.             {
  14.                 if (prop.GetValue(_secHelper) is IntPtr handle)
  15.                 {
  16.                     //返回句柄
  17.                     return handle;
  18.                 }
  19.             }
  20.         }
  21.     }
  22.     //未找到
  23.     return IntPtr.Zero;
  24. }
复制代码
同样地,能在ToolTip内部找到私有字段_parentPopup
  1. public static IntPtr GetNativeWindowHwnd(this ToolTip tip)
  2. {
  3.     var field=tip.GetType().GetField("_parentPopup", privateInstanceFlag);
  4.     if (field != null)
  5.     {
  6.         if(field.GetValue(tip) is Popup{ } popup)
  7.         {
  8.             return popup.GetNativeWindowHwnd();
  9.         }
  10.     }
  11.     return IntPtr.Zero;
  12. }
复制代码
二、应用WindowMaterial特效

有了窗口句柄那么一切都好办了,直接调用我封装好的WindowMaterial类,如果你想了解更多请查看前文。
  1. public static void SetPopupWindowMaterial(IntPtr hwnd,Color compositionColor,
  2.      MaterialApis.WindowCorner corner= MaterialApis.WindowCorner.Round)
  3. {
  4.     if (hwnd != IntPtr.Zero)
  5.     {
  6.         int hexColor = compositionColor.ToHexColor();
  7.         var hwndSource = HwndSource.FromHwnd(hwnd);
  8.         //----
  9.         MaterialApis.SetWindowProperties(hwndSource, 0);
  10.         MaterialApis.SetWindowComposition(hwnd, true, hexColor);
  11.         //----
  12.         MaterialApis.SetWindowCorner(hwnd, corner);
  13.     }
  14. }
复制代码
根据微软的设计规范,这里默认对普通Popup使用圆角,对ToolTip使用小圆角,使用亚克力材质并附加compositionColor。
在github中获取完整的WindowMaterial.cs,我可能会不定期地更新它:WindowEffectTest/WindowMaterial.cs at master · TwilightLemon/WindowEffectTest (github.com)
如果你想使用Mica或MicaAlt等材质则将上面框起来的代码替换为:
  1. MaterialApis.SetWindowProperties(hwndSource, -1);
  2. MaterialApis.SetBackDropType(hwnd, MaterialType.Mica);
  3. MaterialApis.SetDarkMode(hwnd, isDarkMode: true);
复制代码
三、没错我又封装了一个即开即用的类

在Demo中查看封装好的类:WindowEffectTest/FluentPopup.cs at master · TwilightLemon/WindowEffectTest (github.com)
使用FluentPopup
  1. <local:FluentPopup x:Name="testPopup"
  2.                    StaysOpen="False"
  3.                    Placement="Mouse"
  4.                    Background="{DynamicResource PopupWindowBackground}">
  5.     <Grid Height="120" Width="180">
  6.         <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
  7.             nihao
  8.         </TextBlock>
  9.     </Grid>
  10. </local:FluentPopup>
复制代码
其中,Background属性是我自定义的一个依赖属性,只允许使用SolidColorBrush,用于设置亚克力特效的CompositionColor。
如果你希望像Demo中那样让Popup失焦自动关闭,可以设置StaysOpen为False,在后台打开Popup时使用:
  1. private async void ShowPopupBtn_Click(object sender, RoutedEventArgs e)
  2. {
  3.     await Task.Yield();
  4.     testPopup.IsOpen = true;
  5. }
复制代码
关于为什么要加上await Task.Tield(),可以看看吕毅大佬的文章:一点点从坑里爬出来:如何正确打开 WPF 里的 Popup? - Walterlv
在全局内使用FluentToolTip

我自定义了一个附加属性FluentTooltip.UseFluentStyle,你只需要在App.xaml中设置即可全局生效: 这里的PopupWindowBackground和ForeColor是我自定义的颜色资源,你可以根据自己的需要来设置。
同样地Background仅支持SolidColorBrush。
  1. [/code]这样你就可以方便地创建一个Fluent风格的ToolTip了:
  2. [code]
复制代码
 

参考链接

Popup.cs at Dotnet Source
ToolTip.cs at Dotnet Source
一点点从坑里爬出来:如何正确打开 WPF 里的 Popup? - Walterlv
WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类) - TwilightLemon
 

  本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名TwilightLemon(https://blog.twlmgatito.cn),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
文章及其代码仓库可能不时更新,查看原文:WPF中为Popup和ToolTip使用WindowMaterial特效 win10/win11 - Twlm's Blog (twlmgatito.cn)

来源:https://www.cnblogs.com/TwilightLemon/p/18468252
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具