WinUI3应用Mica或Acrylic材料
不管怎么说,我用于学习WinUI3的项目WSAFileLink已经告一段落了。接下来的几篇WinUI相关博文主要是记录这当中的一些实现。
虽然我依旧觉得Microsoft Doc写的并不算好,但是看习惯了就还好。至于WinUI3本身,虽然和之前比好很多,但仍然不算成熟,社区里也经常能看到弃坑为结尾的issue。
两种材料的简介
Mica和Acrylic是Windows11中新提出的两个表面视觉效果,类似于当年的Aero。
Mica - 云母
Mica是一种不透明的材料,它结合用户的主题和壁纸,以创建高度个性化的外观。
Mica专为性能设计,因此它只捕获一次背景墙纸来创建其可视化效果。因此微软官方建议Mica用于应用的基础层,尤其是标题栏区域。
Acrylic - 亚克力
Acrylic是一种半透明材料,类似亚克力玻璃的效果。
Acrylic的效果更加绚丽,它会捕获应用下面所有的画面来创建其可视化效果。因为性能代价,微软官方它仅用于浮出控件和上下文菜单等暂时性的轻型消除图面。
如何在背景上应用材料
重要 API: MicaController类、 DesktopAcrylicController类、 SystemBackdropConfiguration类
想要在应用的背景上使用这些材料,要使用实现ISystemBackdropController
接口的控制器,即:MicaController
或DesktopAcrylicController
。这些类管理系统背景材料的呈现以及材料系统策略的处理。
如果你想用Mica作为背景材料,那么你需要创建MicaController
对象。相应的,如果要使用Acrylic,那么要创建DesktopAcrylicController
对象。
以创建Mica为例:
MicaController m_backdropController;
bool TrySetSystemBackdrop()
{
if (MicaController.IsSupported())
{
...
m_backdropController = new MicaController();
...
}
}
若要使用Mica的Mica Alt变体,请创建一个MicaController
对象并将Kind
属性设置为MicaKind.BaseAlt
:
MicaController m_backdropController;
bool TrySetSystemBackdrop()
{
if (MicaController.IsSupported())
{
...
m_backdropController = new MicaController()
{
Kind = MicaKind.BaseAlt
};
...
}
}
如果想要Acrylic材料,那么:
DesktopAcrylicController m_backdropController;
bool TrySetSystemBackdrop()
{
if (DesktopAcrylicController.IsSupported())
{
...
m_backdropController = new DesktopAcrylicController();
...
}
}
默认情况下,控制器会响应系统浅色和深色主题。 若要替代此行为,可以在控制器上设置以下属性:
FallbackColor
LuminosityOpacity
TintColor
TintOpacity
注意事项
系统支持
应用运行的系统必须支持背景材料。 调用MicaController.IsSupported
或DesktopAcrylicController.IsSupported
方法,以确保在运行时支持背景材料。
有效目标
必须提供实现ICompositionSupportsSystemBackdrop
接口的目标。 在XAML应用中,XAML窗口实现此接口,并用作背景目标。
SystemBackdropConfiguration
对象
SystemBackdropConfiguration
为系统背景控制器提供特定于应用的策略信息,以正确配置系统背景材料。
DispatcherQueue
对象。
主XAML线程上需要一个可用的Windows.System.DispatcherQueue
。 WindowsSystemDispatcherQueueHelper
请参阅示例代码中的类,或在WinUI3库示例中。
官方示例:在 Windows AppSDK/WinUI 3 应用中使用 Mica
对这个示例加上一个简单的判断条件,就能根据设定来切换背景材料。
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
using System.Runtime.InteropServices; // For DllImport
using WinRT; // required to support Window.As<ICompositionSupportsSystemBackdrop>()
public sealed partial class MainWindow : Window
{
WindowsSystemDispatcherQueueHelper m_wsdqHelper; // See below for implementation.
MicaController m_backdropController;
SystemBackdropConfiguration m_configurationSource;
public MainWindow()
{
this.InitializeComponent();
TrySetSystemBackdrop();
}
bool TrySetSystemBackdrop()
{
if (Microsoft.UI.Composition.SystemBackdrops.MicaController.IsSupported())
{
m_wsdqHelper = new WindowsSystemDispatcherQueueHelper();
m_wsdqHelper.EnsureWindowsSystemDispatcherQueueController();
// Create the policy object.
m_configurationSource = new SystemBackdropConfiguration();
this.Activated += Window_Activated;
this.Closed += Window_Closed;
((FrameworkElement)this.Content).ActualThemeChanged += Window_ThemeChanged;
// Initial configuration state.
m_configurationSource.IsInputActive = true;
SetConfigurationSourceTheme();
m_backdropController = new Microsoft.UI.Composition.SystemBackdrops.MicaController()
// Enable the system backdrop.
// Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
m_backdropController.AddSystemBackdropTarget(this.As<Microsoft.UI.Composition.ICompositionSupportsSystemBackdrop>());
m_backdropController.SetSystemBackdropConfiguration(m_configurationSource);
return true; // succeeded
}
return false; // Mica is not supported on this system
}
private void Window_Activated(object sender, WindowActivatedEventArgs args)
{
m_configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
}
private void Window_Closed(object sender, WindowEventArgs args)
{
// Make sure any Mica/Acrylic controller is disposed
// so it doesn't try to use this closed window.
if (m_backdropController != null)
{
m_backdropController.Dispose();
m_backdropController = null;
}
this.Activated -= Window_Activated;
m_configurationSource = null;
}
private void Window_ThemeChanged(FrameworkElement sender, object args)
{
if (m_configurationSource != null)
{
SetConfigurationSourceTheme();
}
}
private void SetConfigurationSourceTheme()
{
switch (((FrameworkElement)this.Content).ActualTheme)
{
case ElementTheme.Dark: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Dark; break;
case ElementTheme.Light: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Light; break;
case ElementTheme.Default: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Default; break;
}
}
}
class WindowsSystemDispatcherQueueHelper
{
[StructLayout(LayoutKind.Sequential)]
struct DispatcherQueueOptions
{
internal int dwSize;
internal int threadType;
internal int apartmentType;
}
[DllImport("CoreMessaging.dll")]
private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object dispatcherQueueController);
object m_dispatcherQueueController = null;
public void EnsureWindowsSystemDispatcherQueueController()
{
if (Windows.System.DispatcherQueue.GetForCurrentThread() != null)
{
// one already exists, so we'll just use it.
return;
}
if (m_dispatcherQueueController == null)
{
DispatcherQueueOptions options;
options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
options.threadType = 2; // DQTYPE_THREAD_CURRENT
options.apartmentType = 2; // DQTAT_COM_STA
CreateDispatcherQueueController(options, ref m_dispatcherQueueController);
}
}
}
参考
1、将 SystemBackdropController 与 WinUI 3 XAML 配合使用 - Windows apps | Microsoft Learn