Dark/Light theme: Sync your Windows 10 default app mode with SOLIDWORKS 3D

SOLIDWORKS 3D offers users a dark and a light theme.

There is a setting in Windows 10 that automatically turn on or off theme selection in third-party applications. SOLIDWORKS does not use that feature so it’s about time we wrote an article to show how a custom add-in can automatically pick up that Windows setting. First, to turn on or off app theme mode, you need to :

  • Type theme in the Windows search bar and pick the first result.
  • Click on Colors on the sidebar and you will get this window.

Clicking light or dark under “Choose your default app mode” will switch the theme in your applications. A good example is Google Chrome.

The source code of the add-in is available on our GitHub. It’s made of two classes:
– swAddIn.cs: This is the add-in class.
– WindowsThemeManager.cs : This class listens for registry changes when the default app mode values.

swAddIn.cs

using Microsoft.Win32;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using SolidWorks.Interop.swpublished;
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsThemeSync
{
[Guid("36EB446E-E8D2-4F2A-AE07-B45D8DD92694"), ComVisible(true)]
public class WindowsThemeSyncAddIn : ISwAddin
{
#region fields
public SldWorks SOLIDWORKS { get; private set; }
public int SessionCookie { get; private set; }
public static object AddInName { get; private set; } = "Windows 10 App Mode Sync - Blue Byte Systems Inc.";
public static object AddInDescription { get; private set; } = "Syncs your SOLIDWORKS theme with Windows 10 default app mode.";
#endregion
/// <summary>
/// This method is called when the add-in is activated in SOLIDWORKS.
/// </summary>
/// <param name="ThisSW">The this sw.</param>
/// <param name="Cookie">The cookie.</param>
/// <returns></returns>
public bool ConnectToSW(object ThisSW, int Cookie)
{
try
{
SOLIDWORKS = ThisSW as SldWorks;
SessionCookie = Cookie;
SOLIDWORKS.SetAddinCallbackInfo(0, this, SessionCookie);
// sync the existing theme from the Windows settings
RefreshTheme();
// start watching for theme registry changes
WindowsThemeManager.WindowsThemeChanged += WindowsThemeManager_WindowsThemeChanged;
WindowsThemeManager.StartWatchingForThemeChanges();
return true;
}
catch (Exception e)
{
// in case something goes wrong
// this might fail an older Windows OS
MessageBox.Show($"Message = {e.Message} Stacktrace: {e.StackTrace}");
return false;
}
}
/// <summary>
/// Fired when the Windows default app mode value in the registry changes
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private void WindowsThemeManager_WindowsThemeChanged(object sender, Theme_e e)
{
var currentTheme = (swInterfaceBrightnessTheme_e)SOLIDWORKS.GetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground);
switch (e)
{
case Theme_e.Light:
if (currentTheme != swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Light)
SOLIDWORKS.SetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground, (int)swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Light);
break;
case Theme_e.Dark:
if (currentTheme != swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Dark)
SOLIDWORKS.SetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground, (int)swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Dark);
break;
default:
break;
}
}
/// <summary>
/// Refreshes the theme.
/// </summary>
public void RefreshTheme()
{
var theme = WindowsThemeManager.GetWindowsTheme();
var currentTheme = (swInterfaceBrightnessTheme_e)SOLIDWORKS.GetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground);
switch (theme)
{
case Theme_e.Light:
if (currentTheme != swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Light)
SOLIDWORKS.SetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground, (int)swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Light);
break;
case Theme_e.Dark:
if (currentTheme != swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Dark)
SOLIDWORKS.SetUserPreferenceIntegerValue((int)swUserPreferenceIntegerValue_e.swSystemColorsBackground, (int)swInterfaceBrightnessTheme_e.swInterfaceBrightnessTheme_Dark);
break;
default:
break;
}
}
/// <summary>
/// Gets called when you deativate the add-in in SOLIDWORKS
/// </summary>
/// <returns></returns>
public bool DisconnectFromSW()
{
try
{
// stop watch for registry changes
WindowsThemeManager.StopWatchingForThemeChanges();
return true;
}
catch (Exception e)
{
//todo: log ex
return false;
}
}
#region com registration
[ComRegisterFunction]
private static void RegisterAssembly(Type t)
{
try
{
string KeyPath = string.Format(@"SOFTWARE\SolidWorks\AddIns\{0:b}", t.GUID);
RegistryKey rk = Registry.LocalMachine.CreateSubKey(KeyPath);
rk.SetValue("Title", AddInName); // Title
rk.SetValue("Description", AddInDescription); // Description
rk.SetValue(null, 1); // startup parameter (loads addin automatically upon SOLIDWORKS startup)
}
catch (Exception e)
{
//todo: msgbox ex
throw e;
}
}
[ComUnregisterFunction]
private static void UnregisterAssembly(Type t)
{
try
{
bool Exist = false;
string KeyPath = string.Format(@"SOFTWARE\SolidWorks\AddIns\{0:b}", t.GUID);
using (RegistryKey Key = Registry.LocalMachine.OpenSubKey(KeyPath))
{
if (Key != null)
Exist = true;
else
Exist = false;
}
if (Exist)
Registry.LocalMachine.DeleteSubKeyTree(KeyPath);
}
catch (Exception e)
{
//todo: msgbox ex
throw e;
}
}
#endregion
}
}
view raw swAddIn.cs delivered with ❤ by emgithub

WindowsThemeManager.cs:

using Microsoft.Win32;
using System;
using System.Management;
using System.Security.Principal;
namespace WindowsThemeSync
{
public enum Theme_e
{
Undefined,
Light,
Dark
}
public class WindowsThemeManager
{
#region Public Events
public static event EventHandler<Theme_e> WindowsThemeChanged;
#endregion
#region Private Fields
private static ManagementEventWatcher watcher;
#endregion
#region Private Properties
#endregion
#region Public Properties
public static bool IsWatching { get; private set; } = false;
#endregion
#region Private Constructors
private WindowsThemeManager()
{
}
#endregion
#region Private Methods
private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string RegistryValueName = "AppsUseLightTheme";
public static Theme_e GetWindowsTheme()
{
try
{
var key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath);
var registryValueObject = key?.GetValue(RegistryValueName);
if (registryValueObject == null)
{
return Theme_e.Light;
}
var registryValue = (int)registryValueObject;
key.Close();
return registryValue > 0 ? Theme_e.Light : Theme_e.Dark;
}
catch (Exception)
{
return Theme_e.Undefined;
}
}
private static void Watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
var ret = GetWindowsTheme();
WindowsThemeChanged?.Invoke(null, ret);
}
catch (System.Exception)
{
// needs to add logger here
}
}
#endregion
#region Public Methods
public static void StartWatchingForThemeChanges()
{
try
{
if (IsWatching == true)
throw new Exception("Object already watching registry entry");
var currentUser = WindowsIdentity.GetCurrent();
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM RegistryValueChangeEvent WHERE " +
"Hive = 'HKEY_USERS' " +
$@"AND KeyPath='{currentUser.User.Value}\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize' AND ValueName='{RegistryValueName}'");
watcher = new ManagementEventWatcher(query);
watcher.EventArrived -= Watcher_EventArrived;
watcher.EventArrived += Watcher_EventArrived;
watcher.Start();
IsWatching = true;
}
catch (Exception)
{
}
}
public static void StopWatchingForThemeChanges()
{
if (watcher != null)
{
watcher.Stop();
watcher.Dispose();
watcher = null;
}
IsWatching = false;
}
#endregion
}
}