Skip to content

Instantly share code, notes, and snippets.

@aelij
Created July 4, 2016 10:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aelij/4ca55602f090714295f42da457f41d5c to your computer and use it in GitHub Desktop.
Save aelij/4ca55602f090714295f42da457f41d5c to your computer and use it in GitHub Desktop.
WPF Theme Manager
using System;
using System.Reflection;
using System.Windows;
public static class ThemeManager
{
#region Fields
private const BindingFlags DefaultStaticFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
private const BindingFlags DefaultInstanceFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
private static readonly Assembly PresentationFramework = typeof(FrameworkElement).Assembly;
private static readonly Type UxThemeWrapper = PresentationFramework.GetType("MS.Win32.UxThemeWrapper");
private static readonly Type SystemResources = PresentationFramework.GetType("System.Windows.SystemResources");
private static readonly MethodInfo SystemResources_InvalidateResources = SystemResources.GetMethod("InvalidateResources", DefaultStaticFlags);
private static readonly MethodInfo SystemResources_OnThemeChanged = SystemResources.GetMethod("OnThemeChanged", DefaultStaticFlags);
private static readonly MethodInfo SystemParameters_InvalidateCache = typeof(SystemParameters).GetMethod("InvalidateCache", DefaultStaticFlags, null, Type.EmptyTypes, null);
private static readonly MethodInfo SystemColors_InvalidateCache = typeof(SystemColors).GetMethod("InvalidateCache", DefaultStaticFlags);
private static readonly FieldInfo UxThemeWrapper_isActive = UxThemeWrapper.GetField("_isActive", DefaultStaticFlags);
private static readonly FieldInfo UxThemeWrapper_themeColor = UxThemeWrapper.GetField("_themeColor", DefaultStaticFlags);
private static readonly FieldInfo UxThemeWrapper_themeName = UxThemeWrapper.GetField("_themeName", DefaultStaticFlags);
#endregion
#region Intercept Theme Change
static ThemeManager()
{
SystemResources.GetMethod("EnsureResourceChangeListener", DefaultStaticFlags).Invoke(null, null);
var hook = Delegate.CreateDelegate(typeof(DependencyObject).Assembly.GetType("MS.Win32.HwndWrapperHook"),
typeof(ThemeManager).GetMethod(nameof(FilterThemeMessage), DefaultStaticFlags));
var notify = SystemResources.GetField("_hwndNotify", DefaultStaticFlags).GetValue(null);
notify = notify.GetType().GetProperty("Value", DefaultInstanceFlags).GetValue(notify, null);
notify.GetType().GetMethod("AddHook", DefaultInstanceFlags).Invoke(notify, new object[] { hook });
}
private static IntPtr FilterThemeMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x31a) // WM_THEMECHANGED
{
handled = true;
}
return IntPtr.Zero;
}
#endregion
#region Change Theme
/// <summary>
/// Changes the theme using a compound theme name (theme-name[.theme-color]).
/// </summary>
/// <param name="compoundThemeName">Compound theme name.</param>
public static void ChangeTheme(string compoundThemeName)
{
if (string.IsNullOrEmpty(compoundThemeName))
{
ChangeTheme(string.Empty, string.Empty);
}
else
{
var themeData = compoundThemeName.Split('.');
ChangeTheme(themeData[0], themeData.Length == 1 ? string.Empty : themeData[1]);
}
}
/// <summary>
/// Changes the theme.
/// </summary>
/// <param name="themeName">Name of the theme.</param>
/// <param name="themeColor">Color of the theme.</param>
public static void ChangeTheme(string themeName, string themeColor)
{
SystemColors_InvalidateCache.Invoke(null, null);
SystemParameters_InvalidateCache.Invoke(null, null);
SystemResources_OnThemeChanged.Invoke(null, null);
if (!string.IsNullOrEmpty(themeName))
{
UxThemeWrapper_isActive.SetValue(null, true);
UxThemeWrapper_themeName.SetValue(null, themeName);
UxThemeWrapper_themeColor.SetValue(null, themeColor);
}
SystemResources_InvalidateResources.Invoke(null, new object[] { false });
}
#endregion
}
@aelij
Copy link
Author

aelij commented Jul 4, 2016

This overrides the default OS theme. Although it is quite a hack (uses private Reflection) it has several advantages. See this post.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment