簡體   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 最近添加了深色模式; 有沒有辦法讓我的 WPF 應用程序尊重這個設置? 最好是一個可以自動翻轉它的開關,但如果沒有,我想我可以在某處讀取系統設置並切換到我的代碼或其他東西中的備用主題......

沒有直接的 API/事件可以從 wpf 檢測暗模式或高對比度模式。 這在 uwp 中可用。 但是有一種方法可以通過 WMI 查詢來檢測主題更改事件,以查看相關注冊表項的注冊表更改。 您將在此處找到詳細信息。 我有一個簡化的類,您可以通過它檢測注冊表更改。

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;
        }
    }
}

為邏輯實現創建真實的枚舉:

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

將相關資源文件添加到項目中。

在此處輸入圖片說明

定義將在發生注冊表更改時通過事件處理程序傳遞的回調參數。

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

現在開始觀看從 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)
        {
        
        }
    }

}

注意:由於觸發了主題更改事件,我們已經將來自 ThemeWatcher 類的相關樣式資源與方法MergeThemeDictionaries()合並。 您也可以根據需要從這里更新它。

調用以下方法以在由於運行時更改資源后更新 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM