How to get current used color theme of Visual Studio
Asked Answered
Y

3

10

I'm creating my own IntelliSense Presenter, since Visual Studio2012 support change theme, so I want my background color of the presenter can be auto-changed when the theme been changed. Is there a way to track the theme changes event, or get the current color theme of the Visual Studio?

Yuan answered 10/4, 2013 at 8:22 Comment(0)
D
18

Yes, this is possible. I had to solve a similiar issue with one of my extensions... The current theme is stored in the Windows Registry; so I implemented the following utility class.

public enum VsTheme
{
    Unknown = 0, 
    Light, 
    Dark, 
    Blue
}

public class ThemeUtil
{
    private static readonly IDictionary<string, VsTheme> Themes = new Dictionary<string, VsTheme>()
    {
        { "de3dbbcd-f642-433c-8353-8f1df4370aba", VsTheme.Light }, 
        { "1ded0138-47ce-435e-84ef-9ec1f439b749", VsTheme.Dark }, 
        { "a4d6a176-b948-4b29-8c66-53c97a1ed7d0", VsTheme.Blue }
    };

    public static VsTheme GetCurrentTheme()
    {
        string themeId = GetThemeId();
        if (string.IsNullOrWhiteSpace(themeId) == false)
        {
            VsTheme theme;
            if (Themes.TryGetValue(themeId, out theme))
            {
                return theme;
            }
        }

        return VsTheme.Unknown;
    }

    public static string GetThemeId()
    {
        const string CategoryName = "General";
        const string ThemePropertyName = "CurrentTheme";
        string keyName = string.Format(@"Software\Microsoft\VisualStudio\11.0\{0}", CategoryName);

        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName))
        {
            if (key != null)
            {
                return (string)key.GetValue(ThemePropertyName, string.Empty);
            }
        }

        return null;
    }
}

Okay; this just helps to figur out the current settings... listening for the theme changed notification is a bit trickier. After your package is loaded, you must obtain an IVsShell instance via the DTE; once you have this object you can utilize the AdviceBroadcastMessages method to subscribe for event notifications. You have to provide an object whose type implements the IVsBroadcastMessageEvents interface...

I don´t want to post the whole implementation, but the following lines might illustrate the key scenario...

class VsBroadcastMessageEvents : IVsBroadcastMessageEvent
{
    int IVsBroadcastMessageEvent.OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
    {
        const uint WM_SYSCOLORCHANGE = 0x15;
        if (msg == WM_SYSCOLORCHANGE) 
        {
            // obtain current theme from the Registry and update any UI...
        }
    }
}

Consider implementing IDisposable on that type as well, in order to be able to unsubscribe from the event source, when the package gets unloaded.

This is how I subscribe for event notifications...

class ShellService
{
    private readonly IVsShell shell;
    private bool advised;

    public ShellService(IVsShell shellInstance)
    {
        this.shell = shellInstance;
    }

    public void AdviseBroadcastMessages(IVsBroadcastMessageEvents broadcastMessageEvents, out uint cookie)
    {
        cookie = 0;
        try
        {
            int r = this.shell.AdviseBroadcastMessages(broadcastMessageEvents, out cookie);
            this.advised = (r == VSConstants.S_OK);
        }
        catch (COMException) { }
        catch (InvalidComObjectException) { }
    }

    public void UnadviseBroadcastMessages(uint cookie)
    {
        ...
    }
}

Keep the value of the cookie parameter; you´ll need it to successfully unsubscribe.

Hope that helps (-:

Donniedonnish answered 13/6, 2013 at 17:11 Comment(1)
This worked great, I just made one enhancement. In the GetThemeId method, the keyName points to a specific version (11.0). You can substitute that out as well to make it work with multiple versions. I used the DTE.Version property to get the current version, it returns a string and is properly formatted to fit right inObjectify
E
17

Just wanted to put an update just in case anyone else comes along.. @Matze and @Frank are totally right.. However in VS 2015.. they added a easy way to detect the theme change. So you need to include PlatformUI an dyou get a super easy event

using Microsoft.VisualStudio.PlatformUI;
....
 //Then you get an event 
 VSColorTheme.ThemeChanged += VSColorTheme_ThemeChanged;

You should make sure your control is disposable so you can unsubscribe from the event...

BONUS!

It also give you easy access to the colors.. even if the user has changed them from the default .. so you can do stuff like this in when set your colors

var defaultBackground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey);
var defaultForeground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowTextColorKey);
Eth answered 8/12, 2015 at 17:15 Comment(1)
these lines give me the same output for dark and light themes. var defaultBackground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey); var defaultForeground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowTextColorKey);Zildjian
O
9

For VS 2015 this has changed, the solution @Matze has still works but you need to update the GetThemeId() function to check for the version and if it's 14.0 (VS2015) look in a different place in the registry. The way the value is stored has changed also, it's still a string but now contains other values seperated by a '*'. The theme guid is the last value in the list.

if (version == "14.0")
{
   string keyName = string.Format(@"Software\Microsoft\VisualStudio\{0}\ApplicationPrivateSettings\Microsoft\VisualStudio", version);

   using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName))
   {
      if (key != null)
      {
          var keyText = (string)key.GetValue("ColorTheme", string.Empty);

              if (!string.IsNullOrEmpty(keyText))
              {
                  var keyTextValues = keyText.Split('*');
                  if (keyTextValues.Length > 2)
                  {
                       return keyTextValues[2];
                  }
              }
      }
   }

   return null;
}
Objectify answered 6/10, 2015 at 14:27 Comment(1)
EnvDTE.DTE dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); string version = dte.Version;Bikales

© 2022 - 2024 — McMap. All rights reserved.