Skip to content

Instantly share code, notes, and snippets.

@dotMorten
Last active March 27, 2025 22:45
Show Gist options
  • Save dotMorten/f462b1174cf51973e6f55ea0eb12e6d3 to your computer and use it in GitHub Desktop.
Save dotMorten/f462b1174cf51973e6f55ea0eb12e6d3 to your computer and use it in GitHub Desktop.
Doing dark/light mode resources in WPF
<Application x:Class="MyWPFStuff.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyWPFStuff"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:ThemeResourceDictionary >
<local:ThemeResourceDictionary.DarkModeResources>
<SolidColorBrush x:Key="Background" Color="Black"/>
</local:ThemeResourceDictionary.DarkModeResources>
<local:ThemeResourceDictionary.LightModeResources>
<SolidColorBrush x:Key="Background" Color="White"/>
</local:ThemeResourceDictionary.LightModeResources>
<SolidColorBrush x:Key="AlwaysWhite" Color="White"/>
</local:ThemeResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
using System;
using System.Windows;
using System.Windows.Threading;
namespace MyWPFStuff
{
public class ThemeResourceDictionary : ResourceDictionary
{
private readonly WeakEventListener<ThemeResourceDictionary, object, object, Microsoft.Win32.UserPreferenceChangingEventArgs> userPreferenceHandler;
private readonly Dispatcher _CurrentDispatcher;
/// <summary>
/// Initializes a new instance of the <see cref="ThemeResourceDictionary"/> class.
/// </summary>
public ThemeResourceDictionary()
{
userPreferenceHandler = new WeakEventListener<ThemeResourceDictionary, object, object, Microsoft.Win32.UserPreferenceChangingEventArgs>(this, null);
userPreferenceHandler.OnEventAction = (instance, sender, e) => SystemEvents_UserPreferenceChanging(e);
userPreferenceHandler.OnDetachAction = (instance, source, weakListener) => Microsoft.Win32.SystemEvents.UserPreferenceChanging -= userPreferenceHandler.OnEvent;
Microsoft.Win32.SystemEvents.UserPreferenceChanging += userPreferenceHandler.OnEvent;
_CurrentDispatcher = Dispatcher.CurrentDispatcher;
UpdateResources();
}
private void SystemEvents_UserPreferenceChanging(Microsoft.Win32.UserPreferenceChangingEventArgs e)
{
if (e.Category == Microsoft.Win32.UserPreferenceCategory.General && m_Theme == AppTheme.Default &&
_CurrentDispatcher != null && !_CurrentDispatcher.HasShutdownStarted && !_CurrentDispatcher.HasShutdownFinished)
{
_CurrentDispatcher.BeginInvoke(new Action(() => UpdateResources()));
}
}
private AppTheme m_Theme;
/// <summary>
/// Gets or sets the theme for the resources.
/// </summary>
public AppTheme Theme
{
get { return m_Theme; }
set
{
if (m_Theme != value)
{
m_Theme = value;
UpdateResources();
}
}
}
private void UpdateResources()
{
bool isDark = m_Theme == AppTheme.Dark;
if(m_Theme == AppTheme.Default)
{
try
{
isDark = (int?)Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", "AppsUseLightTheme", -1) != 1;
}
catch { }
}
if (isDark)
{
if(this.MergedDictionaries.Contains(LightModeResources))
this.MergedDictionaries.Remove(LightModeResources);
this.MergedDictionaries.Add(DarkModeResources);
}
else
{
if (this.MergedDictionaries.Contains(DarkModeResources))
this.MergedDictionaries.Remove(DarkModeResources);
this.MergedDictionaries.Add(LightModeResources);
}
}
public ResourceDictionary LightModeResources { get; } = new ResourceDictionary();
public ResourceDictionary DarkModeResources { get; } = new ResourceDictionary();
}
/// <summary>
/// Theme for the application
/// </summary>
/// <seealso cref="CalciteResources.Theme"/>
public enum AppTheme
{
/// <summary>
/// Defaults to the system theme
/// </summary>
Default,
/// <summary>
/// Light theme
/// </summary>
Light,
/// <summary>
/// Dark theme
/// </summary>
Dark
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment