Created
July 4, 2016 10:14
-
-
Save aelij/4ca55602f090714295f42da457f41d5c to your computer and use it in GitHub Desktop.
WPF Theme Manager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This overrides the default OS theme. Although it is quite a hack (uses private Reflection) it has several advantages. See this post.