WinUI3应用Mica或Acrylic材料

不管怎么说,我用于学习WinUI3的项目WSAFileLink已经告一段落了。接下来的几篇WinUI相关博文主要是记录这当中的一些实现。

虽然我依旧觉得Microsoft Doc写的并不算好,但是看习惯了就还好。至于WinUI3本身,虽然和之前比好很多,但仍然不算成熟,社区里也经常能看到弃坑为结尾的issue。

两种材料的简介

Mica和Acrylic是Windows11中新提出的两个表面视觉效果,类似于当年的Aero。

Mica - 云母

Mica是一种不透明的材料,它结合用户的主题和壁纸,以创建高度个性化的外观。

Mica专为性能设计,因此它只捕获一次背景墙纸来创建其可视化效果。因此微软官方建议Mica用于应用的基础层,尤其是标题栏区域。

Acrylic - 亚克力

Acrylic是一种半透明材料,类似亚克力玻璃的效果。

Acrylic的效果更加绚丽,它会捕获应用下面所有的画面来创建其可视化效果。因为性能代价,微软官方它仅用于浮出控件和上下文菜单等暂时性的轻型消除图面。

如何在背景上应用材料

重要 APIMicaController类DesktopAcrylicController类SystemBackdropConfiguration类

想要在应用的背景上使用这些材料,要使用实现ISystemBackdropController接口的控制器,即:MicaControllerDesktopAcrylicController。这些类管理系统背景材料的呈现以及材料系统策略的处理。

如果你想用Mica作为背景材料,那么你需要创建MicaController对象。相应的,如果要使用Acrylic,那么要创建DesktopAcrylicController对象。

以创建Mica为例:

1
2
3
4
5
6
7
8
9
10
11
MicaController m_backdropController;

bool TrySetSystemBackdrop()
{
if (MicaController.IsSupported())
{
...
m_backdropController = new MicaController();
...
}
}

若要使用Mica的Mica Alt变体,请创建一个MicaController对象并将Kind属性设置为MicaKind.BaseAlt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MicaController m_backdropController;

bool TrySetSystemBackdrop()
{
if (MicaController.IsSupported())
{
...
m_backdropController = new MicaController()
{
Kind = MicaKind.BaseAlt
};
...
}
}

如果想要Acrylic材料,那么:

1
2
3
4
5
6
7
8
9
10
11
DesktopAcrylicController m_backdropController;

bool TrySetSystemBackdrop()
{
if (DesktopAcrylicController.IsSupported())
{
...
m_backdropController = new DesktopAcrylicController();
...
}
}

默认情况下,控制器会响应系统浅色和深色主题。 若要替代此行为,可以在控制器上设置以下属性:

  • FallbackColor
  • LuminosityOpacity
  • TintColor
  • TintOpacity

注意事项

系统支持

应用运行的系统必须支持背景材料。 调用MicaController.IsSupportedDesktopAcrylicController.IsSupported方法,以确保在运行时支持背景材料。

有效目标

必须提供实现ICompositionSupportsSystemBackdrop接口的目标。 在XAML应用中,XAML窗口实现此接口,并用作背景目标。

SystemBackdropConfiguration对象

SystemBackdropConfiguration为系统背景控制器提供特定于应用的策略信息,以正确配置系统背景材料。

DispatcherQueue对象。

主XAML线程上需要一个可用的Windows.System.DispatcherQueueWindowsSystemDispatcherQueueHelper请参阅示例代码中的类,或在WinUI3库示例中。

官方示例:在 Windows AppSDK/WinUI 3 应用中使用 Mica

对这个示例加上一个简单的判断条件,就能根据设定来切换背景材料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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