Sheet names in a MenuBar menu

P

Petr Danes

I recently started teaching in a software technical school, and my first
course was in advanced Excel. Naturally, I prepared a workbook with lots of
examples on various worksheets, but not until I started lecturing did I
realize that lots of sheets mean lots of scrolling left and right to locate
the correct sheet. I'd never needed a workbook with many sheets for any of
my own projects, nor those I had created for others. The course went fairly
well anyway, but I wanted a better way to navigate between sheets.

Part of the idea came from ASAP Utilities' function of creating a separate
index sheet with clickable hyperlinks for all sheets, and part of the code
from John Walkenbach's Excel Power Programming book. (My thanks to you
both.)

The clickable index sheet function is handy, but solves only half of the
problem - it quickly locates a sheet 'somewhere', but getting back to the
index sheet for another shot out into the myriad of sheets is still a
hassle. I decided a menu bar approach suited my needs better.

The code reads in all the sheet names and creates a dropdown menu of sheet
names. Originally it included a sort as well, but then I decided that I
prefer the menu in the same order as the sheets in the workbook, and my
naming and ordering scheme results in the names being in order anyway. The
sort is still there, just the call commented out, in case someone wants to
use it that way. There is no error checking and it takes no account of
hidden properties of sheets, since I don't use that, but it could be added
easily enough. The code is called from the Workbook_Open routine, so that
the menu is freshly rebuilt every time the workbook opens. Probably not
completely necessary, but the execution time is trivial and it makes for
ease of use. I initially added a call from the Workbook_NewSheet event, but
abandoned that since the code adds the default sheet name in use at the
instant of creation (Sheet1 or some such), which is not very useful, since I
always rename the sheet to something sensible, but by then the sheet has
already been added to the menu with default name. If there were
Workbook_SheetRename and Workbook_SheetDelete events, I would add calls
there, as well as the NewSheet event. That would make it completely
automatic and always current, but this works well enough. The first item on
the menu, before the sheet names is a refresh call, so I can keep the menu
up to date manually without having to close and re-open the workbook or
switch to the VBA editor.

There are two versions of the menu code, one creates a simple dropdown menu
list, suitable for up to approximately thirty sheets on my machine (a bigger
monitor would allow more), the second creates a two-stage list, grouped by
the first character in the sheet name. I name my sheets by course day, so
that "1.n xxxxx" is a sheet for the first day of the course, "2.n xxxxx" for
the second day and so on. "0.n xxxxx" sheets are general sheets, not
intended for any specific day. The second method obviously better handles a
very large number of sheets, at the cost of a fraction of a second delay
while the second tier unfolds. At the moment, my code creates both versions.
I'll have to use them for a while to see which one I prefer for classroom
use.

Pete

--
This e-mail address is fake, to keep spammers and their auto-harvesters out
of my hair. If you want to get in touch personally, I am 'pdanes' and I use
yahoo mail. But please use the newsgroup when possible, so that all may
benefit from the exchange of ideas.



Start code (watch for line wrap!)
====================================================

Option Explicit

Dim AEb As CommandBarButton
Dim AEp As CommandBarPopup
Dim AEp2 As CommandBarPopup
Dim SheetNames() As String

Sub SheetMenu()
Dim i&

ReDim SheetNames(1 To ActiveWorkbook.Sheets.Count)

' Fill array with sheet names
For i = 1 To UBound(SheetNames)
SheetNames(i) = Sheets(i).Name
Next i

' Sort the array in ascending order
' Not used in current implementation,
' but simply uncomment line below to order sheet names.
'BubbleSort SheetNames

' Build the sheet menus
MakeMenuItem ' simple menu
MakeMenuItem2 ' 2-tier menu

End Sub

Private Sub MakeMenuItem()
' Create the menu bar addition
Dim i&
With Application.CommandBars("Worksheet Menu Bar")
On Error Resume Next
.Controls("Sheets").Delete
On Error GoTo 0
Set AEp = .Controls.Add(msoControlPopup)
With AEp
.Caption = "Sheets"
Set AEb = .Controls.Add(msoControlButton)
With AEb
.Caption = "(...Refresh...)"
.OnAction = "GoToSheet"
.Parameter = "(...Refresh...)"
End With
For i = 1 To UBound(SheetNames)
Set AEb = .Controls.Add(msoControlButton)
With AEb
.Caption = SheetNames(i)
.OnAction = "GoToSheet"
.Parameter = SheetNames(i)
End With
Next i
End With
End With
End Sub

Private Sub MakeMenuItem2()
' Create the menu bar addition
Dim i&, nmhld$

' Only one character is used for grouping
nmhld = "!"
With Application.CommandBars("Worksheet Menu Bar")
On Error Resume Next
.Controls("Sheets2").Delete
On Error GoTo 0
Set AEp = .Controls.Add(msoControlPopup)
With AEp
.Caption = "Sheets2"
Set AEb = .Controls.Add(msoControlButton)
With AEb
.Caption = "(...Refresh...)"
.OnAction = "GoToSheet"
.Parameter = "(...Refresh...)"
End With
For i = 1 To UBound(SheetNames)
If nmhld <> Left$(SheetNames(i), Len(nmhld)) Then
Set AEp2 = .Controls.Add(msoControlPopup)
AEp2.Caption = Left$(SheetNames(i), InStr(SheetNames(i),
"."))
nmhld = Left$(SheetNames(i), Len(nmhld))
End If
With AEp2
Set AEb = .Controls.Add(msoControlButton)
With AEb
.Caption = SheetNames(i)
.OnAction = "GoToSheet"
.Parameter = SheetNames(i)
End With
End With
Next i
End With
End With
End Sub

Sub BubbleSort(List() As String)
' Sorts the List array in ascending order
Dim First As Integer, Last As Integer
Dim i As Integer, j As Integer
Dim Temp

First = LBound(List)
Last = UBound(List)
For i = First To Last - 1
For j = i + 1 To Last
If List(i) > List(j) Then
Temp = List(j)
List(j) = List(i)
List(i) = Temp
End If
Next j
Next i
End Sub

Public Sub GoToSheet()
' Action routine called by the menu
' Passed parameter is either the sheet name to call or the refresh command
If CommandBars.ActionControl.Parameter = "(...Refresh...)" Then
SheetMenu
Else
ActiveWorkbook.Sheets(CommandBars.ActionControl.Parameter).Activate
End If
End Sub
 
J

john

A simple way would be to use the builtin popup.
Paste followin code into Thisworkbook page. When you right click any
worksheet, you will get a menu of all worksheet tabs in activeworkbook. Click
to select required worksheet.

Hope useful.

Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target
As Range, Cancel As Boolean)
On Error Resume Next
If ActiveWorkbook.Sheets.Count <= 16 Then
application.CommandBars("Workbook Tabs").ShowPopup 500, 225
Else
application.CommandBars("Workbook Tabs").Controls("More
Sheets...").Execute
End If
On Error GoTo 0
Cancel = True
End Sub
 
M

Mark Ivey

John,

I really like the function you listed, but I also use my right click menu
extensively....

Do you have something like this that doesn't do away with the rest of the
features of a right click menu?

Mark Ivey
 
J

john

Mark,
not really - code is not something I personally developed - I came across
this approach in my early programming days and I use for large workbooks. As
a suggestion, you could try & place code in say SheetBeforeDoubleClick event
if you do not want to lose RightClick function?
 
M

Mark Ivey

John,

Good idea...

Either way, the meat of the code is useful. I think I may have a good
application for it.

Thanks for sharing...

Mark Ivey
 
J

john

we are here to help each other - glad you find it of some use hope it helped
Petr with his problem.
 
S

splot!

I know this may not be especially elegant but I've always solved the
'return to menu sheet' problem by having a hyperlink back to it on
each of the individual sheets. I usually have some freozen lines a
thte top of my sheets so it is easy to have a hyperling in that area
where it is readily found.

Regards

Chris
 
R

Rick Rothstein \(MVP - VB\)

On my copy of XL2003 (and this works in XL2007), I get the same popup menu
as this SheetBeforeRightClick event procedure produces when I right-click
the navigation controls (the four little buttons) to the left of the Sheet
Tabs.

Rick
 
M

Mark Ivey

Good point Rick...

I always new this feature was there, but I usually forget about it when I
really need it...


One reason I made the addin I mentioned previously in this thread was
because I usually have several workbooks open at the same time and need to
navigate not only to the workbook, but also to a specific worksheet.

Mark
 
G

Gord Dibben

I prefer Bob Phillips' browsesheet code to go to any sheet in the workbook.

Sub BrowseSheets()
Const nPerColumn As Long = 38 'number of items per column
Const nWidth As Long = 13 'width of each letter
Const nHeight As Long = 18 'height of each row
Const sID As String = "___SheetGoto" 'name of dialog sheet
Const kCaption As String = " Select sheet to goto"
'dialog caption


Dim i As Long
Dim TopPos As Long
Dim iBooks As Long
Dim cCols As Long
Dim cLetters As Long
Dim cMaxLetters As Long
Dim cLeft As Long
Dim thisDlg As DialogSheet
Dim CurrentSheet As Worksheet
Dim cb As OptionButton
Application.ScreenUpdating = False
If ActiveWorkbook.ProtectStructure Then
MsgBox "Workbook is protected.", vbCritical
Exit Sub
End If
On Error Resume Next
Application.DisplayAlerts = False
ActiveWorkbook.DialogSheets(sID).Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set CurrentSheet = ActiveSheet
Set thisDlg = ActiveWorkbook.DialogSheets.Add
With thisDlg
.Name = sID
.Visible = xlSheetHidden
'sets variables for positioning on dialog
iBooks = 0
cCols = 0
cMaxLetters = 0
cLeft = 78
TopPos = 40
For i = 1 To ActiveWorkbook.Worksheets.Count
If i Mod nPerColumn = 1 Then
cCols = cCols + 1
TopPos = 40
cLeft = cLeft + (cMaxLetters * nWidth)
cMaxLetters = 0
End If
Set CurrentSheet = ActiveWorkbook.Worksheets(i)
cLetters = Len(CurrentSheet.Name)
If cLetters > cMaxLetters Then
cMaxLetters = cLetters
End If
iBooks = iBooks + 1
.OptionButtons.Add cLeft, TopPos, cLetters * nWidth, 16.5
.OptionButtons(iBooks).text = _
ActiveWorkbook.Worksheets(iBooks).Name
TopPos = TopPos + 13
Next i
.Buttons.Left = cLeft + (cMaxLetters * nWidth) + 24
CurrentSheet.Activate
With .DialogFrame
.Height = Application.Max(68, _
Application.Min(iBooks, nPerColumn) * nHeight + 10)
.Width = cLeft + (cMaxLetters * nWidth) + 24
.Caption = kCaption
End With
.Buttons("Button 2").BringToFront
.Buttons("Button 3").BringToFront
Application.ScreenUpdating = True
If .Show Then
For Each cb In thisDlg.OptionButtons
If cb.Value = xlOn Then
ActiveWorkbook.Worksheets(cb.Caption).Select
Exit For
End If
Next cb
Else
MsgBox "Nothing selected"
End If
Application.DisplayAlerts = False
.Delete
End With
End Sub

NOTE: Gary Brown revised this code to cover hidden sheets and chart sheets and
other potential problems.

See this thread for that revised code.

http://snipurl.com/1l8o4

I would make one more change to Gary's code.

Const nWidth As Long = 8 'width of each letter
Change to 10 or sheetnames < 4 chars will be incomplete


Gord Dibben MS Excel MVP
 
P

Petr Danes

Thanks Mark, but that has more functionality than is appropriate for my
task. I only need to navigate around the one workbook, and the extra
fiddling of opening the UserForm, clicking in one ListBox, then clicking in
another ListBox, then finally clicking a CommandButton is more effort than I
want to expend. With the menu approach, I press the mouse button on the menu
bar caption to open the menu, slide down to the sheet name I want and let go
of the button. I find it difficult to imagine anything simpler.

Your approach looks good for navigating around multiple workbooks, though,
and I will keep it in mind for such an eventuality. One suggestion, though,
if you don't mind. If you add a double-click event handler to the second
ListBox, you could dispense with the OK button, or at least have it as an
alternative. Maybe a double-click event handler would even be appropriate
for the first ListBox, with some default action, like jumping to the first
sheet in the workbook, or maybe the most recently used sheet in the workbook
if you want to get creative. (My apologies if it already does stuff like
that. I didn't download it, just looked at the website.)

Petr
 
P

Petr Danes

Didn't know about that one, Rick, thanks for the tip. Unfortunately, it's
limited to 15 sheet names before you have to use the "More sheets..."
feature to access stuff further to the right, which is right around the
number where the need for a direct jump function starts rearing its head.
And then you get a ListBox with scroll bars, which is even more clicking and
fiddling. It's still useful even so - I wonder if there is a reg hack that
would force Excel to show more than 15 names.

Petr
 
P

Petr Danes

Thanks Dave, that looks nice. Takes a little bit more real estate than my
menu bar, but it's neat that it gives you the option of sorting or not.
Another one for the toolbox.

Petr
 
P

Petr Danes

Hello Gord.

Also not bad, although I think it forces the user to take unneeded steps.
Why should it be necessary to click a RadioButton and then click OK, rather
than just click on the name directly to initiate the action?

And having a Message Box pop up to confirm that I clicked the cancel button
seems completely pointless. Inform the user that they didn't select anything
and force them to dismiss yet another dialog box when all they wanted was to
cancel the action?

But tastes vary, it may suit some people.

Petr
 
P

Petr Danes

Thank you everyone for chiming in. My original arrangement is still the best
for my (admittedly limited) needs, so I will likely stick with it, but I'm
glad it stimulated some discussion and I've learned a few new things along
the way as well. And if my contribution helps someone else, so much the
better.

Long live newsgroups.

Petr
 
G

Gord Dibben

Hello Gord.

Also not bad, although I think it forces the user to take unneeded steps.
Why should it be necessary to click a RadioButton and then click OK, rather
than just click on the name directly to initiate the action?

I prefer confirming by seeing what sheet I have selected before leaping to that
sheet.

If you want a one-click see Dave P's sheet navigation bar.
And having a Message Box pop up to confirm that I clicked the cancel button
seems completely pointless.
Inform the user that they didn't select anything
and force them to dismiss yet another dialog box when all they wanted was to
cancel the action?

You can easily remove that bit of code if that is your preference.
But tastes vary, it may suit some people.

Thank you for that concession.

Suits me just fine and I am pleased that Bob made it available.
 
S

Shah Shailesh

Hi Petr,

I had also developed one addins "workbook navigation" for my needs. If you
are interested you can download it from below site.

Some features:
1. Index of all Sheet Names in separate worksheet with hyperlink.
2. Sort sheets ascending or descending, Create\remove multiple windows.
3. Synchronies all the sheets to view same area in the worksheet.
4. Copy activesheet without truncated of cells having more then 255
characters.

...more + image in the webpage.

http://in.geocities.com/shahshaileshs/


Regards,
Shailesh Shah
http://in.geocities.com/shahshaileshs/
If You Can't Excel with Talent, Triumph with Effort.


Free Addins Office Menu-2003 for Office-2007
http://in.geocities.com/shahshaileshs/menuaddins
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top