Search code examples
c#excelvbavisual-studioexcel-addins

C# Visual Studio Excel Add-in: How can I detect Excel Office Theme Change?


I wrote a class that detects what is the current Excel theme.

Get Excel current office theme:

//Declaration
string officeVersion;
int themeCode;

// Get Office Version first
officeVersion = "16.0";

// Goto the Registry Current Version
RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + officeVersion + @"\Common");

// Get Stored Theme
themeCode = (int)rk.GetValue("UI Theme", GlobalVar.COLORFUL);

Then based on the value of themeCode, I can determine what the current Excel theme is:

// Theme Constants
public const int COLORFUL = 0;
public const int DARKGREY = 3;
public const int BLACK = 4;
public const int WHITE = 5;

My question:

  • How can I detect when the user, during Excel Running, change the Office Theme from the Excel Options?
  • In Another way, is there any Excel Event triggered when the User has edited anything from the Excel Options?
  • How can I detect/trap that event please?

enter image description here

I used already Process Monitor and got the location of the Registry key where the theme is stored. But I cannot constantly check the Registry, I prefer to detect when the user clicked on More Commmand\Excel Options if that event is detectable.

Your answer and suggestions are most welcome. Thanks in advance!


Solution

  • Great thanks to @PortlandRunner for the approach he gave me in the comments. I came up with the following code:

    using Microsoft.Win32;
    using System;
    using System.Drawing;
    using System.Management;
    using System.Security.Principal;
    
    namespace YourProject
    {
        /*
         #####################################
         # GLOBAL CONSTANTS FOR OFFICE THEME #
         # By Tsiriniaina Rakotonirina       #
         #####################################
         */
        public class GlobalVar
        {
            //Theme Constants
            public const int COLORFUL = 0;
            public const int DARKGREY = 3;
            public const int BLACK = 4;
            public const int WHITE = 5;
        }
    
        /*
         ########################################
         # OFFICE CLASS TO RETURN TO THE ADDINS #
         # By Tsiriniaina Rakotonirina          #
         ########################################
         */
        public class ExcelTheme
        {
            private int code;             //Theme Code               
            private Color backgroundColor;//Addins Backcolor based on Theme
            private Color textForeColor;  //Addins Text Color based on Theme
    
            public Color BackgroundColor { get => backgroundColor; set => backgroundColor = value; }
            public Color TextForeColor { get => textForeColor; set => textForeColor = value; }
            public int Code { get => code; set => code = value; }
        }
    
        /*
         ###############################
         # OFFICE THEME CHANGE WATCHER #
         # By Tsiriniaina Rakotonirina #
         ###############################
         */
        class ExcelThemeWatcher
        {
            /*
             *****************************************
             * CLASS CONSTRUCTOR                     *
             * ---> The Watch start right away after *
             *      the class is created             *
             *****************************************
             */
            public ExcelThemeWatcher()
            {
                //Start Watching Office Theme Change
                //By calling the following method
                StartThemeWatcher();
            }
    
            /*
             *****************************************
             * GET OFFICE VERSION                    *
             * ---> Read the Registry and            *
             *      get the Current Office Version   *
             *****************************************
             */
            public int GetOfficeVersion()
            {
                //Get Current Excel Version
                try
                {
                    //Get Office Version
                    //Goto the Registry Current Version
                    RegistryKey rk = Registry.ClassesRoot.OpenSubKey(@"Excel.Application\\CurVer");
    
                    //Read Current Version
                    string officeVersion = rk.GetValue("").ToString();
    
                    //Office Version
                    string officeNumberVersion = officeVersion.Split('.')[officeVersion.Split('.').GetUpperBound(0)];
    
                    //Return Office Version
                    return Int32.Parse(officeNumberVersion);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return 0;
                }
            }
    
            /*
             *****************************************
             * GET OFFICE THEME                      *
             * ---> Read the Registry and            *
             *      get the Current Office Theme     *
             *****************************************
             */
            private int GetRegistryOfficeTheme()
            {
                //Get Office Version first
                string officeVersion = GetOfficeVersion().ToString("F1");
    
                //Goto the Registry Current Version
                RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + officeVersion + @"\Common");
    
                return Convert.ToInt32(rk.GetValue("UI Theme", GlobalVar.COLORFUL));
            }
    
            /*
             *****************************************
             * GET ADDINS THEME                      *
             * ---> Based on the Office Theme        *
             *      Return the Addins Theme          *
             *****************************************
             */
            public ExcelTheme GetAddinsTheme()
            {
                ExcelTheme theme = new ExcelTheme();
    
                //Default Theme Code
                theme.Code = GetRegistryOfficeTheme();
    
                //Get Background Colors
                theme.BackgroundColor = ColorTranslator.FromHtml("#EFE9D7");
                theme.TextForeColor = ColorTranslator.FromHtml("#004B8D");
    
                try
                {
                    switch (theme.Code)
                    {
                        case GlobalVar.COLORFUL:
                            theme.BackgroundColor = ColorTranslator.FromHtml("#E6E6E6");
                            theme.TextForeColor = ColorTranslator.FromHtml("#004B8D");
    
                            break;
    
                        case GlobalVar.DARKGREY:
                            theme.BackgroundColor = ColorTranslator.FromHtml("#666666");
                            theme.TextForeColor = ColorTranslator.FromHtml("White");
                            break;
    
                        case GlobalVar.BLACK:
                            theme.BackgroundColor = ColorTranslator.FromHtml("#323130");
                            theme.TextForeColor = ColorTranslator.FromHtml("#CCA03B");
                            break;
    
                        case GlobalVar.WHITE:
                            theme.BackgroundColor = ColorTranslator.FromHtml("#FFFFFF");
                            theme.TextForeColor = ColorTranslator.FromHtml("#004B8D");
    
                            break;
    
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
    
                return theme;
            }
    
            /*
             ******************************************
             * START OFFICE THEME CHANGE WATCH        *
             * ---> Using WMI, read and watch         *
             *      Registry Section for Office Theme *
             ******************************************
             */
            private void StartThemeWatcher()
            {
                string keyPath;   //Office Theme Path
                string valueName; //Office Theme Value name
    
                //Get Office Version first
                string officeVersion = GetOfficeVersion().ToString("F1");
    
                //Set the KeyPath based on the Office Version
                keyPath = @"Software\\Microsoft\\Office\\" + officeVersion + "\\Common";
                valueName = "UI Theme";
    
                //Get the Current User ID
                //---> HKEY_CURRENT_USER doesn't contain Value as it is a shortcut of HKEY_USERS + User ID
                //     That is why we get that currentUser ID and use it to read the wanted location
    
                //Get the User ID
                var currentUser = WindowsIdentity.GetCurrent();
    
                //Build the Query based on 3 parameters
                //Param #1: User ID
                //Param #2: Location or Path of the Registry Key
                //Param #3: Registry Value to watch
                var query = new WqlEventQuery(string.Format(
                        "SELECT * FROM RegistryValueChangeEvent WHERE Hive='HKEY_USERS' AND KeyPath='{0}\\\\{1}' AND ValueName='{2}'",
                        currentUser.User.Value, keyPath.Replace("\\", "\\\\"), valueName));
    
                //Create a Watcher based on the "query" we just built
                ManagementEventWatcher watcher = new ManagementEventWatcher(query);
    
                //Create the Event using the "Function" to fire up, here called "KeyValueChanged"
                watcher.EventArrived += (sender, args) => KeyValueChanged();
    
                //Start the Watcher
                watcher.Start();
    
            }
    
            /*
             ******************************************
             * EVENT FIRED UP WHEN CHANGE OCCURS      *
             * ---> Here the event is instructed      *
             *      to update the Addins Theme        *
             ******************************************
             */
            private void KeyValueChanged()
            {
                // Here, whenever the user change the Office theme,
                // this function will automatically Update the Addins Theme
                Globals.ThisAddIn.SetAddinsInterfaceTheme();
            }
        }
    
    }
    

    I didn't feel the need to stop the watcher, but if you came up with the idea, tell me where to put it ;)

    UPDATE:

    It's good to tell as well that I was so existed when I tested to change the Office Theme and saw my Addins theme change as well. Would love to hear from you as well!