This is the sixth of nine installments in the Build an Excel Add-In series. In previous installments, we wrote some code to perform a useful task, enhanced its functionality to make it more broadly applicable, modularized the code and included an auxiliary modular procedure, built a dialog to ask the user for preferences, and brought all the code together. What remains is to construct the user interface elements for Classic Excel (2003 and earlier) and for new Excel (2007 and later), tie up a few loose ends, and create a setup file for easy deployment. A linked outline can be found at the end of this article.
Microsoft Office User Interfaces
Microsoft introduced a flexible and powerful user interface system in Office 97, and this interface has remained in use through Office 2003 (and a subset remains in use in later versions). The user interface consists of various command bars, which include menu bars and toolbars. Command bars can contain menus as well as buttons. Menus can contain submenus and buttons. Submenus can contain, well, you get the idea. This system of menus, toolbars, and buttons is easily customized through the user interface and via VBA.
Here is what our add-in’s menu customizations look like. This article shows how to create these customizations.
The Microsoft Office development team at Microsoft noticed some things about the command-bar-style interface, and about how users interacted with it. The interface was becoming complicated, with three or more levels of menus and submenus existing in some places. Each application ended up with its Tools menu, a kitchen junk drawer of commands that didn’t logically fit into the other menus. Users didn’t know what all the features were, or couldn’t find them, if they were buried more than a level deep, so they kept suggesting new features which already existed. Many users also didn’t know how to clean up their interfaces, so there were too many rows of docked toolbars and too many floating toolbars obstructing the workspace.
In response to the complicated nature of the command-bar-based interface and to the inability of some users to learn to use it as intended, the Microsoft Office team gutted the interface, replacing most toolbar implementations in favor of a new Ribbon in Office 2007. The ribbon is controlled using XML instead of VBA. Instead of toolbars which are visible all or most of the time, the ribbon has a set of different tabs. These tabs contain fewer buttons, and the buttons used more tend to be larger. The tabs become visible when the Office program thinks you might want to use them, and invisible when you actually do want to use them. No parts of the new interface can be undocked, so users who didn’t know how to click the close box of a floating toolbar now have clean interfaces, while users who like dispatching toolbars and palettes to areas on the screen where they are needed have to resort to extra mouse travel distances and excess mouse clicks. The ribbon-based interface is easy to customize using the RibbonX XML dialect and related VBA callbacks, but it is practically inaccessable through the user interface. Unless, of course, you count the QAT (Quick Access Toolbar), which is like a Classic toolbar on strong sedatives. Fortunately the ability of a user to customize their own interface was resuscitated for use in Office 2010.
This article will show how to construct a simple set of Classic Excel menu and toolbar customizations using VBA. The next article in the sequence will show simple XML code and VBA callbacks for use in New Excel.
Classic Excel User Interface Code
The object model for the Office command bar system is straightforward. The CommandBars collection contains menu bars, standard toolbars, and popup menus (context or right-click menus). There is a lot of documentation of this system on the internet. This article shows a set of simple, hard-coded routines, all that is necessary for this simple add-in. There are a number of examples of more elaborate and flexible routines that use tables in Excel workbooks to define all of the interface elements to be created. John Walkenbach describes Creating Custom Menus using his MenuMakr system. I started with his system, but like any good engineer, I tweaked it beyond all recognition to create the system I use in my own add-ins and in projects for my clients.
Here is the simple code in its entirety, copied out of its code module. Explanations for selected sections of the code follow the complete listing.
''========================================================================
'' Peltier Technical Services, Inc., Copyright © 2010. All rights reserved.
''========================================================================
Option Explicit
Const sMenuName As String = "&PTS Charts"
Const sToolbars As String = "Worksheet Menu Bar|Chart Menu Bar|PTS Charts"
Const sButton As String = "PT_Plotter"
Const sOnAction As String = "PT_Plotter_Dialog"
Sub Create_PTPlotter_Menu()
Dim ctlpop As CommandBarPopup
Dim ctlbtn As CommandBarButton
Dim iMenu As Long
Dim vMenu As Variant
Delete_PTPlotter_Menu
vMenu = Split(sToolbars, "|")
For iMenu = LBound(vMenu) To UBound(vMenu)
On Error Resume Next
If Application.CommandBars(vMenu(iMenu)) Is Nothing Then
With Application.CommandBars.Add(vMenu(iMenu), msoBarFloating)
.Left = Application.Left + Application.Width / 4
.Top = Application.Top + 144
.Visible = True
End With
End If
On Error GoTo 0
With Application.CommandBars(vMenu(iMenu))
On Error Resume Next
If .Type = msoBarTypeMenuBar Then
Set ctlpop = .Controls(sMenuName)
On Error GoTo 0
If ctlpop Is Nothing Then
Set ctlpop = .Controls.Add(10, 1, , , True)
With ctlpop
.Caption = sMenuName
.Visible = True
End With
End If
Set ctlbtn = ctlpop.Controls.Add(1, 1, , , True)
Else
Set ctlbtn = .Controls.Add(1, 1, , , True)
.Visible = True
End If
With ctlbtn
.Caption = sButton
.OnAction = "'" & ThisWorkbook.Name & "'!" & sOnAction
.TooltipText = "Create chart from arbitrary data range"
.FaceId = 422
.Visible = True
End With
Set ctlpop = Nothing
End With
Next
End Sub
Sub Delete_PTPlotter_Menu()
Dim ctlpop As CommandBarPopup
Dim ctlbtn As CommandBarControl
Dim iMenu As Long
Dim vMenu As Variant
On Error Resume Next
vMenu = Split(sToolbars, "|")
For iMenu = LBound(vMenu) To UBound(vMenu)
With Application.CommandBars(vMenu(iMenu))
If .Type = msoBarTypeMenuBar Then
Set ctlpop = .Controls(sMenuName)
Do
Set ctlbtn = ctlpop.Controls(sButton)
If ctlbtn Is Nothing Then Exit Do
ctlbtn.Delete
Set ctlbtn = Nothing
Loop
If ctlpop.Controls.Count = 0 Then
ctlpop.Delete
End If
Else
Do
Set ctlbtn = .Controls(sButton)
If ctlbtn Is Nothing Then Exit Do
ctlbtn.Delete
Set ctlbtn = Nothing
Loop
If .Controls.Count = 0 Then
.Delete
End If
End If
End With
Next
End Sub
Explanations
The module starts by defining a few constants. The menu name sMenuName, “&PTS Charts” has an ampersand that tells Excel to use the next character (P) as an accelerator key, that is, to activate the menu when Alt plus the next character is pressed (Alt+P). This character will appear underlined in the menu name: “PTS Charts”.
sToolBars is a pipe-delimited string of command bars which will be customized. The remaining constants list the button caption and the name of the procedure to be run when the button is clicked.
Create_PTPlotter_Menu is the routine that adds our buttons to the Classic Excel interface. The first command in this routine is Delete_PTPlotter_Menu, which removes any existing buttons from this add-in which have somehow been orphaned due to a disorderly shutdown.
The pipe-delimited string is converted into an array of command bar names. The program loops through this array, looking for command bars with each name. If such a command bar is not found, then a new standard command bar is created using that name, other wise the program continues with the found command bar.
If the command bar is a menu bar, the program looks for a menu with the indicated name. If such a menu is not found on the command bar, a new menu is created, otherwise the existing menu is used. Then the button is placed on the menu. If the command bar is a regular toolbar, the button is placed directly on the toolbar.
The caption of the button is assigned, as is the name of the procedure to run when the button is clicked. The procedure name is prepended with the workbook name in single quotes, so Excel will not confuse our procedure with a procedure with the same name in another workbook.
The button is assigned its tooltip text, which appears when the cursor passes over the button.
Built-in button image number 422 is used for the button’s icon. Display FaceIDs is a simple utility that helps you browse Excel’s button images. Not described here is the means to assign a custom button image.
Finally the button is made visible, so users can find it.
Delete_PTPlotter_Menu goes through the command bars listed in the pipe-delimited string, looking for buttons with the name used by our add-in. This is done inside Do loops, in case multiple crashes left behind multiple identical buttons. If the last button of a menu or toolbar is deleted, the empty menu or toolbar is also deleted.
Our Menu and Toolbar
The menu and toolbar customizations produced by the code above are displayed below.
The menu is on the Worksheet Menu Bar; the menu on the Chart menu Bar is the same. The PTS Charts menu and the PTS Charts toolbar already existed, with a variety of my utilities displayed, and the PT_Plotter button is added at the end of the list.
Contents: How to Build an Excel Add-In
- Build an Excel Add-In 1 – Basic Routine
- Build an Excel Add-In 2 – Enhanced Functionality
- Build an Excel Add-In 3 – Auxiliary Modular Functions
- Build an Excel Add-In 4 – Create the Dialog
- Build an Excel Add-In 5 – Tie the Code Together
- Build an Excel Add-In 6 – Interface for 2003
- Build an Excel Add-In 7 – Interface for 2007
- Build an Excel Add-In 8 – Last Steps
- Build an Excel Add-In 9 – Deployment
Create_LblLastPt_Menu
XL-Dennis says
Jon,
Nice write up, is there any particular reasons why You don’t use the Tag property? I find it to be good especially when removing any custom solutions in the Classic UI.
Kind regards,
Dennis
Jon Peltier says
Dennis –
I have used .Tag is some cases. I guess here the .Caption seemed easy enough.
DaleW says
Jon,
Good commentary.
In order to walk before trying to run with classic Excel custom menus, I’m seeing what I can do with J-Walk’s MenuMakr that you mentioned. (If it was a good enough starting point for you . . .)
Am I right that in order to adapt MenuMakr for Excel charting, one likely would want to tweak it to manage CommandBars(“Chart Menu Bar”) instead of just of just “Worksheet Menu Bar”? Seems like it might be common to have partially overlapping lists of macros that a developer would want to add to those two standard menus — to preserve context sensitivity.
Apparently your code elegantly juggles three toolbars at once. In order to someday appreciate it, I’d better practice my toss with just one or two balls at a time.
Jon Peltier says
Dale –
Correct, I manipulate both menu bars. Not terribly elegant: sometimes I hit both in a loop, other times I have entries for both in the menu data table. Almost everything that I add to the Worksheet Menu Bar’s Tools menu is also added to the Chart Menu Bar’s Tools menu, and vice versa.
Jon says
This is a great article series! I need to do just this (deploy a macro with button to both 03 and 07 users) however the business needs it this month before your series will likely be finished unfortunately. Back to the google I suppose.
Jon Peltier says
Jon –
Start with Ron de Bruin’s Change the Ribbon in Excel 2007 or Excel 2010. Then pick up the oustanding book RibbonX: Customizing the Office 2007 Ribbon, by Robert Martin, Ken Puls, and Teresa Hennig.
And thanks for reminding me to get working on the last few articles in that series.
Kurt says
Hi Jon,
me – and a lot of others I asume – are waiting for the next part(s) of this series