Detecting Dark and Light themes in a WPF application

 
 
  • Gérald Barré

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:

C#
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:

C#
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.

csproj (MSBuild project file)
<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:

C#
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:

C#
[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:

C#
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!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub