View Full Version : Customizing Right-Click Context Menu In Excel For CommandBars Cell Row And Column
MrBlackd
08-04-2013, 02:08 AM
I have compiled a set of macros and user forms (lets call them extras) and I have managed to place them in the context menu of the cell.
I have used the logic described in this article (http://msdn.microsoft.com/en-us/library/office/gg469862(v=office.14).aspx). Check the code of AddToCellMenu and DeleteFromCellMenu Subs.
It worked on one or more cells perfectly, however I realized that when I right clicked a row or column the context menu did not have the extras.
It applied on one or more cells cause I have used
Dim ContextMenu As CommandBar
Set ContextMenu = Application.CommandBars("Cell")
but in order to make it work for rows context menu I have to use
Dim ContextMenu As CommandBar
Set ContextMenu = Application.CommandBars("Rows")
and for columns context menu use
Dim ContextMenu As CommandBar
Set ContextMenu = Application.CommandBars("Columns")
I would like to combine them somehow so as not to repeat unnecessarily lines of code for each context menu so as to apply the extras, especially if you think that they are in several submenus also...
Please advise... and thank you in advance for any idea.
Excel Fox
08-04-2013, 03:30 PM
Take out the Workbook_Activate event. And use the code below to which I've made some modifications
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
If Target.Columns.Count = Columns.Count Then
Call AddToCellMenu("Row")
ElseIf Target.Rows.Count = Rows.Count Then
Call AddToCellMenu("Column")
Else
Call AddToCellMenu("Cell")
End If
End Sub
Private Sub Workbook_Deactivate()
Call DeleteFromCellMenu
End Sub
Sub AddToCellMenu(strCommandBarName As String)
Dim ContextMenu As CommandBar
Dim MySubMenu As CommandBarControl
' Delete the controls first to avoid duplicates.
Call DeleteFromCellMenu
' Set ContextMenu to the Cell context menu.
Set ContextMenu = Application.CommandBars(strCommandBarName)
' Add one built-in button(Save = 3) to the Cell context menu.
ContextMenu.Controls.Add Type:=msoControlButton, ID:=3, before:=1
' Add one custom button to the Cell context menu.
With ContextMenu.Controls.Add(Type:=msoControlButton, before:=2)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "ToggleCaseMacro"
.FaceId = 59
.Caption = "Toggle Case Upper/Lower/Proper"
.Tag = "My_Cell_Control_Tag"
End With
' Add a custom submenu with three buttons.
Set MySubMenu = ContextMenu.Controls.Add(Type:=msoControlPopup, before:=3)
With MySubMenu
.Caption = "Case Menu"
.Tag = "My_Cell_Control_Tag"
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "UpperMacro"
.FaceId = 100
.Caption = "Upper Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "LowerMacro"
.FaceId = 91
.Caption = "Lower Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "ProperMacro"
.FaceId = 95
.Caption = "Proper Case"
End With
End With
' Add a separator to the Cell context menu.
ContextMenu.Controls(4).BeginGroup = True
End Sub
Sub DeleteFromCellMenu()
Dim ContextMenu As CommandBar
Dim ctrl As CommandBarControl
Dim lng As Long
Const strBarNames As String = "Cell,Column,Row"
' Set ContextMenu to the Cell context menu.
For lng = LBound(Split(strBarNames, ",")) To UBound(Split(strBarNames, ","))
Set ContextMenu = Application.CommandBars(Split(strBarNames, ",")(lng))
' Delete the custom controls with the Tag : My_Cell_Control_Tag.
For Each ctrl In ContextMenu.Controls
If ctrl.Tag = "My_Cell_Control_Tag" Then
ctrl.Delete
End If
Next ctrl
Next lng
' Delete the custom built-in Save button.
While Not ContextMenu.FindControl(ID:=3) Is Nothing
ContextMenu.FindControl(ID:=3).Delete
Wend
End Sub
patel
08-04-2013, 07:44 PM
Very interesting, can you paste the whole code or attach a sample file ? some subs are missing
Excel Fox
08-05-2013, 12:03 AM
Sure Patel.
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
If Target.Columns.Count = Columns.Count Then
Call AddToCellMenu("Row")
ElseIf Target.Rows.Count = Rows.Count Then
Call AddToCellMenu("Column")
Else
Call AddToCellMenu("Cell")
End If
End Sub
Private Sub Workbook_Deactivate()
Call DeleteFromCellMenu
End Sub
Sub AddToCellMenu(strCommandBarName As String)
Dim ContextMenu As CommandBar
Dim MySubMenu As CommandBarControl
' Delete the controls first to avoid duplicates.
Call DeleteFromCellMenu
' Set ContextMenu to the Cell context menu.
Set ContextMenu = Application.CommandBars(strCommandBarName)
' Add one built-in button(Save = 3) to the Cell context menu.
ContextMenu.Controls.Add Type:=msoControlButton, ID:=3, before:=1
' Add one custom button to the Cell context menu.
With ContextMenu.Controls.Add(Type:=msoControlButton, before:=2)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "ToggleCaseMacro"
.FaceId = 59
.Caption = "Toggle Case Upper/Lower/Proper"
.Tag = "My_Cell_Control_Tag"
End With
' Add a custom submenu with three buttons.
Set MySubMenu = ContextMenu.Controls.Add(Type:=msoControlPopup, before:=3)
With MySubMenu
.Caption = "Case Menu"
.Tag = "My_Cell_Control_Tag"
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "UpperMacro"
.FaceId = 100
.Caption = "Upper Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "LowerMacro"
.FaceId = 91
.Caption = "Lower Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!" & "ProperMacro"
.FaceId = 95
.Caption = "Proper Case"
End With
End With
' Add a separator to the Cell context menu.
ContextMenu.Controls(4).BeginGroup = True
End Sub
Sub DeleteFromCellMenu()
Dim ContextMenu As CommandBar
Dim ctrl As CommandBarControl
Dim lng As Long
Const strBarNames As String = "Cell,Column,Row"
' Set ContextMenu to the Cell context menu.
For lng = LBound(Split(strBarNames, ",")) To UBound(Split(strBarNames, ","))
Set ContextMenu = Application.CommandBars(Split(strBarNames, ",")(lng))
' Delete the custom controls with the Tag : My_Cell_Control_Tag.
For Each ctrl In ContextMenu.Controls
If ctrl.ID = 3 Or ctrl.Tag = "My_Cell_Control_Tag" Then
ctrl.Delete
End If
Next ctrl
Next lng
End Sub
Sub ToggleCaseMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
Select Case cell.Value
Case UCase(cell.Value): cell.Value = LCase(cell.Value)
Case LCase(cell.Value): cell.Value = StrConv(cell.Value, vbProperCase)
Case Else: cell.Value = UCase(cell.Value)
End Select
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub UpperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = UCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub LowerMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = LCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub ProperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = StrConv(cell.Value, vbProperCase)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
patel
08-05-2013, 10:27 AM
Thanks for the code.
I think ther'is a bug , now I have this right clic menu , without added Items, but with many save items, how can I reset it ?
Excel Fox
08-05-2013, 06:26 PM
Patel, yes you are right. I missed that one. I've corrected that above.
MrBlackd
08-05-2013, 06:34 PM
Sorry but.... which code is the correct?
Excel Fox
08-05-2013, 06:44 PM
The one in post #4
patel
08-05-2013, 09:24 PM
Ok, thank you, it works well now.
MrBlackd
08-05-2013, 10:14 PM
I have managed to make the adjustments needed for my set of macros and extra options needed in the context menu and it works if I make it on an xlsm file.
However since I needed this to work on all the instances of excel, I thought that I should place it on personal macro workbook or at least save the xlsm as add-in (xlam).
Sadly none of these options worked so wherever I right-click nothing happens. In none of the 2 options available...
Any suggestion??
Thanks again in advance!
patel
08-05-2013, 10:32 PM
http://msdn.microsoft.com/en-us/library/office/gg469862%28v=office.14%29.aspx
Excel Fox
08-05-2013, 11:41 PM
You will not be able to trigger workbook level events across your application unless you create application level events using the WithEvents keyword.
Paste this entire code in the ThisWorkbook class module of the Personal macro workbook, save and restart excel
Option Explicit
Public WithEvents App As Application
Private Sub App_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
If Target.Columns.Count = Columns.Count Then
Call AddToCellMenu("Row")
ElseIf Target.Rows.Count = Rows.Count Then
Call AddToCellMenu("Column")
Else
Call AddToCellMenu("Cell")
End If
End Sub
Private Sub App_WorkbookDeactivate(ByVal Wb As Workbook)
Call DeleteFromCellMenu
End Sub
Sub AddToCellMenu(strCommandBarName As String)
Dim ContextMenu As CommandBar
Dim MySubMenu As CommandBarControl
' Delete the controls first to avoid duplicates.
Call DeleteFromCellMenu
' Set ContextMenu to the Cell context menu.
Set ContextMenu = Application.CommandBars(strCommandBarName)
' Add one built-in button(Save = 3) to the Cell context menu.
ContextMenu.Controls.Add Type:=msoControlButton, ID:=3, before:=1
' Add one custom button to the Cell context menu.
With ContextMenu.Controls.Add(Type:=msoControlButton, before:=2)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "ToggleCaseMacro"
.FaceId = 59
.Caption = "Toggle Case Upper/Lower/Proper"
.Tag = "My_Cell_Control_Tag"
End With
' Add a custom submenu with three buttons.
Set MySubMenu = ContextMenu.Controls.Add(Type:=msoControlPopup, before:=3)
With MySubMenu
.Caption = "Case Menu"
.Tag = "My_Cell_Control_Tag"
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "UpperMacro"
.FaceId = 100
.Caption = "Upper Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "LowerMacro"
.FaceId = 91
.Caption = "Lower Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "ProperMacro"
.FaceId = 95
.Caption = "Proper Case"
End With
End With
' Add a separator to the Cell context menu.
ContextMenu.Controls(4).BeginGroup = True
End Sub
Sub DeleteFromCellMenu()
Dim ContextMenu As CommandBar
Dim ctrl As CommandBarControl
Dim lng As Long
Const strBarNames As String = "Cell,Column,Row"
' Set ContextMenu to the Cell context menu.
For lng = LBound(Split(strBarNames, ",")) To UBound(Split(strBarNames, ","))
Set ContextMenu = Application.CommandBars(Split(strBarNames, ",")(lng))
' Delete the custom controls with the Tag : My_Cell_Control_Tag.
For Each ctrl In ContextMenu.Controls
If ctrl.ID = 3 Or ctrl.Tag = "My_Cell_Control_Tag" Then
ctrl.Delete
End If
Next ctrl
Next lng
End Sub
Sub ToggleCaseMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
Select Case cell.Value
Case UCase(cell.Value): cell.Value = LCase(cell.Value)
Case LCase(cell.Value): cell.Value = StrConv(cell.Value, vbProperCase)
Case Else: cell.Value = UCase(cell.Value)
End Select
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub UpperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = UCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub LowerMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = LCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub ProperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = StrConv(cell.Value, vbProperCase)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Private Sub Workbook_Open()
Set App = Application
End Sub
MrBlackd
08-06-2013, 12:26 AM
Actually I have placed only this in ThisWorkbook
Option Explicit
Public WithEvents App As Application
Private Sub App_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
If Target.Columns.Count = Columns.Count Then
Call AddToCellMenu("Row")
ElseIf Target.Rows.Count = Rows.Count Then
Call AddToCellMenu("Column")
Else
Call AddToCellMenu("Cell")
End If
End Sub
Private Sub App_WorkbookDeactivate(ByVal Wb As Workbook)
Call DeleteFromCellMenu
End Sub
Private Sub Workbook_Open()
Set App = Application
End Sub
This in a Module called Context_Menus
Sub AddToCellMenu(strCommandBarName As String)
Dim ContextMenu As CommandBar
Dim MySubMenu As CommandBarControl
' Delete the controls first to avoid duplicates.
Call DeleteFromCellMenu
' Set ContextMenu to the Cell context menu.
Set ContextMenu = Application.CommandBars(strCommandBarName)
' Add one built-in button(Save = 3) to the Cell context menu.
ContextMenu.Controls.Add Type:=msoControlButton, ID:=3, before:=1
' Add one custom button to the Cell context menu.
With ContextMenu.Controls.Add(Type:=msoControlButton, before:=2)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "ToggleCaseMacro"
.FaceId = 59
.Caption = "Toggle Case Upper/Lower/Proper"
.Tag = "My_Cell_Control_Tag"
End With
' Add a custom submenu with three buttons.
Set MySubMenu = ContextMenu.Controls.Add(Type:=msoControlPopup, before:=3)
With MySubMenu
.Caption = "Case Menu"
.Tag = "My_Cell_Control_Tag"
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "UpperMacro"
.FaceId = 100
.Caption = "Upper Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "LowerMacro"
.FaceId = 91
.Caption = "Lower Case"
End With
With .Controls.Add(Type:=msoControlButton)
.OnAction = "'" & ThisWorkbook.Name & "'!ThisWorkbook." & "ProperMacro"
.FaceId = 95
.Caption = "Proper Case"
End With
End With
' Add a separator to the Cell context menu.
ContextMenu.Controls(4).BeginGroup = True
End Sub
Sub DeleteFromCellMenu()
Dim ContextMenu As CommandBar
Dim ctrl As CommandBarControl
Dim lng As Long
Const strBarNames As String = "Cell,Column,Row"
' Set ContextMenu to the Cell context menu.
For lng = LBound(Split(strBarNames, ",")) To UBound(Split(strBarNames, ","))
Set ContextMenu = Application.CommandBars(Split(strBarNames, ",")(lng))
' Delete the custom controls with the Tag : My_Cell_Control_Tag.
For Each ctrl In ContextMenu.Controls
If ctrl.ID = 3 Or ctrl.Tag = "My_Cell_Control_Tag" Then
ctrl.Delete
End If
Next ctrl
Next lng
End Sub
And finally another module that holds the actual macros
Sub ToggleCaseMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
Select Case cell.Value
Case UCase(cell.Value): cell.Value = LCase(cell.Value)
Case LCase(cell.Value): cell.Value = StrConv(cell.Value, vbProperCase)
Case Else: cell.Value = UCase(cell.Value)
End Select
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub UpperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = UCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub LowerMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = LCase(cell.Value)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Sub ProperMacro()
Dim CaseRange As Range
Dim CalcMode As Long
Dim cell As Range
On Error Resume Next
Set CaseRange = Intersect(Selection, _
Selection.Cells.SpecialCells(xlCellTypeConstants, xlTextValues))
On Error GoTo 0
If CaseRange Is Nothing Then Exit Sub
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
For Each cell In CaseRange.Cells
cell.Value = StrConv(cell.Value, vbProperCase)
Next cell
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
And it works just fine in personal macro workbook.
I believe that it will do just fine also in xlam ;)
Thank you very much for all the trouble you ve gone into.
I promise to share the addin with my exras as soon as it is tested properly.
Excel Fox
08-06-2013, 01:36 AM
I believe that it will do just fine also in xlam ;) Yes it should
patel
08-06-2013, 09:05 AM
You will not be able to trigger workbook level events across your application unless you create application level events using the WithEvents keyword.
I think it would be enough to eliminate this sub to make the manu change permanent
Private Sub Workbook_Deactivate()
Call DeleteFromCellMenu
End Sub
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.