WinUI3应用运行时切换主题,系统上下文菜单未更新求助
WinUI3 运行时切换主题同步标题栏右键菜单的解决方案
问题原因
SetPreferredAppMode(uxtheme.dll的#135入口)是仅在进程初始化阶段生效的API,它负责设置进程级的主题偏好,但运行时调用无法触发系统托管的非客户区元素(比如标题栏右键菜单)的主题重绘——这类控件属于窗口管理器维护范畴,不受应用自身UI刷新逻辑影响。
可行解决方案
要在运行时同步标题栏右键菜单的主题,需要结合进程级主题设置、应用主题切换,以及强制刷新窗口非客户区三个步骤:
确保应用自身主题正确切换
先设置WinUI3应用的RequestedTheme,这是应用UI主题切换的基础:Application.Current.RequestedTheme = ApplicationTheme.Dark; // 切换为深色,或设为Light切换为浅色调用SetPreferredAppMode更新进程主题偏好
运行时调用该API仍会更新进程的主题标记,为后续系统控件刷新做准备:SetPreferredAppMode(2); // 2=强制深色,1=强制浅色,0=跟随系统强制刷新窗口非客户区
通过DwmSetWindowAttribute设置窗口的沉浸式深色模式属性,触发系统重绘标题栏及关联的右键菜单:
首先定义所需的API:[DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; [DllImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true, CharSet = CharSet.Unicode)] private static extern int SetPreferredAppMode(int preferredAppMode); [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);然后在切换主题的按钮点击事件中执行完整逻辑:
private void ToggleThemeButton_Click(object sender, RoutedEventArgs e) { bool isCurrentDark = Application.Current.RequestedTheme == ApplicationTheme.Dark; // 切换到相反主题 var targetTheme = isCurrentDark ? ApplicationTheme.Light : ApplicationTheme.Dark; int targetAppMode = isCurrentDark ? 1 : 2; Application.Current.RequestedTheme = targetTheme; SetPreferredAppMode(targetAppMode); // 获取当前窗口句柄 var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); // 设置沉浸式深色模式属性 int useDarkMode = isCurrentDark ? 0 : 1; DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ref useDarkMode, sizeof(int)); // 发送WM_NCACTIVATE消息强制刷新非客户区 SendMessage(hwnd, 0x86, IntPtr.Zero, IntPtr.Zero); }
系统应用的实现逻辑
Windows设置等系统应用直接与系统主题服务绑定,当系统主题变更时,系统会主动推送通知,触发所有系统控件同步更新主题;第三方应用则需要通过手动触发非客户区刷新的方式,同步系统托管控件的主题状态。
内容的提问来源于stack exchange,提问作者Chibako
相关产品推荐
相关产品推荐

