Tag: vba tools

  • Reading the Office UI Theme

    Reading the Office UI Theme

    In 2016, Microsoft officially introduced the ability to toggle between light and dark themes in Windows 10. With Office 2016, there was also the ability to switch to a black theme for Office applications, but it wasn’t until Windows 11 that the Office UI Theme had the option of “Use system setting” so that users who had set Windows to use a dark theme, would have a dark/black theme in their Office apps by default. Most recently Excel has also introduced an additional “Dark Mode” toggle button on the View ribbon, which sets the main worksheet to use a black background with white text.

    Many users like to use a dark theme / dark mode as it reduces the amount of bright white light from their screen, and it can give a more modern feel to the UI. However, there are also an equal number, if not more, that like the traditional look and feel of the colourful / light mode for Office applications. As a developer, it is nice to be able to accommodate both camps and preferably be able to dynamically tell which theme/mode the user is using and match your own project UI to that user’s preference.

    Sadly, when it comes Excel project development, and especially VBA-based development, this can be very difficult to accommodate. Microsoft has not updated the style of the UserForm elements in VBA in a long time, and so any VBA applications tend to look like a throwback to Windows 97. Some more customisation is possible when developing VBA tools for Office through Visual Studio, but this is used for compiled office add-ins rather than included VBA code within a single Excel project file.

    Part of the problem is that Microsoft is pushing towards Office Scripts tools and automation where a HTML-based UI is used, but at present there are still plenty of disadvantages for many Excel projects in using Office Scripts over VBA.

    There is, and it can look great! But that is a subject for another day (future post incoming).

    Instead, in this post I plan to go over how it is possible to read which Office theme a user is currently using and then update the styling of a standard VBA UserForm as it is loaded so that you can match your UI to the user’s main Excel application.

    Reading the UI Theme

    Both the chosen Windows theme and the Office UI theme are stored as persistent values in the registry. Values in the registry are stored as key-value pairs similar to entries in a Dictionary object.

    In order to read the value for a registry key you simply need to know the full address for that registry key. Similarly, setting a registry key requires the full address, however, often also requires additional permissions, such as admin permissions, and so can be an issue when sharing with others who may not have the necessary permissions for setting/creating registry keys.

    To find the required registry address, you can either employ some expert Google skills and find an article which describes it, or you can take a more DIY approach:

    • use a program like Process Monitor (Procmon.exe), which monitors all process calls made by Windows; filter the monitoring output for registry calls and for the Excel application (e.g. excel.exe), and then look to see what is updated as you toggle the UI Theme option in Excel.

    For Windows 11 (I haven’t been able to test this on Windows 10 at the moment) and Office 365:

    • the Windows theme is stored in the registry with the key AppsUseLightTheme at the following address:
      • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme
      • 0 = No/False -> Dark theme
      • 1 = Yes/True -> Light theme
    • the Office UI theme setting is stored in the registry with the key UI Theme at the following full address:
      • HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Common\UI Theme
      • 3 = Dark Grey
      • 4 = Black
      • 5 = White
      • 6 = Use system setting (i.e. Windows theme setting)
      • 7 = Colourful

    In order to match your project UI to the user chosen Office UI theme, read the Office UI theme value first; if it comes back as a value of 6 (“Use system setting”), then read the Windows theme value:

    • for Windows Light mode use the Office White theme (5)
    • for Windows Dark mode use the Office Black theme (4)

    To read a registry entry, first create a Windows Script Host object (WScript) in order to run shell methods (line 5) using CreateObject(). To read a registry value, use the .RegRead() method (line 9). It is important to set the created object back to Nothing after you have finished using it in order to release the object from memory (line 13).

    ' Define local variables
    Dim objShell As Object
    
    ' Create the Windows Scipt Host object
    Set objShell = CreateObject("WScript.Shell")
    
    On Error Resume Next
        ' Attempt to read the local user Office UI Theme Regisrty entry
        Debug.Print "OfficeUI Theme: " & objShell.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Common\UI Theme")
        ' Attempt to read the local user Windows Theme Registry entry
        Debug.Print "Windows Theme:  " & objShell.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme")
    On Error Goto 0
    
    ' Clear object as no longer needed
    Set objShell = Nothing
    

    Using the Office UI Theme

    By reading the current Office (+/- Windows theme if the Office theme comes back as “Use system setting”), you can adjust the look of your UserForm to give a more cohesive appearance when the user transitions from native Excel UI to your projects UI.

    To update the styling of a UserForm before it becomes visible to the user, you would need to check the current Office UI theme as part of the UserForm_Initialize() sub routine. This sub routine is executed when another process first calls the UserForm and completes before the UserForm_Activate() sub routine which is executed at the point at which the UserForm is to be displayed to the user and becomes available for the user to interact with.

    A relatively efficient method of doing this is, having got the current UI theme, to update a series of module-level variables which define UI element colours to match the theme. These colour variables then are used to update the different UserForm elements as required before the UserForm is displayed either also in the Initialize() sub routine or in the Activate() sub routine.

    Below is a simple example of reading the Office UI theme using an approach similar to that shown in the code above and then updating a simplied UserForm to match the Excel theme styling.

    ' Defining the global variables used to set the form element colours
    Dim lnFgPrimary As Long, lnFgSecondary As Long
    Dim lnBgPrimary As Long, lnBgSecondary As Long
    Dim lnClrAccent1 As Long, lnClrAccent2 As Long, lnClrFooter As Long
    
    Private Sub UserForm_Initialize()
        
        ' Define local variables
        Dim intTheme As Integer
        
        ' Attempt to read the current Office UI Theme value and set the appropriate colour variables
        intTheme = GetThemeInfo
        Call SetThemeColours(intTheme)
        
        ' Update the form elements as required
        Me.BackColor = lnBgPrimary
        Me.ForeColor = lnFgPrimary
        Me.Lbl_Title.ForeColor = lnClrAccent1
        Me.Img_Divider.BackColor = lnClrAccent2
        With Me.Frm_Main
            .BackColor = lnBgSecondary
            .ForeColor = lnFgSecondary
        End With
        Me.Lbl_Main.ForeColor = lnFgSecondary
        Me.Lbl_Footer.BackColor = lnClrFooter
        
    End Sub
    
    Private Function GetThemeInfo() As Integer
        
        '### Return values:
        '###    1 = Colourful (light) theme
        '###    2 = Black (Dark) theme
        '###    3 = Dark grey theme
        '###    4 = White theme
        '### Defaults to the Colourful/Light theme
        
        ' Define local variables
        Dim intTh As Integer, intWn As Integer, intReturn As Integer
        
        ' Read the Office UI Theme and Windows UI Theme using the RXL_OfficeUtilities functions
        intTh = RXL_OfficeUtilities.ReadOfficeUITheme_Value
        ' If the Office theme value is 6 ("Use system setting") then also read the Windows theme
        If intTh = 6 Then _
            intWn = RXL_OfficeUtilities.ReadWindowsUITheme_Value
        
        ' Decide the correct return value based on the Office UI theme and Windows theme values
        Select Case intTh
            Case 7
                intReturn = 1
            Case 4
                intReturn = 2
            Case 3
                intReturn = 3
            Case 5
                intReturn = 4
            Case 6
                If intWn = 0 Then
                    intReturn = 2
                Else
                    intReturn = 4
                End If
            Case Else
                intReturn = 1
        End Select
        
        ' Return function result
        GetThemeInfo = intReturn
        
    End Function
    
    Private Sub SetThemeColours(ByVal pInt As Integer)
        
        ' Set the module-level colour variables based on the theme value passed as pInt
        Select Case pInt
            Case 1
                lnFgPrimary = 0             'RGB(0,0,0)
                lnFgSecondary = 2697513     'RGB(41,41,41)
                lnBgPrimary = 16777215      'RGB(240,240,240)
                lnBgSecondary = 16777215    'RGB(255,255,255)
                lnClrAccent1 = 16777215     'RGB(255,255,255)
                lnClrAccent2 = 12106326     'RGB(86,186,184)
                lnClrFooter = 15790320      'RGB(240,240,240)
            Case 2
                lnFgPrimary = 16777215      'RGB(255,255,255)
                lnFgSecondary = 16777215    'RGB(255,255,255)
                lnBgPrimary = 0             'RGB(0,0,0)
                lnBgSecondary = 2697513     'RGB(41,41,41)
                lnClrAccent1 = 14145696     'RGB(160,216,215)
                lnClrAccent2 = 12106326     'RGB(86,186,184)
                lnClrFooter = 2697513       'RGB(41,41,41)
            Case 3
                lnFgPrimary = 0             'RGB(0,0,0)
                lnFgSecondary = 0           'RGB(0,0,0)
                lnBgPrimary = 5395026       'RGB(82,82,82)
                lnBgSecondary = 12434877    'RGB(189,189,189)
                lnClrAccent1 = 16777215     'RGB(255,255,255)
                lnClrAccent2 = 12106326     'RGB(86,186,184)
                lnClrFooter = 12434877      'RGB(189,189,189)
            Case 4
                lnFgPrimary = 0             'RGB(0,0,0)
                lnFgSecondary = 2697513     'RGB(41,41,41)
                lnBgPrimary = 16777215      'RGB(240,240,240)
                lnBgSecondary = 16777215    'RGB(255,255,255)
                lnClrAccent1 = 8620825      'RGB(25,139,131)
                lnClrAccent2 = 12106326     'RGB(86,186,184)
                lnClrFooter = 15790320      'RGB(240,240,240)
        End Select
        
    End Sub
    
    

    The UserForm_Initialize() sub routine starts by calling the GetThemeInfo() function to determine the current user Office UI theme (line 12). The GetThemeInfo() function returns a numeric value for the current theme, defaulting to 1 for the colourful/light theme as this tends to be most widely used Office UI theme. The returned theme value is then used in the SetThemeColours() sub routine call (line 13).

    The SetThemeColours() sub routine uses a Select statement to set the appropriate colour values for the module-level variables (lines 75-108) based on the passed theme value (pInt). When setting a colour, most UserForm elements/controls require a Long data type value (a whole number) rather than a colour code such as RGB. As such it is easier to store the chosen colour values in the code as the calculated Long value with the RGB colour code shown next to the Long value as a comment for reference.

    Having determined the current theme choice and set the colour variables, the UserForm_Initialize() sub routine is then used to update the required UserForm elements/controls using the updated module-level colour variables (lines 16-25) before the UserForm_ Activate() sub routine is executed and the UserForm is displayed to the user.

    Below are screen shots of the simple UserForm in the above VBA code across the four different Office UI themes.

    If you wish to have a play around with this idea more yourself and investigate the code, I have uploaded an demo file. The file contains the full RXL_OfficeUtilities VBA module and a simple example UserForm which updates to the Office UI theme as shown in the screen shots above. The file (Read Office UI Theme.xlsm) can be found in the RXL_VBATools GitHub repository at the following address:

    https://github.com/r-silk/RXL_VBATools/tree/main/OfficeUITheme

    The RXL_OfficeUtilities VBA module contains different functions for reading the UI theme, as well as functions to read the full Excel version and build number.

    • ReadOfficeUITheme_Value()
      • Reads the Office UI theme and returns a numeric value to indicate which theme is currently in use
    • ReadWindowsUITheme_Value()
      • Reads the Windows UI theme and returns a numeric value to indicate which theme is currently in use
    • ReadOfficeUITheme_String( )
      • Reads the Office UI theme and Windows UI theme and returns a string with the name of the theme currently in use
    • ReadExcelBuildNumber_String( )
      • Reads the full Excel version and build number for the current Excel instance and returns a string in the format: [major].[minor].[revision].[build]
    • ReadExcelBuildNumber_Array( )
      • Reads the full Excel version and build number for the current Excel instance, splits it into parts, and returns an array in the format:
        • (1) = major version number
        • (2) = minor version number
        • (3) = revision number
        • (4) = build number

    If you would like to have a closer look at the RXL_OfficeUtilities code and have a play around with a demo, there is an Excel file (Read Office UI Theme.xlsm) which can be downloaded from the RXL_VBATools GitHub repository within the OfficeUITheme sub-folder:

    https://github.com/r-silk/RXL_VBATools

    To include the RXL_OfficeUtilities module in your own Excel project, you simply need to import the module into your VBA project and then make the requried function call as discussed above.

    The module can be imported by using one of the two methods below:

    1. Copy from demo file
      • If you have downloaded the Read Office UI Theme.xlsm file (https://github.com/r-silk/RXL_VBATools), you can copy the module and import it directly.
      • Open the Read Office UI Theme.xlsm file, as well as the Excel file you wish to import it into, and open the VB Editor.
      • Find the RXL_OfficeUtilities module in the VB Editor Project Explorer, right-click on the module and select “Export File…
      • Save the RXL_OfficeUtilities.bas file to your Desktop.
      • Select the Excel file you wish to import the module to in the VB Editor Project Explorer, right-click, and select “Import File…
      • You can then select the RXL_OfficeUtilities.bas file from your Desktop and click “Open” – this will import a copy of the VBA module into your Excel project file.
    2. Download from repository
      • Alternatively, go to the GitHub repository and the OfficeUITheme sub-folder, where the demo file is saved (https://github.com/r-silk/RXL_VBATools), and directly download the RXL_OfficeUtilities.bas file.
      • Open the Excel file you wish to import the module into, go to to the VB Editor and find the chosen Excel file in the VB Editor Project Explorer.
      • Right-click on the chosen Excel file and select “Import File…“; choose the RXL_OfficeUtilities.bas file where you downloaded it to and click “Open” – this will import a copy of the VBA module into your Excel project file.

  • Excel Status Bar Progress Bar

    Excel Status Bar Progress Bar

    When developing Excel projects, sometimes it becomes necessary to provide some automation for your user, such as clicking a button to import some external data, update a complicated static output, or solve a circularity. In most cases this can be achieved by writing some VBA to run the process for your user and hopefully this will be a quick and simple process.

    However, despite the developers best efforts, the VBA code can run slowly (especially if the end user’s computer is not the most powerful or up-to-date) leaving your user staring at a spinning cursor wheel with no idea of what is happening, how much longer the code will take to finish, or if the code is even still running correctly. In cases where a process is expected to take more than 1-2 seconds to complete, some form of progress bar or progress message can help the user to understand what is happening and how far along the process has got to.

    Potential options to alert the users to what is happening as the VBA code executes include:

    • Displaying a pop-up window (via a UserForm) displaying a message to the user; the message can be updated as the code progresses to keep the user informed.
    • Displaying a pop-up window (via a UserForm) displaying an animated progress bar to visual for the user how far along the total VBA code execution the process has got; this can include a text message as well.

    The main disadvantage of this approach is that it requires additional processing power to load and display the pop-up window (UserForm), and each time your code wants to update the progress bar or the displayed message, the UserForm needs to re-paint the window, which will interrupt the VBA code running and take up some processing power and execution time. I have also noticed that using this approach can lead to a lot of screen flickering, particularly when the code does run faster or frequent updates to the displayed pop-up window are processed. Although this screen flickering will successfully indicate to the user that something is happening and the code is progressing, it can be quite distracting and off-putting to watch.

    The Idea

    You may have noticed that often Excel makes use of the Excel Status Bar (at the bottom of the Excel window) to display messages and even progress bars, such as when saving a workbook. The Status Bar can be easily updated without the process executing in VBA itself (as an update to the displayed Status Bar text triggers processes within the Excel application itself) and as it is not a separate window there is little to no screen flicker with fast/frequent updates.

    I had previously seen a 3rd party Excel add-in which made use of the Excel Status Bar to display a simple text-based progress bar and a percentage value to represent how far along a VBA process the code had got to. This add-in used a series of “-” characters to show the length of the unfilled progress bar and replaced the “-” characters with a “|” or “/” character as the progress bar was filled. This achieved what I wanted to replicate, but as you cannot control the font used in the Status Bar (e.g. setting a mono-space font), when the displayed progress bar text changes from “-” characters to “|” characters the length of the displayed progress bar changes, which is quite distracting and doesn’t look as smooth as I wanted.

    After a lot of Googling, I found a suggestion to look at the extended Unicode character set which can be used to display a series of set characters beyond the standard Latin characters in most default fonts. The Unicode character set includes box drawing characters, which were used in the first text-based computer games in MS-DOS, as well block elements, geometric shapes and other widely used symbols.

    Within the geometric shapes set I found the filled “black square” (Unicode U+25A0) and empty “white square” (Unicode U+25A1). Unfortunately, when displayed in the Excel Status Bar, the white square character is smaller than the filled square meaning that as the progress bar fills and the filled squares replace the empty squares, the progress bar changes length, which was what I was trying to avoid. Fortunately, after some trial an error, I found that the “empty ballot box” (Unicode U+2610) in the miscellaneous symbols section, displays in the Status Bar as the same size as the filled “black square” character.

    The idea was, to create a VBA module that would produce a nice looking text-based progress bar displayed in the Excel Status Bar, complete with percentage completion and optional message string. I wanted to make this a self-contained module which could simply be dropped in to any VBA project and easily used and updated with a couple of sub-routine calls and minimal impact on the execution of the main code which was calling it.

    VBA Status Bar Progress Bar

    Below is the main code for my version of an Excel Status Bar progress bar. It uses three sub-routines and four module-level constants to control its functionality.

    0. Module-Level Constants & Variables

    ' Constants
    Private Const ProgBar_Len As Integer = 20   ' Sets the total size of the progress bar in "blocks"
    Private Const ProgBar_Max As Integer = 255  ' The max Excel StatusBar string in characters
    Private Const Char_Empty As Long = 9744     ' Unicode character Hex: &H2610 => CLng("&H2610") = 9744
    Private Const Char_Full As Long = 9632      ' Unicode character Hex: &H25A0 => CLng("&H25A0") = 9632
    
    ' Variables used for the Progress Bar
    Dim ProgBar_Empty As String * 1, ProgBar_Full As String * 1
    Dim ProgBar_Set As Long
    Dim blUserStBar As Boolean
    

    The constants (declared with the key word Const) are Private as they are only intended to be used within the RXL_ProgressBar module. They are used to avoid having hard-coded values within the main code, and so that if one of the values needs to be updated, it can easily be done by updating the single constant value rather than having to find all occurrences of that hard-coded value in the code.

    The ProgBar_Len value sets how many text squares make up the length of the progress bar the code creates. By default, it is set to 20 blocks so that each filled block/square represents 5%. completion. This can be updated if you wish.

    The ProgBar_Max value represents the maximum number of characters that the Excel StatusBar can hold and should not be changed.

    The Char_Empty and Char_Full values are the Long value representation of the Unicode characters for the progress bar characters. You can change these if you wish, but the value needs to be a whole number value (as a Long data type). As the Unicode character set uses hexadecimal values (represented as “U+” then the hex value), you can use the CLng() function in VBA to convert the hexadecimal portion of the U+ value into the corresponding numeric value.

    1. InitialiseProgressBar()

    Public Sub InitialiseProgressBar(Optional ByRef pStrMessage As String = "")
        
        ' Initialise the persistent global variables and set as required
        '  - where the VBA session has initialised or reset the global variables will be empty
        '    and re-defined, the variables will have been re-defined and initialised at 0 (zero)
        '  - by placing the string definitions in the global variables and only setting once at
        '    initialisation reduces the number nof ChrW() function calls
        ProgBar_Empty = ChrW$(Char_Empty)
        ProgBar_Full = ChrW$(Char_Full)
        
        ' Check the length of the max set portion of the progress bar string
        '  - uses the number of blocks to display plus the length of the max size of extra string
        '    which is made up of the progress % in brackets plus space to add the optional message
        ProgBar_Set = ProgBar_Len + Len(" (100%):  ")
        
        ' Capture the currnet user status for displaying the StatusBar
        blUserStBar = Application.DisplayStatusBar
        
        ' Ensure that the StatusBar is being displayed
        If Application.DisplayStatusBar = False Then _
            Application.DisplayStatusBar = True
        
        ' Set the initial output message and empty progress bar
        Call UpdateProgressBar(0, pStrMessage)
        
    End Sub
    

    The InitialiseProgressBar() sub routine should be called when before the progress bar is used for the first time. The sub routine sets the persistent progress bar characters (ProgBar_Empty and ProgBar_Full) as well calculating the total potential length of the progress bar with the percentage value text displayed (ProgBar_Set).

    The ProgBar_Empty and ProgBar_Full variables are declared as single character strings to optimise their memory allocation, and a single ChrW() function call is made to set each character for these global variables. As the ChrW() and Chr() function calls can be surprisingly expensive function calls, this process reduces the number of calls to just two at initialisation rather than at least two ChrW() function calls every time the progress bar is updated.

    The initialisation function captures the current user chosen Application.DisplayStatusBar state (determines whether Excel displays the Status Bar) and then ensures that it is displayed. Before, finally displaying the initial empty progress bar with 0% completion and with the optional message if provided via the pStrMessage argument.

    2. UpdateProgressBar()

    Public Sub UpdateProgressBar(ByVal pDblProgress As Double, _
                                 Optional ByRef pStrMessage As String = "")
        
        ' Define local variables
        Dim strOut As String
        Dim lnBlocks As Long, lnPercent As Long
        
        ' Restrict the progress value in case progress value passed is outside of the expected range
        If pDblProgress > 1 Then _
            pDblProgress = 1
        If pDblProgress < 0 Then _
            pDblProgress = 0
        ' Convert the passed progress value (% as Double/decimal) to a whole number
        lnPercent = CLng(pDblProgress * 100)
            
        ' Calculate the number of filled progress bar blocks for the passed progress value (%)
        lnBlocks = CLng(pDblProgress * ProgBar_Len)
        ' Restrict the number of bar blocks in case it is calculated as greater than the set total 
        ' progress bar length
        If lnBlocks > ProgBar_Len Then _
            lnBlocks = ProgBar_Len
        
        ' Note - max length for a StatusBar message is 255 characters
        '      - the progress bar aspect of the message will be between 28 and 30 characters
        '        [=> 20 boxes + "_(" + 1-3 digits for percentage complete + "%):__"]
        '      - need to check the length of the passed message string [:= pStrMessage] and restrict
        '         -> i.e. If greater than (255 - 30) then restrict to allowable length - 3, and add 
        '                 "..." at the end to indicate a truncated message string
        If Len(pStrMessage) > (ProgBar_Max - ProgBar_Set) Then
            pStrMessage = Left$(pStrMessage, ProgBar_Max - ProgBar_Set - 3) & "..."
        End If
        
        ' Construct the output string
        strOut = String(lnBlocks, ProgBar_Full) & _
                 String(ProgBar_Len - lnBlocks, ProgBar_Empty) & _
                 " (" & lnPercent & "%)"
        If pStrMessage <> vbNullString Then _
            strOut = strOut & ":  " & pStrMessage
            
        ' Display the constructed output string in the Excel StatusBar
        Application.StatusBar = strOut
        
    End Sub
    

    The UpdateProgressBar() sub routine is the main code in this module, which re-draws the progress bar each time it needs to be updated.

    The sub routine expects the pDblProgress argument to be passed as a decimal number between 0.00 and 1.00, which is equivalent to a percentage progress between 0% and 100%.

    • if a value is passed which is less than 0, then the sub routine will restrict the value to 0.00;
    • if a value is passed which is greater than 1, then the sub routine will restrict the value to 1.00.

    Although the value passed as the pDblProgress argument can have any number of decimal places, when calculating the number of filled progress bar characters to display and the conversion to a percentage figure to display, the value will be rounded down to the nearest whole number. This rounding in both cases is achieved by converting the calculated value (as a decimal value) to a Long data type, which is a non-decimal value. This conversion removes any decimal element to the number and just keeps the whole number portion of the value thereby effectively rounding down to the nearest whole number in an efficient manner.

    Both the pDblProgress argument and the optional pStrMessage argument can be calculated in the sub routine call. For example:

    • if a VBA process is being run which is made up of a fixed number of definable steps (totalSteps),
    • and the variable currStep represents the process step which is being executed
    • the UpdateProgressBar() sub routine could be run with the following code each time a new step starts execution
    UpdateProgressBar pDblProgress:=currStep/totalSteps, _
                      pStrMessage:="Running step " & currStep & " of " & totalSteps
    

    The pStrMessage parameter is optional and can have a text string of any length passed, which is then displayed to the right of the progress bar to inform the user of what the associated VBA code is doing at that time. If it is omitted, then the just the progress bar characters and the percentage progress are displayed.

    The Excel Status Bar has a maximum capacity of 255 characters (as reflected in the ProgBar_Max constant) and will cut off any characters beyond this total. To help deal with this, the code above will calculate the expected total constructed string length (the progress bar characters, % progress characters, and the optional message), and if this will be greater than 255 characters, the optional message string is restricted to the allowable length and “…” characters are added to the end to indicate that the string has been truncated.

    3. CloseProgressBar()

    Public Sub CloseProgressBar()
        
        ' Reset the StatusBar display to the setting used before initialising and displaying the 
        ' progress bar
        ' - checks if one of the calculated global variables is 0 (zero) which would indicate that 
        '   the global variables have cleared and reset and so the blUserStBar variable is likely a 
        '   false negative
        ' - where the calculated global variables are reset, default the DisplayStatusBar setting to True
        Application.DisplayStatusBar = IIf(ProgBar_Set = 0, True, blUserStBar)
        
        ' Release the StatusBar back to the application otherwise the final progress bar message 
        ' will persist
        Application.StatusBar = False
    
    End Sub
    

    The CloseProgressBar() sub routine attempts to reset the Application.DisplayStatusBar property to state that it was before the progress bar was initialised.

    The sub routine also makes an update call to the Application.StatusBar property, but instead of providing a string to update the Status Bar with, it sends a False value. This releases the Status Bar back to Excel so that further updates of the text displayed on the Status Bar comes from the Excel application itself. If this final False value is not sent, then your final progress bar and message will persist on the Status Bar beyond the end of your VBA code execution finishing.

    Example

    An example of the intended use of the three sub routines in the RXL_ProgressBar module is shown below:

    Private Sub Executed_VBA_Code()
        
        ' Start of the main process code
        ...
        
        ' Initialise the StatusBar progress bar with a start message
        InitialiseProgressBar pStrMessage:="Running [process] ..."
        
        ' More main process code
        ...
        
        ' Update the progress bar as each step of the main process is executed
        UpdateProgressBar pDblProgress:=(currStep - 1)/totalSteps, _
                          pStrMessage:="Running [process] - step " & currStep & " of " & totalSteps
        ...
        
        ' Update the progress bar for a final message
        UpdateProgressBar pDblProgress:=1, _
                          pStrMessage:="[process] finished!"
        ' Provide a message box for the user to indicate the main process has finished
        []
        ' Close the progress bar before ending the main process sub routine
        CloseProgressBar
        ' Complete any tidy-up code for the end of the main process code
        ...
        
    End Sub
    

    If you would like to have a closer look at the Status Bar Progress Bar code and have a play around with a demo, there is an Excel file (RXL_ProgressBar Demo.xlsm) which can be downloaded from the RXL_VBATools GitHub repository within the ProgressBar sub-folder:

    https://github.com/r-silk/RXL_VBATools

    To include the RXL_ProgressBar module in your own Excel project, you simply need to import the module into your VBA project and then make the three sub routine calls as discussed above.

    The module can be imported by using one of the two methods below:

    1. Copy from demo file
      • If you have downloaded the RXL_ProgressBar Demo.xlsm file (https://github.com/r-silk/RXL_VBATools), you can copy the module and import it directly.
      • Open the RXL_ProgressBar Demo.xlsm file, as well as the Excel file you wish to import it into, and open the VB Editor.
      • Find the RXL_ProgressBar module in the VB Editor Project Explorer, right-click on the module and select “Export File…
      • Save the RXL_ProgressBar.bas file to your Desktop.
      • Select the Excel file you wish to import the module to in the VB Editor Project Explorer, right-click, and select “Import File…
      • You can then select the RXL_ProgressBar.bas file from your Desktop and click “Open” – this will import a copy of the VBA module into your Excel project file.
    2. Download from repository
      • Alternatively, go to the GitHub repository and the ProgressBar sub-folder, where the demo file is saved (https://github.com/r-silk/RXL_VBATools), and directly download the RXL_ProgressBar.bas file.
      • Open the Excel file you wish to import the module into, go to to the VB Editor and find the chosen Excel file in the VB Editor Project Explorer.
      • Right-click on the chosen Excel file and select “Import File…“; choose the RXL_ProgressBar.bas file where you downloaded it to and click “Open” – this will import a copy of the VBA module into your Excel project file.