根据微软开发文档「在 Win32 应用中支持深色和浅色主题」,WinUI 3 可以实现深色和浅色主题的切换,但是标题栏却不在其列,好在我们有办法通过自定义标题栏来实现深色模式。然而,问题在于我们通常不会去自定义标题栏菜单,导致菜单与系统或软件的主题不匹配。

本文使用 Windows 的非公开 API 来支持标题栏原生菜单的深色主题。

相关 API

暗色模式 API 参考 ysc3839/win32-darkmode

C++
// 1903 18362
enum PreferredAppMode
{
    Default,
    AllowDark,
    ForceDark,
    ForceLight,
    Max
};

using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903
using fnFlushMenuThemes = void (WINAPI *)(); // ordinal 136

我们主要关注 PreferredAppMode 的三个值:AllowDarkForceDarkForceLight。它们分别表示跟随系统主题,强制深色主题,强制浅色主题。

实现

创建调用代码:

C#
private enum PreferredAppMode
{
    Default,
    AllowDark,
    ForceDark,
    ForceLight,
    Max
};

[DllImport("uxtheme.dll", EntryPoint = "#135")]
private static extern IntPtr SetPreferredAppMode(PreferredAppMode preferredAppMode);

[DllImport("uxtheme.dll", EntryPoint = "#136")]
private static extern IntPtr FlushMenuThemes();

创建辅助方法:

C#
public static void UpdateTitleBarContextMenu(Microsoft.UI.Xaml.ElementTheme theme)
{
    var mode = theme switch
    {
        ElementTheme.Light => PreferredAppMode.ForceLight,
        ElementTheme.Dark  => PreferredAppMode.ForceDark,
        _                  => PreferredAppMode.AllowDark,
    };
    SetPreferredAppMode(mode);
    FlushMenuThemes();
}

这样我们就可以通过传入元素主题来刷新菜单主题。

效果如下:

标题栏菜单深色主题演示