[英]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.