5

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!

Pᴇʜ
  • 45,553
  • 9
  • 41
  • 62
  • 2
    Assuming for a moment that we can't detect theme change event... I'll take a guess that you want to change appearance of an add-in you've created. Is it possible to add another option in your add-in to change it's "theme" manually by user selection instead of automatically detecting it? – Automate This Apr 29 '20 at 04:00
  • 3
    You might find this [theme watcher](https://princetonits.com/blog/technology/detecting-office-theme-change-event-vsto-addin-registry-value-change-event-c/) approach interesting. – Automate This Apr 29 '20 at 04:34
  • @PortlandRunner, your suggestion is great! That didn't pass through my mind. In fact, I can adapt my Addin's theme with the Office theme after a restart, that's why I wanted to know if an event exists. But this could be a great Solution as well. Thumbs up for this! – Tsiriniaina Rakotonirina Apr 29 '20 at 13:22
  • @PortlandRunner, I'm testing this theme_watcher as well and will report to you once I get it to work. In fact, I have updated it to detect the current Office version as the example works only with Office 15.0 which is hard coded. I'll try to make it dynamic, then will surely share here if it works. – Tsiriniaina Rakotonirina Apr 29 '20 at 13:24
  • 1
    Yes, please do share when available. The more I thought about this I wondered why Microsoft didn't make this built in for add-ins developers. Perhaps it's just an oversight but I'm sure your not the only one wanting this same functionality! – Automate This Apr 29 '20 at 13:46

1 Answers1

1

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!

halfer
  • 18,701
  • 13
  • 79
  • 158