I have a custom ribbon that calls vba commands. When it does, I get the error message
Microsoft Access cannot run the macro or callback function [FUNCTION OR SUB]
where [FUNCTION OR SUB] is the Function or Sub name I am calling.
An example of the code that generates the above message is:
Public Function OnButtonPress2(ctl As IRibbonControl)
MsgBox "OnButtonPress2"
End Function
Where OnButtonPress2 is called in the ribbon's XML file for a button as
onAction="OnButtonPress2
The above works for a macro, so it's likely not to be the ribbon's xml
======== VERSION 2
I've created a new simple test db with the XML and module shown below:
The results of each are:
Macro1 - works fine (it has a popup message)
CommandOnAction2 - msgbox "There was an error compiling this function"
OnButtonPress2 - msgbox "Microsoft Access cannot run the macro or callback function 'OnButtonPress2 '
on the line "Public gobjRibn As IRibbonUI", IRibbonUI does not autocomplete leading me to believe there is a reference that needs to be added
XML (Ribbon name is NewRibbon and is set to open on startup)
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon startFromScratch="false">
<tabs>
<tab id="tab1" label="Your Custom Tab">
<group id="group1" label="Your Custom Group">
<button id="SampleButton" label="Macro1" onAction="Macro1" />
<button id="SampleButton2" label="CommandOnAction2" onAction="=CommandOnAction2()" />
<button id="SampleButton3" label="OnButtonPress2" onAction="OnButtonPress2" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
module (name is modRibbon)
Option Compare Database
Public gobjRibn As IRibbonUI
Public Function CommandOnAction2(ByVal ctl As IRibbonControl)
MsgBox "Function CommandOnAction2"
End Function
Public Sub OnButtonPress2(ByVal ctl As IRibbonControl)
MsgBox " Sub OnButtonPress2"
End Sub
I was able to recreate your issue in Access 2010 and I got your example to work by changing the Function to a Sub that looks like this:
Public Sub OnButtonPress2(ByVal control_ As IRibbonControl)
MsgBox "OnButtonPress2"
End Sub
BTW, while searching for information on this issue I stumbled upon a posting in another forum here. In it, Albert D. Kallal discusses using onAction="=MyPublicFunctionName()" as an alternative to callbacks; perhaps you may find that useful in some cases.
Related
I have a custom ribbon, I'd like to make one of the buttons dynamic based on values in a table
so, when the form is closed, I'd like for the ribbon button and label to change based on changes made in the form (if there were any)
Here's my VBA code just for that button.
And I created 2 separate buttons with different images and labels in the USysRibbonImages table. I have mine setup a tad different, that table also stores button names, macro names and labels, besides the images. I did it that way since each image record can only be tied to one button anyway, so, figured I might as well make my images into buttons in the same table. And this process is automated somewhat. I'm explaining in case someone wonders why the code is a little different from what you usually see for ribbons.
But it works fine for static ribbons, it's once I want to make them a little dynamic is where I have issues
Public Sub fnGetRibbonImages(control As IRibbonControl, ByRef image)
Dim attach As Attachment
DoCmd.OpenForm "fZRibbonImages", acNormal, , , , acHidden
If control.ID = "btnServicesShippingWeightBucketsCollections" Or control.ID = "btnServicesShippingWeightBucketsCollectionsRed" Then
'regular
If dCount("MissingServicesShippingWeightBucketsID", "MissingServicesShippingWeightBuckets") > 0 Then
Forms("fZRibbonImages").Filter = "([USysRibbonImages].[ButtonName]='btnServicesShippingWeightBucketsCollections')"
Else
'red
Forms("fZRibbonImages").Filter = "([USysRibbonImages].[ButtonName]='btnServicesShippingWeightBucketsCollectionsRed')"
End If
Forms("fZRibbonImages").FilterOn = True
Set attach = Forms("fZRibbonImages").Controls.Item("Images")
Set image = attach.PictureDisp()
End If
DoCmd.Close acForm, "fZRibbonImages", acSaveYes
End Sub
Public Sub GetRibbonLabel(ByVal control As Office.IRibbonControl, ByRef returnedVal)
If control.ID = "btnServicesShippingWeightBucketsCollections" Or control.ID = "btnServicesShippingWeightBucketsCollectionsRed" Then
If dCount("MissingServicesShippingWeightBucketsID", "MissingServicesShippingWeightBuckets") > 0 Then
returnedVal = "FIX!!!!!"
Else
returnedVal = "Buckets"
End If
End If
End Sub
And here's my ribbon xml (I removed a bunch of buttons, the code is the same for all)
<?xml version="1.0" encoding="utf-8"?>
<customUI
xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoadRibbon" loadImage="OnLoadImage">
<ribbon startFromScratch="true">
<tabs>
<tab id="tabDefault" label="App Options">
<group id="grpMappings" label="Mappings">
<button id="btnZoneXWalk" label="Zone Xwalk" onAction="mZoneXWalk" getImage="fnGetRibbonImages" size="large"/>
<button id="btnServicesShippingWeightBucketsCollections" label="Shipping Weight Buckets" onAction="mServicesShippingWeightBucketsCollections" getImage="fnGetRibbonImages" size="large"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
I tried putting this with the CLOSE button for the form
sbRefreshRibbon
MyRibbon.Invalidate
And here's the code for the sbRefreshRibbon sub
Public Sub sbRefreshRibbon()
On Error GoTo RestartApp
MyRibbon.Invalidate
On Error GoTo 0
Exit Sub
RestartApp:
MsgBox "Please restart Application for Ribbon changes to take effect", _
vbCritical, "Ribbon Refresh Failed"
End Sub
No luck, except after a few changes (don't remember exactly what I did, I've been at this for a while) I got an error that said to restart to reload the ribbon
Instead of the label attribute for the ribbon controls you need to use the getLabel one with a callback which should have the following signature:
C#: string GetLabel(IRibbonControl control)
VBA: Sub GetLabel(control As IRibbonControl, ByRef label)
C++: HRESULT GetLabel([in] IRibbonControl *pControl, [out, retval] BSTR *pbstrLabel)
Visual Basic: Function GetLabel(control As IRibbonControl) As String
So, in your ribbon XML file you need to replace the label attribute:
<group id="grpMappings" label="Mappings">
<button id="btnZoneXWalk" label="Zone Xwalk" onAction="mZoneXWalk" getImage="fnGetRibbonImages" size="large"/>
<button id="btnServicesShippingWeightBucketsCollections" getLabel="GetRibbonLabel" onAction="mServicesShippingWeightBucketsCollections" getImage="fnGetRibbonImages" size="large"/>
</group>
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)
I have written several custom UI Ribbon menus for both Excel and Word. I have working custom context (right click) menus with my Excel apps, but cannot seem to get them to work in Word. I used the same coding as with my working menus in Excel and wrote the XML using the "Custom UI Editor for Microsoft Office" so there should be no issue.
I will post my code. The real question is will this type of custom menu work in Word at all?
The XML in the ribbon code is (showing the last lines only):
</tabs>
</ribbon>
<contextMenus>
<contextMenu idMso="ContextMenuCell">
<dynamicMenu id="mnuMain" label= "BidSmart Contract" imageMso="DocumentTemplate" getContent="GetContent" insertBeforeMso="Cut"/>
</contextMenu>
</contextMenus>
The getContent sub, "GetContent" looks like this:
Sub GetContent(control As IRibbonControl, ByRef returnedVal)
Dim xml As String
xml = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">" & _
"<button id=""mnuCtxtEmail"" label=""Send and Email"" imageMso=""TableSharePointListsRefreshList"" screentip=""Send an Email to the Client, Designer or Contractor..."" onAction=""mnuEmail""/>" & _
"<button id=""mnuCtxtEnvelope"" label=""Print an Envelope"" imageMso=""WhatIfAnalysisMenu"" screentip=""Create an Envelope addressed to the Client, Designer or Contractor..."" onAction=""mnuEnvelope""/>" & _
"</menu>"
returnedVal = xml
End Sub
If you are using the same ribbon XML you won't get your XL context menu to work in WD. You need to find the correct idMso for the context menu in WD that you want to add your menu to. You can get the control ids here: Office 2016 control ids
I have built a Word Macro-Enabled Template (.dotm) that works perfectly on my machine when I save it to my Word Startup folder. However, when I send the .dotm file to my colleagues (some on same environment as me and some not) an error is generated when pressing the custom ui ribbon button:
The macro cannot be found or has been disabled because of your security settings
The macro settings are set to run all macros, the folder containing the template >is a trusted location
.... more help options follow
My colleagues also save the file to their startup folder and load Word. The ribbon shows perfectly, but the macro does not run. My colleagues and I have done a boatload of searching online and cannot figure out the issue, as all my configurations are correct, as far as I can tell, which I will list below:
Macro Settings: Enable All Macros
Trust access to the VBA project object model is checked
Startup folder is established as a trusted location
Custom UI XML
<?xml version="1.0" encoding="utf-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon>
<tabs>
<tab id="customTab" label="WebMerge">
<group id="CustomHelp" label="Map Tags">
<button id="mapper"
visible="true"
size="large"
label="Open Mapper"
screentip="Opens Mapping Engine"
onAction="LoadMappingGuide"
imageMso="FindDialog"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
UI Ribbon Connection in Standard Module
Option Explicit
Sub LoadMappingGuide(ByVal Control As IRibbonControl) 'also tried without ByVal
'select business unit or automatically detect based on username against our database ...
Dim mappingForm As New mappingGuide
mappingForm.Show vbModal
End Sub
mappingForm (UserForm) Code
Option Explicit
Private Sub cancelButton_Click()
Unload Me
End Sub
Private Sub searchBox_Change()
generateList
Dim n As Long, index As Long
index = 0
For n = 0 To smartTagList.ListCount - 1
If InStr(1, smartTagList.List(index, 0), searchBox.Value, vbTextCompare) > 0 Then
index = index + 1
ElseIf InStr(1, smartTagList.List(index, 1), searchBox.Value, vbTextCompare) > 0 Then
index = index + 1
ElseIf InStr(1, smartTagList.List(index, 2), searchBox.Value, vbTextCompare) > 0 Then
index = index + 1
Else
smartTagList.RemoveItem (index)
End If
Next n
End Sub
Private Sub smartTagList_Click()
If smartTagList.ListIndex > -1 And smartTagList.Selected(smartTagList.ListIndex) Then
Dim smartyTag As String
smartyTag = smartTagList.List(smartTagList.ListIndex, 2)
Selection.Range.Text = smartyTag
End If
Unload Me
End Sub
Private Sub UserForm_Initialize()
generateList
End Sub
Private Sub generateList()
'replace with code to get values from database
Dim fields() As String
Dim descriptions() As String
Dim smartyTags() As String
fields = Split("Producer Name,Producer Address,Producer City,Producer State,Producer Zip,Insured Name,Insured Address,Insured City,Insured State,Insured ZIp,Risk Premium,TRIA Premium,Final Premium", ",")
descriptions = Split("Name of Producer,Address Line of Producer,City of Producer,State of Producer,Zip Code of Producer,Name of Insured,Address of Insured,City of Insured,State of Insured,ZIp of Insured,Total Premium for all risks excluding terrorism taxes and surcharges.,Premium resulting from acceptance of terrorism protection,Total Premium of quote / policy", ",")
smartyTags = Split("{$Producer Name},{$ProducerAddress},{$ProducerCity},{$ProducerState},{$ProducerZip},{$InsuredName},{$InsuredAddress},{$InsuredCity},{$InsuredState},{$InsuredZIp},{$RiskPremium},{$TRIAPremium},{$FinalPremium}", ",")
Dim i As Long
For i = LBound(fields) To UBound(fields)
With smartTagList
.AddItem
.List(i, 0) = fields(i)
.List(i, 1) = descriptions(i)
.List(i, 2) = smartyTags(i)
End With
Next
End Sub
This is what my VBE looks like with .dotm open.
The most likely reason for the error message is that the macro is not available in the template containing the Ribbon customization. Often, they remain in the Normal.dotm template on the development machine.
Background:
Something the VBA developer always needs to keep in mind when starting out on a project and testing in Word is that Word works with multiple "contexts". First, foremost and the default when recording macros and creating functionality in the private user environment is the Normal.dotm template. This is "universal": it comes into play no matter in which document you work, even if that document is attached to a different template.
THe great danger in this is that you may forget to move code meant for a specific template from Normal.dotm to that template. Or, even if you do copy the code over, if code with the same procedure names is present in both templates, that in Normal.dotm may keep precedence.
This is especially a problem when assigning macros to Ribbon and QAT buttons. Even if Normal.dotm from the developer machine is no longer present, the Ribbon or QAT assignment still looks for that and doesn't check for an alternate source.
So if you decide you want to keep the code in Normal.dotm as well as in the specific template it's a good idea to rename the procedures in Normal.dotmto avoid conflicts.
I have a peculiar problem happening, which I haven't been able to solve despite hours of googling & tinkering.
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="OnHomeRibbonLoad">
<ribbon startFromScratch="true">
<tabs>
<tab id="tab_Home" label ="Home" visible="true">
<group id="grp_Navigate" label="Navigate" visible="true">
<button id="btn_Tasks" label="Tasks" onAction="OnAction" visible="true" imageMso="ViewAllProposals" size="large" />
<toggleButton id="btn_Parties" label="Parties" onAction="OnAction" getPressed="GetPressed" visible="true" imageMso="ViewAllProposals" size="large" />
<button id="btn_Memos" label="Memos" onAction="OnAction" visible="true" imageMso="AccessTableIssues" size="large" />
</group>
</tab>
</tabs>
</ribbon>
<backstage onShow="OnBackstageShow">
<button id="btn_Login" label="Login" insertAfterMso="TabPrint" visible="true" getEnabled="GetEnabled" onAction="OnAction" isDefinitive="true"/>
<tab id="tab_Settings" label="Settings" insertAfterMso="TabPrint" visible="true" getEnabled="GetEnabled" ></tab>
<tab id="tab_Welcome" label="Welcome" insertAfterMso="TabPrint" visible="true" > </tab>
<tab idMso="TabPrint" visible="false"/>
<button idMso="ApplicationOptionsDialog" visible="false"/>
</backstage>
I have the above custom UI xml, and changed one of the buttons to be a togglebutton instead (id="btn_Parties"). All three buttons were working fine before, but after the togglebutton can't find its onAction callback function. The other two buttons trigger fine, but clicking the togglebutton results in an error saying... "Microsoft Access cannot run the macro or callback function 'OnAction'. Make sure the macro or function exists and takes the correct parameters".
Public Sub OnHomeRibbonLoad(ribbon As IRibbonUI)
Set HomeRibbon = ribbon
End Sub
Public Sub OnAction(control As IRibbonControl)
Select Case control.ID
Case "btn_Tasks"
MsgBox "Load TaskView"
Case "btn_Parties"
MsgBox "Load PartyView"
Case "btn_Memos"
MsgBox "Load MemoView"
Case Else
Debug.Print "Missing case in OnAction: " & control.ID
End Select
End Sub
Public Sub GetPressed(control As IRibbonControl, ByRef returnedVal)
Select Case control.ID
Case "tbtn_Parties"
returnedVal = IsPartiesLoaded
Case Else
returnedVal = True
Debug.Print "Missing case in GetEnabled: " & control.ID
End Select
End Sub
Is there something special about togglebutton that makes its OnAction event different to standard button?
If anyone can see where I'm going wrong, that would be much appreciated. Thanks.
The onAction callback for the toggleButton control should look like the following one:
C#: void OnAction(IRibbonControl control, bool pressed)
VBA / VB: Sub OnAction(control As IRibbonControl, pressed As Boolean)
C++: HRESULT OnAction([in] IRibbonControl *pControl, [in] VARIANT_BOOL *pvarfPressed)
Read more about the Fluent UI (aka Ribbon UI) in the following series of 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)
I am trying to make smth like this:
For now I use the imageMso attribute just for testing purposes. Even though, nothing really happens.
That's the XML for the gallery:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<ribbon>
<tabs>
<tab idMso="TabAddIns" label="Gallery">
<group id="grpGallery" label="Example Gallery">
<gallery id="galleryID" label="My Gallery" columns="3" rows="2" size="large"
itemHeight="100" itemWidth="100"
getItemID="CallbackGetItemID"
getItemCount="CallbackGetItemsCount"
getItemImage="CallbackGetItemImage"
getItemScreentip="CallbackGetItemScreentip"
getItemSupertip="CallbackGetItemSupertip">
<item id="galImg1" imageMso="PictureBrightnessGallery"/>
<item id="galImg2" imageMso="ZoomPrintPreviewExcel"/>
</gallery>
....
And the VB code to it:
Public idCounter As Integer
Public Sub New()
idCounter = 0
End Sub
Public Function GetCustomUI(ByVal ribbonID As String) As String Implements Office.IRibbonExtensibility.GetCustomUI
Return GetResourceText("GalleryTest.Gallery.xml")
End Function
#Region "Ribbon Callbacks"
Public Sub Ribbon_Load(ByVal ribbonUI As Office.IRibbonUI)
Me.galleryRibbon = ribbonUI
End Sub
Public Sub CallbackGetItemID(control As Microsoft.Office.Core.IRibbonControl, _
index As Integer, ByRef itemID As Integer)
itemID = idCounter
idCounter += 1
End Sub
Public Sub CallbackGetItemsCount(control As Microsoft.Office.Core.IRibbonControl, _
ByRef count As Integer)
count = 6
End Sub
Public Sub CallbackGetItemScreentip(control As Microsoft.Office.Core.IRibbonControl, _
Index As Integer, ByRef screentip As String)
screentip = "Screentip"
End Sub
Public Sub CallbackGetItemSupertip(control As Microsoft.Office.Core.IRibbonControl, _
Index As Integer, ByRef supertip As String)
'TO DO'
End Sub
As you see, it's nothing fancy. I am just getting into the vb.net + excel world. What happens is that I get the "My Gallery" button for the dropdown, in the excel tab, but it is empty. No elements. Not even the dummy items.
Any suggestions?
P.S. I followed this documentation
Try to return actual values in the callbacks. For example, I see non-existing IDs in the callback.
There is no need to add the CallbackGetItemsCount callback for the gallery control if you specified items explicitly.
You can read more about the Ribbon UI in the following series of articles in MSDN:
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)