VBA Enable Ribbon button by Selection - excel

I am trying to figure out how I can enable a ribbon button based on Selection, I know I need to use Worksheet_SelectionChange Event however I not sure how to proceed. I have exhausted all options looking on how to do it, can someone help with this please, I have asked on Mr Excel but no answers to what I need.
Example of what I am looking for is:
If a column is selected then enable ribbon button a or If a Row is selected then Enable ribbon button b

Use ribbon callbacks and call IRibbonUI.Invalidate or IRibbonUI.InvalidateControl where appropriate. See How to get the reference to the IRibbonUI in VBA? for more information.
You can customize the Ribbon UI by using callback procedures in VBA macros or COM add-ins. For each of the callbacks the add-in implements, the responses are cached. For example, if an add-in writer implements the getImage callback procedure for a button, the function is called once, the image loads, and then if the image needs to be updated, the cached image is used instead of recalling the procedure. This process remains in-place until the code signals that the cached values are invalid by using the Invalidate method, at which time, the callback procedure is again called and the return response is cached. The add-in or VBA macros can then force an immediate update of the UI by calling the Refresh method.
In your custom UI XML file you need to declare the onLoad callback:
<customUI … onLoad=”MyAddInInitialize” …>
And then in VBA you could use:
Dim MyRibbon As IRibbonUI
Sub MyAddInInitialize(Ribbon As IRibbonUI)
Set MyRibbon = Ribbon
End Sub
Sub myFunction()
‘ Invalidates the caches of all of this add-in’s controls
MyRibbon.Invalidate()
End Sub
Read more about the Fluent UI (aka Ribbon UI) in the following articles:
Customizing the 2007 Office Fluent Ribbon for Developers (Part 1 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 2 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3)

This is what I have so far but I'm still a novice.
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnRibbonLoad">
<ribbon>
<tabs>
<tab id="MyTools" label="Tools">
<group id="MoveGroup" label="Move" tag="GroupMove" >
<button id="MoveColumnLeft" tag="EnableLeft" imageMso="GoRtl" screentip="Move Column Left" supertip="Move the selected column to the left" label="Left" size="large" onAction="OnActionButton" getEnabled="GetEnabled" />
<button id="MoveColumnRight" tag="EnableRight" imageMso="GoLeftToRight" screentip="Move Column Right" supertip="Move the selected column to the right" label="Right" size="large" onAction="OnActionButton" getEnabled="GetEnabled" />
`<separator id="MoveSep" />`
<button id="MoveRowUp" tag="EnableUp" imageMso="MessagePrevious" screentip="Move Row Up" supertip="Move the selected row up" label="Up" size="large" onAction="OnActionButton" getEnabled="GetEnabled" />
<button id="MoveRowDown" tag="EnableDown" imageMso="MessageNext" screentip="Move Row Down" supertip="Move the selected row down" label="Down" size="large" onAction="OnActionButton" getEnabled="GetEnabled" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Option Explicit
Public oRibbon As IRibbonUI, bEnabled As Boolean
Sub OnRibbonLoad(ribbon As IRibbonUI)
Set oRibbon = ribbon
bEnabled = True
End Sub
Sub OnActionButton(control As IRibbonControl)
Select Case control.ID
Case "MoveColumnLeft"
bEnabled = enabled
oRibbon.Invalidate
Case "MoveColumnRight"
bEnabled = enabled
oRibbon.Invalidate
End Select
End Sub
Sub GetEnabled(control As IRibbonControl, ByRef enabled)
`Select Case control.ID
Case "MoveColumnLeft"
enabled = enabled
Case "MoveColumnRight"
enabled = enabled
End Select
End Sub

Related

AddIn IRibbonUI callbacks and .Invalidate fails after it installs any other AddIn with its own IRibbonUI

Description: This is Excel 2013 VBA case. I created AddIn which should be able to install and uninstall another AddIns from AddIn list stored in global variable "listOfAddIns" which is Array of Arrays of Variant Type called "valueArray". There are "group control" buttons (for purpose of install or uninstall all available addins) and "individual control" toogle buttons (for install or uninstall specific AddIn). I use a lot of callback functions for purpose of full customization of all elements from VBA.
Problem: Everything works perfectly for installing/uninstalling AddIns without their own Ribbon Tabs. Once installed AddIn has its own Ribbon Tab, I am no longer able to use ribbon object .Invalidate method and call callbacks. I get this poorly descriped Error Message:
Run-Time error'-2147467259(80004005)': Method 'Invalidate' of object 'IRibbonUI' failed
My Idea: Due to described symptomns, I suspect, that there are some duplicity problems of IRibbonControl from different AddIns. Despite a lot of efforts, I cannot figure it out.
Relevant part of CustomUI XML:
<customUI onLoad="OnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon startFromScratch="false">
<tabs>
<tab id = "INST1ID" label="Installer">
<group id="GroupControls" label="Group controls">
<button id="B1ID" label="Install All" size="large" onAction="InstallAll" imageMso="AcceptInvitation" />
<button id="B2ID" label="Uninstall ALL" size="large" onAction="UninstallAll" imageMso="MasterViewClose" />
</group>
<group id="IndividualControls" label="Individual controls">
<toggleButton id="TB1ID" imageMso="HappyFace" label="AddIn1" onAction="TBsControl" size="large" tag="1" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
<toggleButton id="TB2ID" imageMso="HappyFace" label="AddIn2" onAction="TBsControl" size="large" tag="2" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
<toggleButton id="TB3ID" imageMso="HappyFace" label="AddIn3" onAction="TBsControl" size="large" tag="3" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
OnLoad Sub:
Public Sub OnLoad(ribbon As IRibbonUI)
Set ribbonObject = ribbon
End Sub
Main procedure for "Group control" example:
Public Sub InstallAll(control As IRibbonControl)
Call ResetSomePublicVariables
Call CreateEntryValuesAndFillToListOfAddIns
Call CheckAvailabilityAndInstallationOfAddInsAndFillToListOfAddIns
Call UpdateListOfAddInsToInstallAllAvailableAddIns
Call InstallOrUninstallAddInsDueToListOfAddIns
ribbonObject.Invalidate
End Sub
Main procedure for "Individual control" and expanded 4th Sub:
Public Sub TBsControl(control As IRibbonControl, pressed As Boolean)
Call ResetSomePublicVariables
Call CreateEntryValuesAndFillToListOfAddIns
Call CheckAvailabilityAndInstallationOfAddInsAndFillToListOfAddIns
Call UpdateListOfAddInsDueToToogleButtonRequest(control.tag, pressed)
Call InstallOrUninstallAddInsDueToListOfAddIns
ribbonObject.Invalidate
End Sub
Public Sub UpdateListOfAddInsDueToToogleButtonRequest(tag As Long, pressed As Boolean)
Dim valueArray() As Variant
valueArray = listOfAddIns(tag)
If pressed = True Then
valueArray(VAColumnIndex_Installed) = 1
Else
valueArray(VAColumnIndex_Installed) = 0
End If
listOfAddIns(tag) = valueArray
End Sub
Callback example:
Public Sub TBsGetVisible(control As IRibbonControl, ByRef returnedVal)
For Each Item In listOfAddIns
If control.tag = Item(VAColumnIndex_Number) Then
returnedVal = True
Exit For
Else
returnedVal = False
End If
Next Item
End Sub
I didnt used Custom UI Editor or similar software and I guess that it will not be helpful in this phase
I tried to move onLoad sub to ThisWorkbook module. I understand, that moving callbacks to ThisWorkbook is wrong way. I also tried ThisWorkbook and ThisAddin prefixes to IRibbonControl argument, but thats also wrong way. So far with my tries to somehow differentiate eventual IRibbonControl duplicities (my idea only).
I tried to rename IRibbonUI objects in other AddIns without any progress.
I read a lot of sources and saw a lot of examples and different approaches on Microsoft, StackOverFlow on link below but it didnt helped me with this.
https://excelguru.ca/?s=ribbon+part
Any ideas and especially simple solutions of this problem are very welcomed, thank you very much
Solved.
First line of XML file of all my AddIns looked like this and that was a source of all problems described before.
<customUI onLoad="OnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
Same name for onLoad procedure in different AddIns resulted in described behaviour including earlier mentioned error when 2 or more AddIns with this same name was installed. Renaming onLoad procedures to different names solved everything instantly.
Source which helped me: https://excelguru.ca/debugging-ribbonx-invalidateinvalidatecontrol-failures%E2%80%A6/

Excel VBA Buttonbar on sheet

I'm trying to find a ButtonBar solution to add to a sheet in excel (so not in forms, directly on the sheet)
While looking I ran into the ActivX controll: ButtonBar Class, that gets added like the code below.
Can anyone tell me how I can add buttons to this control?
Or do you know of any other buttonbar bype controls I could use on an Exel sheet?
ActiveSheet.OLEObjects.Add(ClassType:="UmOutlookAddin.ButtonBar.1", Link:= _
False, DisplayAsIcon:=False, Left:=96.75, Top:=15, Width:=214.5, _
Height:=17.25).Select
You can control the click unsing the code below, but I have not found a way to add new buttons:
Private Sub ButtonBar1_OnClick(ByVal ButtonId As Long)
I don't think you can add buttons. I tried changing the label and that crashed Excel:
Sub Test()
Dim bb As ButtonBar
Set bb = ActiveSheet.OLEObjects(1).Object
bb.SetButtonLabel PlayButtonId, "Test" 'Boom
End Sub
The ButtonBar seems too unstable and I would not recommend using it.
However, you have other options. For example, on the Developer tab you have the simple Button control:
You can add multiple buttons and then group them:
You could obviously make them adjacent to mimic a bar:
As you can see, they even have a 'pressed' animation when you click them (mid button).
If you don't need the animation then you can just add any shape to work as a button. You would add one shape and format it and then make copies and assign a different macro for each (with right click and Assign Macro...). You would then group them when done. For example:
Or, you could just use a custom ribbon tab if you don't necessarily need the buttons in the sheet itself. Here is an example where I showed step by step how to add a custom ribbon but there are many ways of doing it if you search the web. In that example the custom ribbon is not used to display anything but rather is used for it's Init event. But it's easy to replace the xml at step 2f with something like this:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="InitRibbon">
<ribbon>
<tabs>
<tab id ="TestTabID" Label="Test">
<group id="FirstGroupID" Label="First Group">
<button id="RefreshData" label="Refresh Data" size="large" imageMso="Refresh" onAction="RibbonCallTool" />
<button id="UnloadData" label="Unload Data" size="large" imageMso="RecordsDeleteRecord" onAction="RibbonCallTool" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
in which case you would also have this method in a standard VBA module:
'*******************************************************************************
'Callback ("onAction"). Runs when a control is clicked in the Custom Ribbon tab
'*******************************************************************************
Public Sub RibbonCallTool(ByVal ctrl As IRibbonControl)
Select Case ctrl.ID
Case "RefreshData"
MsgBox "Refresh"
Case "UnloadData"
MsgBox "Unload"
Case Else
Debug.Print "Control <" & ctrl.ID & "> does not have an associated action attached!"
End Select
End Sub
Finally, you could always have a single button that opens a modeless form with all the menus you need.

Excel VBA - Ribbon - Set value of EditBox

I have a custom tab on the excel ribbon with an EditBox. The user should enter a number between 100 and 200 in the EditBox. If the user enters anything other than that, then an error message should pop-up and the text in the EditBox should change to 100. The last part is the one I'm having difficulty with, setting the EditBox text to "100".
'Callback for EditBox onChange event
Sub setQVal(control As IRibbonControl, ByRef text)
If Not IsNumeric(text) Or text < 100 Or text > 200 Then
MsgBox "Error! Please enter a value between 100 and 200."
text = 100 'This doesn't seem to work
Exit Sub
End If
QVal = text
End Sub
Any assistance would be appreciated
The signature of the onChange callback should look like this:
Sub OnChange(control As IRibbonControl, text As String)
To change the text you need to implement the getText callback:
Function GetText(control As IRibbonControl) As String
The getText callback is invoked by the Office applications when the Ribbon UI is invalidated. So, you need to force the UI to invoke callbacks. For each of the callbacks that the code implements, the responses are cached.
For example, if an add-in writer implements the getImage callback procedure for a button, the function is called once, the image loads, and then if the image needs to be updated, the cached image is used instead of recalling the procedure. This process remains in place for the control until the add-in signals that the cached values are invalid by using the InvalidateControl method, at which time, the callback procedure is again called and the return response is cached.
In the following example, starting the host application triggers the onLoad event procedure that then calls a procedure that creates an object representing the Ribbon UI. Next, a callback procedure is defined that invalidates a control on the UI and then refreshes the UI.
<customUI … OnLoad="MyAddInInitialize" …>
And in the code, if you need to update the text (get the getText callback invoked) you need to use the following approach:
Dim MyRibbon As IRibbonUI
Sub MyAddInInitialize(Ribbon As IRibbonUI)
Set MyRibbon = Ribbon
End Sub
Sub myFunction()
MyRibbon.InvalidateControl("editBoxControlID") ' Invalidates the cache of a single control
End Sub

Accessing Microsoft Forms 2.0 Frame Controls in VBA

I found out that the only way to have controls be visible in a Frame in Excel is to add them with right-mouse-click Edit. But when I do this the controls are not listed in VBA.
These are the controls on the worksheet:
The first option button is optToday, the first textbox txtToday, the second option button optDate, the second textbox txtDate. The frame is groupDate, the button cmdGetWeek, the third textbox is txtWeekofMonth.
But this is all that comes up in VBA:
I am looking to call a function to calculate week of month when the user clicks on the button. If the user selected today this is the date that will be sent to the function. If the user selected a different date that will will be sent instead.
What I have so far for the code is this:
Private Sub cmdGetWeek_Click()
Dim selectedDate As Date
Dim calcWeekNum
If (optToday.Value = True) Then
selectedDate = txtToday.Value
Else:
selectedDate = txtDate.Value
End If
MsgBox ("Selected: " & selectedDate)
calcWeekNum = WeekOfMonth(selectedDate) End Sub
I get a Run-Time Error '424' for "If (optToday.Value = True) Then".
What is the correct way of accessing the frame controls?
It's unclear whether you added a Form Frame or an ActiveX Frame.
Form Frames are insertable, by default, from the Insert menu:
In order to be able to handle control events in VBA, you need to use the ActiveX form of the Frame control... But by default, the Frame ActiveX control isn't visible on the Insert menu. You'll need to press the More Controls button:
And then choose Microsoft Forms 2.0 Frame Control from the list of controls:
You'll then need to ensure that you add the ActiveX form of the Option Buttons and other controls... All of the controls (and their events), including the Frame and the Option Buttons will then appear in the Worksheet's event drop-down.

Calling an excel macro from the ribbon

Intro:
I have written some short excel macros (tested, they work fine) and want to link them to a button in the Ribbon (Excel 2010). I had already done it successfully in Excel 2007.
I am using Custom UI Editor to build a new ribbon, which also works fine. Everything is packaged in a .xlam add-in and added to Excel. The ribbon shows up nicely, all other buttons works, but ...
Problem:
when I hit the button that is linked to the macro I get the error: "wrong number of parameters or property assignment not valid" (message translated from Italian, might not be exactly the same in English)
Troubleshooting info:
The macros do not have parameters. The same macros can be successfully called and executed manually. I am even able to add the same macros to the Quick Access Toolbar.
Here is the specific portion of the ribbon script:
<group id="DupNumber" label="Number" insertBeforeMso="GroupNumber" >
<comboBox idMso="NumberFormatGallery"/>
<box id="HN1" boxStyle="horizontal">
<buttonGroup id="HNButtonGroup1">
<button id="Euro" onAction="Roberto.xlam!EURZ" imageMso="F" supertip="text ..."/>
<button id="EuroNZ" onAction="Roberto.xlam!EURNZ" imageMso="E" supertip="text ..."/>
<button idMso="PercentStyle"/>
<button id="Comma" onAction="Roberto.xlam!NewCommaFormat" imageMso="C" supertip="test ..."/>
<button idMso="PercentStyle"/>
</buttonGroup>
</box>
and here are the macros:
Sub EURZ()
Application.ActiveCell.NumberFormat = "€ #,##0.00"
End Sub
Sub EURNZ()
Application.ActiveCell.NumberFormat = "€ #,##0"
End Sub
Sub NewCommaFormat()
Application.ActiveCell.NumberFormat = "#,##0"
End Sub
Can you help me?
Thanks
Roberto
I believe you need to add this param to your macro: control As IRibbonControl
So it should look like this:
Sub EURZ(control As IRibbonControl)
Application.ActiveCell.NumberFormat = "€ #,##0.00"
End Sub

Resources