I'm trying to dynamically change a button image on a custom ribbon in Access 365.
I so far have this ribbon xml:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onl oad="OnRibbonLoad" loadImage="LoadImages">
<ribbon startFromScratch="false">
<tabs>
<tab id="elcdb" label="EDB">
<group id="ElecDBMaint" label="Maintenance">
<splitButton id="MySplitButton2" size="large">
<button id="Button50" label="Large Button with Menu"/>
<menu id="Menu20" itemSize="normal">
<button id="Button60" label="First"/>
<button id="Button70" label="Second"/>
<button id="Button80" label="Third"/>
</menu>
</splitButton>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Which gives an ribbon which looks like this:
The idea is that after a button is clicked, the image will change to show the last clicked button so that when the button is clicked again, it executes the previous action rather than the user drilling down each and every time through the menus to find the previous button...
I guess there has to be a GetImage call back in there to set the image but I cannot find where to put it. I also need to know how the callback sets the image - I'm looking for the inbuilt function images not external ones (FileSave, FileSendAsAttachment, FileQuickPrint for example).
I'm also guessing the OnAction button somehow needs to keep track of the last function unless there is another callback which can determine the state of the button???
I have managed to find a basis which looks like this:
You can hover over the image and click on the image. You can also click on the text to reveal the menu and associated buttons...
and the ribbon xml is
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onl oad="OnRibbonLoad" loadImage="LoadImages">
<ribbon startFromScratch="false">
<tabs>
<tab id="elcdb" label="EDB">
<group id="elcdb" label="EDB">
<splitButton id="MySplitButton2" size="large">
<button id="Button50" label="Large Button with Menu" imageMso="LeaveReader"/>
<menu id="Menu20" itemSize="normal">
<button id="Button60" label="First"/>
<button id="Button70" label="Second"/>
<button id="Button80" label="Third"/>
</menu>
</splitButton>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
CodePudding user response:
You could do this with the getLabel
and getImage
properties. You just need to invalidate the ribbon (or just the control) once the new values have been set.
The sample ribbon xml would be this:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onl oad="CustomRibbon_onLoad">
<ribbon>
<tabs>
<tab id="myTab" label="Test" insertAfterMso="TabHome">
<group id="myGroup" label="Try This">
<splitButton id="mySplitButton" size="large" >
<button id="mainButton"
getLabel="SplitButton_getLabel"
getImage="SplitButton_getImage"
onAction="SplitButton_onAction" />
<menu id="buttonMenu" itemSize="normal">
<button id="subButton1" label="First" imageMso="Club" onAction="SubButton1_onAction"/>
<button id="subButton2" label="Second" imageMso="Diamond" onAction="SubButton2_onAction"/>
<button id="subButton3" label="Third" imageMso="Heart" onAction="SubButton3_onAction"/>
<button id="subButton4" label="Fourth" imageMso="Spade" onAction="SubButton4_onAction"/>
</menu>
</splitButton>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
And in a module the code would be:
Option Explicit
#If VBA7 Then
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
destination As Any, _
source As Any, _
ByVal length As Long)
#Else
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
destination As Any, _
source As Any, _
ByVal length As Long)
#End If
Private mRibbonUI As IRibbonUI
Private mButtonIndex As Long
Private mButtonLabels(4) As String
Private mButtonImages(4) As String
Public Sub CustomRibbon_onLoad(ribbonUI As IRibbonUI)
Set mRibbonUI = ribbonUI
wsSettings.Range("A1").value = ObjPtr(mRibbonUI)
mButtonIndex = 0
mButtonLabels(0) = "Select"
mButtonLabels(1) = "First"
mButtonLabels(2) = "Second"
mButtonLabels(3) = "Third"
mButtonLabels(4) = "Fourth"
mButtonImages(0) = "HappyFace"
mButtonImages(1) = "Club"
mButtonImages(2) = "Diamond"
mButtonImages(3) = "Heart"
mButtonImages(4) = "Spade"
End Sub
Public Sub SplitButton_getLabel(ctrl As IRibbonControl, ByRef value)
value = mButtonLabels(mButtonIndex)
End Sub
Public Sub SplitButton_getImage(ctrl As IRibbonControl, ByRef value)
value = mButtonImages(mButtonIndex)
End Sub
Public Sub SplitButton_onAction(ctrl As IRibbonControl)
ExecuteAction
End Sub
Public Sub SubButton1_onAction(ctrl As IRibbonControl)
mButtonIndex = 1
CustomRibbon.Invalidate
ExecuteAction
End Sub
Public Sub SubButton2_onAction(ctrl As IRibbonControl)
mButtonIndex = 2
CustomRibbon.Invalidate
ExecuteAction
End Sub
Public Sub SubButton3_onAction(ctrl As IRibbonControl)
mButtonIndex = 3
CustomRibbon.Invalidate
ExecuteAction
End Sub
Public Sub SubButton4_onAction(ctrl As IRibbonControl)
mButtonIndex = 4
CustomRibbon.Invalidate
ExecuteAction
End Sub
Private Sub ExecuteAction()
If mButtonIndex = 0 Then
MsgBox "You must select an item first."
Else
Debug.Print "Action index is: " & mButtonIndex
End If
End Sub
Private Property Get CustomRibbon() As IRibbonUI
#If VBA7 Then
Dim aPtr As LongPtr
#Else
Dim aPtr As Long
#End If
Dim ribUI As Object
On Error GoTo EH
If Not mRibbonUI Is Nothing Then
Set CustomRibbon = mRibbonUI
Exit Function
End If
aPtr = wsSettings.Range("A1").Value2
CopyMemory ribUI, aPtr, LenB(aPtr)
Set mRibbonUI = ribUI
Set ribUI = Nothing
Set CustomRibbon = mRibbonUI
Exit Function
EH:
End Property
It's easy to lose reference to the IRibbonUI object (once the VBA is stopped, for example, the reference is lost), so a common way of dealing with that is to store the pointer somewhere and CopyMemory
the pointer to the object if reference is lost.