Detecting Dark and Light themes in a WPF application
If an application support both light and dark themes, it is important to detect the current system theme and update the application accordingly. In this post, I describe how to detect if a WPF application should use a light or dark theme.
#Method 1: Using the registry and the Windows message loop
Windows stores the current theme in the registry. The following code reads the current theme from the registry:
private static bool IsLightTheme()
{
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
var value = key?.GetValue("AppsUseLightTheme");
return value is int i && i > 0;
}
Then, you can use the following code to detect when the theme changes:
public partial class MainWindow : Window
{
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Detect when the theme changed
HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
source.AddHook((IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
{
const int WM_SETTINGCHANGE = 0x001A;
if (msg == WM_SETTINGCHANGE)
{
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
{
var isLightTheme = IsLightTheme();
// TODO Change app theme accordingly
}
}
return IntPtr.Zero;
});
}
}
#Method 2: Using the UISettings class
First, you need to update the TargetFramework
to specify the minimum Windows version required. This way, the Windows API will be accessible from your code.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
</PropertyGroup>
</Project>
Then, you can use UISettings
to get the current theme:
var settings = new UISettings();
var isLightTheme = IsColorLight(settings.GetColorValue(UIColorType.Background));
settings.ColorValuesChanged += (sender, args) =>
{
var isLightTheme = IsColorLight(settings.GetColorValue(UIColorType.Background));
// TODO Change app theme accordingly
};
// From https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes?WT.mc_id=DT-MVP-5003978#know-when-dark-mode-is-enabled
static bool IsColorLight(Color clr)
=> ((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128);
#Updating the title bar to a dark theme
The title bar is part of the application window. Therefore, you need to update the window style to use the dark theme if needed. The following code updates the window style to use the dark theme:
[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_BEFORE_20H1 = 19;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
{
if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17763))
{
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18985))
{
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
}
int useImmersiveDarkMode = enabled ? 1 : 0;
return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
}
return false;
}
You can use the previous code in OnSourceInitialized
to update the window style:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var isLightTheme = IsLightTheme();
HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
UseImmersiveDarkMode(source.Handle, !isLightTheme);
}
Title bar in light theme
Title bar in dark theme
Do you have a question or a suggestion about this post? Contact me!