简体   繁体   English

有没有办法让 WPF 应用程序尊重 Windows 10 中暗/亮主题的系统选择?

[英]Is there any way to make a WPF app respect the system choice of dark/light theme in Windows 10?

Windows 10 recently added a dark mode; Windows 10 最近添加了深色模式; is there any way to make my WPF app respect this setting?有没有办法让我的 WPF 应用程序尊重这个设置? Preferably a switch that could flip it over automatically, but if not, I guess I could read a system setting somewhere and switch to an alternate theme in my code or something...最好是一个可以自动翻转它的开关,但如果没有,我想我可以在某处读取系统设置并切换到我的代码或其他东西中的备用主题......

There is no straightforward API/event to detect dark mode or high contrast mode from wpf.没有直接的 API/事件可以从 wpf 检测暗模式或高对比度模式。 Which is available in uwp.这在 uwp 中可用。 But there is a way to detect the theme changed event by WMI query to watch the registry changes for related registry key.但是有一种方法可以通过 WMI 查询来检测主题更改事件,以查看相关注册表项的注册表更改。 You will find details here .您将在此处找到详细信息。 I have a simplified class by which you can detect the registry changes.我有一个简化的类,您可以通过它检测注册表更改。

public class ThemeWatcher
{
    private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";

    private const string RegistryValueName = "AppsUseLightTheme";
    private static WindowsTheme windowsTheme;

    public WindowsTheme WindowsTheme
    {
        get { return windowsTheme; }
        set { windowsTheme = value; }
    }

    public void StartThemeWatching()
    {
        var currentUser = WindowsIdentity.GetCurrent();
        string query = string.Format(
            CultureInfo.InvariantCulture,
            @"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
            currentUser.User.Value,
            RegistryKeyPath.Replace(@"\", @"\\"),
            RegistryValueName);
        
        try
        {
            windowsTheme = GetWindowsTheme();
            MergeThemeDictionaries(windowsTheme);

            var watcher = new ManagementEventWatcher(query);
            watcher.EventArrived += Watcher_EventArrived;
            SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
            // Start listening for events
            watcher.Start();
        }
        catch (Exception ex)
        {
            // This can fail on Windows 7
            windowsTheme = WindowsTheme.Default;
            
        }

    }

    private void MergeThemeDictionaries(WindowsTheme windowsTheme)
    {
        string appTheme = "Light";
        switch (windowsTheme)
        {
            case WindowsTheme.Light:
                appTheme = "Light";
                break;
            case WindowsTheme.Dark:
                appTheme = "Dark";
                break;
            case WindowsTheme.HighContrast:
                appTheme = "HighContrast";
                break;
        }

        App.Current.Resources.MergedDictionaries[0].Source = new Uri($"/Themes/{appTheme}.xaml", UriKind.Relative);
        
    }

    private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        windowsTheme = GetWindowsTheme();

        MergeThemeDictionaries(windowsTheme);

        ThemeChangedArgument themeChangedArgument = new ThemeChangedArgument();
        themeChangedArgument.WindowsTheme = windowsTheme;

        App.WindowsThemeChanged?.Invoke(this, themeChangedArgument);
        
    }

    private void Watcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        windowsTheme = GetWindowsTheme();

        MergeThemeDictionaries(windowsTheme);

        ThemeChangedArgument themeChangedArgument = new ThemeChangedArgument();
        themeChangedArgument.WindowsTheme = windowsTheme;

        App.WindowsThemeChanged?.Invoke(this, themeChangedArgument);
        
    }

    public WindowsTheme GetWindowsTheme()
    {
        WindowsTheme theme = WindowsTheme.Light;

        try
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath))
            {
                object registryValueObject = key?.GetValue(RegistryValueName);
                if (registryValueObject == null)
                {
                    return WindowsTheme.Light;
                }

                int registryValue = (int)registryValueObject;

                if (SystemParameters.HighContrast)
                    theme = WindowsTheme.HighContrast;

                theme = registryValue > 0 ? WindowsTheme.Light : WindowsTheme.Dark;
            }

            return theme;
        }
        catch (Exception ex)
        {
            return theme;
        }
    }
}

Create realated Enum for logical implementation:为逻辑实现创建真实的枚举:

public enum WindowsTheme
{
    Default = 0,
    Light = 1,
    Dark = 2,
    HighContrast = 3
}

Add Related Resource files to the project.将相关资源文件添加到项目中。

在此处输入图片说明

Define a callback argument that will be passed through event handler when registry change will be occurred.定义将在发生注册表更改时通过事件处理程序传递的回调参数。

public class ThemeChangedArgument
{
    public WindowsTheme WindowsTheme { set; get; }
}

Now start watching theme changed from OnStartup method of App.xaml.cs.现在开始观看从 App.xaml.cs 的 OnStartup 方法更改的主题。

public partial class App : Application
{
  private ThemeWatcher themeWatcher;
  private WindowsTheme systrayTheme = WindowsTheme.Light;
  public static EventHandler<ThemeChangedArgument> WindowsThemeChanged;
  .......

   protected override void OnStartup(StartupEventArgs e)
   {
   .......................
    themeWatcher = new ThemeWatcher();
    systrayTheme = themeWatcher.GetWindowsTheme();
    themeWatcher.StartThemeWatching();

    if(WindowsThemeChanged != null)
    {
        WindowsThemeChanged -= OnWindowsThemeChanged;
    }

    WindowsThemeChanged += OnWindowsThemeChanged;
    .......................
   }

  private void OnWindowsThemeChanged(object sender, ThemeChangedArgument e)
  {
    systrayTheme = e.WindowsTheme;
    //Now do whatever you want to do with this updated theme.
  }
  
        
    protected override void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);

        try
        {
            if (WindowsThemeChanged != null)
            {
                WindowsThemeChanged -= OnWindowsThemeChanged;
            }
            Application.Current?.Shutdown();
            Process.GetCurrentProcess()?.Kill();
        }
        catch (Exception ex)
        {
        
        }
    }

}

Note: We already merged related style resource from ThemeWatcher class with the method MergeThemeDictionaries() due to theme changed event fired.注意:由于触发了主题更改事件,我们已经将来自 ThemeWatcher 类的相关样式资源与方法MergeThemeDictionaries()合并。 You can updated it from here also according to your need.您也可以根据需要从这里更新它。

Call below method to update the UI after resource changes due to runtime.调用以下方法以在由于运行时更改资源后更新 UI。

    private void InvalidedMainWindow()
    {
        if (!Application.Current.Dispatcher.CheckAccess())
        {
            Application.Current.Dispatcher.InvokeAsync(() => InvalidedMainWindow());
            return;
        }

        App.Current.MainWindow.UpdateLayout();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Windows 10周年更新或更高版本中使用WPF应用程序中的浅色或深色主题? - How can I get whether Windows 10 Anniversary Update or later is using its light or dark theme in a WPF app? WPF - 通过单击适当的按钮将主题从暗变为亮 - Avalonia - WPF - Change theme from dark to light by clicking proper button - Avalonia 使用c#在Windows手机(浅色或深色)中的主题 - theme in windows phone(light or dark) using c# 黑暗/光明主题资产限定符 - Dark/Light theme assets qualifiers 如果 Windows 10 使用深色主题 (UWP),则设置 TextBlock 文本 - Set TextBlock Text if Windows 10 uses dark theme (UWP) 有没有办法让Windows 10中的WPF应用程序不那么“丑陋”? - Is there a way to make WPF application less “ugly” in Windows 10? 根据浅色或深色的主题选择更改图像 - Change image based on Theme selection for Light or Dark 使WPF Listview遵循Windows主题 - Make WPF Listview adhere to the Windows theme 如何强制Windows Phone 8应用程序以轻主题运行 - How to force windows phone 8 app to be run in light theme C#Windows Metro应用程序上的Light主题中的FileSavePicker - FileSavePicker in Light theme on C# windows metro App
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM