先看效果圖:
大致思路是:透過反射獲取Popup
內部的原生視窗控制代碼
,然後透過前文已經實現的WindowMaterial
類來應用視窗特效;對於ToolTip
,為了保持其易用性,我使用了附加屬性+全域性樣式
的方式來實現,ToolTip
也是一個特殊的Popup
.
前文連結:WPF 模擬UWP原生視窗樣式——亞克力|雲母材質、自定義標題欄樣式、原生DWM動畫 (附我封裝好的類)
本文的Demo:
TwilightLemon/WindowEffectTest: 測試win10/11的模糊效果 (github.com)
一、獲取原生視窗控制代碼
透過查閱.NET原始碼得知,Popup內部透過一個型別為PopupSecurityHelper
的私有欄位_secHelper
來管理視窗hWnd
,並且在建立完成之時會觸發Popup.Opened
事件。
透過反射來獲取視窗控制代碼:
const BindingFlags privateInstanceFlag = BindingFlags.NonPublic | BindingFlags.Instance; public static IntPtr GetNativeWindowHwnd(this Popup popup) { //獲取Popup內部的_secHelper欄位Info var field = typeof(Popup).GetField("_secHelper", privateInstanceFlag); if (field != null) { //獲取popup的_secHelper欄位值 if (field.GetValue(popup) is { } _secHelper) { //獲取_secHelper的Handle屬性Info if (_secHelper.GetType().GetProperty("Handle", privateInstanceFlag) is { } prop) { if (prop.GetValue(_secHelper) is IntPtr handle) { //返回控制代碼 return handle; } } } } //未找到 return IntPtr.Zero; }
同樣地,能在ToolTip
內部找到私有欄位_parentPopup
public static IntPtr GetNativeWindowHwnd(this ToolTip tip) { var field=tip.GetType().GetField("_parentPopup", privateInstanceFlag); if (field != null) { if(field.GetValue(tip) is Popup{ } popup) { return popup.GetNativeWindowHwnd(); } } return IntPtr.Zero; }
二、應用WindowMaterial特效
有了視窗控制代碼那麼一切都好辦了,直接呼叫我封裝好的WindowMaterial
類,如果你想了解更多請檢視前文。
public static void SetPopupWindowMaterial(IntPtr hwnd,Color compositionColor, MaterialApis.WindowCorner corner= MaterialApis.WindowCorner.Round) { if (hwnd != IntPtr.Zero) { int hexColor = compositionColor.ToHexColor(); var hwndSource = HwndSource.FromHwnd(hwnd); //---- MaterialApis.SetWindowProperties(hwndSource, 0); MaterialApis.SetWindowComposition(hwnd, true, hexColor); //---- MaterialApis.SetWindowCorner(hwnd, corner); } }
根據微軟的設計規範,這裡預設對普通Popup使用圓角,對ToolTip使用小圓角,使用亞克力材質並附加compositionColor。
在github中獲取完整的WindowMaterial.cs,我可能會不定期地更新它:WindowEffectTest/WindowMaterial.cs at master · TwilightLemon/WindowEffectTest (github.com)
如果你想使用Mica或MicaAlt等材質則將上面框起來的程式碼替換為:
MaterialApis.SetWindowProperties(hwndSource, -1); MaterialApis.SetBackDropType(hwnd, MaterialType.Mica); MaterialApis.SetDarkMode(hwnd, isDarkMode: true);
三、沒錯我又封裝了一個即開即用的類
在Demo中檢視封裝好的類:WindowEffectTest/FluentPopup.cs at master · TwilightLemon/WindowEffectTest (github.com)