How to disable (Record Macro) menu (by macro codes) - vba

In (Microsoft Excel) In (Tools > Macro) There is a menu with name of (Record Macro)
How to disable (Record Macro) menu by a macro code?

You can use a loop like this:
Dim ctl As CommandBarControl
For Each ctl In Application.CommandBars.FindControls(ID:=184)
ctl.Enabled = False
Next ctl

For Excel 2007+, you need to manipulate the ribbon. This is as good as I can come up with, the problem as Rory and I discussed in comments to his answer is that the MacroRecord command can be added to the Status Bar, and the Status Bar is not part of the ribbon model. The hack is to disable the status bar, but since that is an application-level property, you need an event handler to re-enable it for other workbooks.
Ribbon XML to disable the controls:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="ribbonOnLoad">
<commands>
<command idMso="MacroRecord"
enabled="false"
/>
</commands>
<ribbon>
<tabs>
<tab>
<group idMso="GroupMacros">
<button idMso="MacroRecord"
enabled="false"
/>
</group>
<group idMso="GroupCode">
<button idMso="MacroRecord"
enabled="false"
/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
VBA in a standard module to handle the ribbon callbacks and associated procedures:
Option Explicit
Option Private Module
Public XLEvents As New cEventClass
'Callback for customUI.onLoad
Sub ribbonOnLoad(ribbon As IRibbonUI)
Call SetEventHandler
Call ToggleStatusBar(ThisWorkbook)
End Sub
Sub SetEventHandler()
If XLEvents.appevent Is Nothing Then
Set XLEvents.appevent = Application
End If
End Sub
Sub ToggleStatusBar(wb As Workbook, Optional ret$)
If (wb.Name = ThisWorkbook.Name) Then
Application.StatusBar = "Status Bar Disabled"
Else
Application.StatusBar = False
End If
End Sub
VBA in a Class module called "cEventClass" to do application event-handling:
Option Explicit
Public WithEvents appevent As Application
Dim ret As String
Private Sub appevent_WorkbookActivate(ByVal wb As Workbook)
Call ToggleStatusBar(wb, ret)
End Sub

Related

Button activated on ribbon. After click, it is deactivated for two seconds and is activated again

In the xml I have a button for the ribbon:
<button id="PRUEBA" label="Boton Prueba" size="normal" onAction="Macro11" imageMso="DirectRepliesTo" tag="EtiquetaG" getEnabled="Habilitado"/>
For vba:
I would like this button to appear enabled to work on the ribbon, but once pressed, do the macro for which it is defined, then the button is disabled for two seconds and is enabled again.
Thanks.
In the ribbon XML you use the getEnabled callback which returns the control's state as a boolean value:
getEnabled="Habilitado"
You need to make sure it always returns a valid result depending on which your control can be disabled and enabled dynamically at runtime.
I dont speak english, sorry. I use a translator. Well, What I intend is that when pressing the button on the ribbon, it is disabled a couple of seconds after executing the macro. The macro does nothing, it just inserts a content block of Word. Although the example I give is a test in an excel sheet, it would be for a word document. It is related to the post: Enable/Disable Ribbon Controls Independently But I would only like it to work with a button and put a timer. I do not know how to do it.
Option Explicit
Dim Rib As IRibbonUI
Public MyTag As String
'Public EnableButton1 As Boolean
Sub Habilitado(control As IRibbonControl, ByRef Enabled)
If MyTag = "Enable" Then
Enabled = True
Else
If control.Tag Like MyTag Then
Enabled = True
Else
Enabled = False
End If
End If
End Sub
Sub EnableControlsWithCertainTag3()
'Habilita solo el control con la etiqueta Tag "SoloUno"
Call RefrescarCinta(Tag:="SoloUno")
End Sub
Sub EnabledAllControls()
'Habilita todos
Call RefrescarCinta(Tag:="*")
End Sub
Sub DisableAllControls()
'Deshabilita todos
Call RefrescarCinta(Tag:="")
End Sub
Sub RefrescarCinta(Tag As String)
MyTag = Tag
If Rib Is Nothing Then
MsgBox "Cierra y vuelve a abrir" & vbNewLine & _
"Visit this page for a solution:
http://www.rondebruin.nl/ribbonstate.htm"
Else
Rib.Invalidate
End If
End Sub
Sub RibbonOnLoad(ribbon As IRibbonUI)
Set Rib = ribbon
End Sub
Lines on XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI onLoad="RibbonOnLoad"
xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<!-- Add two custom groups on the Home Tab with a few buttons -->
<ribbon>
<tabs>
<tab idMso="TabHome" >
<group id="MyCustomGroup1" label="Group1"
tag="MyPersonalGroup1" >
<button id="G1B1" label="Caption 1" size="normal"
onAction="Macro1" imageMso="DirectRepliesTo"
tag="SoloUno" getEnabled="Habilitado"/>
<button id="PRUEBA" label="Boton Prueba" size="normal"
onAction="Macro11" imageMso="DirectRepliesTo"
tag="EtiquetaG" getEnabled="Habilitado"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Macros:
Sub Macro1(control As IRibbonControl)
MsgBox "This is Macro 1 in group 1"
End Sub
Sub Macro11(control As IRibbonControl)
Application.OnTime Now + TimeValue("00:00:02"), "Macro12"
End Sub
Sub Macro12()
MsgBox "El mensaje funciona; intenta incluir en el modulo macro del
mensaje, un timer, a ver que pasa"
End Sub

Microsoft Visio VBA CustomUI Ribbon help - Changing Default Value of EditBox

I have a customUI with an EditBox and want to set the default value when the User opens the Visio template file.
Here is the customUI xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="CustomRibbonOnLoad">
<ribbon>
<tabs>
<tab id="CustomTab1" label="My Tab">
<group
id="BOMGroup"
label="BOM"
autoScale="true"
>
<editBox id="BOMLink" label="BOM Link:" onChange="ThisDocument.BOMLink_onChange" getText = "ThisDocument.BOM_getText"/>
<button
id="GoToBOMLink"
label="Go To BOM Link"
screentip="Opens Browser to go to BOM Link"
size="large"
imageMso="HyperlinkOpenExcel"
onAction="ThisDocument.OpenBOM"
/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
So I expect BOMLink to have its default text updated through its getText callback function.
Here is my vba code:
Dim MyRibbon As IRibbonUI
Sub CustomRibbonOnLoad(ribbon As IRibbonUI)
Set MyRibbon = ribbon
Debug.Print "CustomRibbonOnLoad called"
MyRibbon.InvalidateControl ("BOMLink") ' Invalidates the cache of a single control
End Sub
Sub myFunction()
MyRibbon.InvalidateControl ("BOMLink") ' Invalidates the cache of a single control
End Sub
'Callback for BOMLink onChange
Sub BOMLink_onChange(control As IRibbonControl, text As String)
End Sub
'Callback for BOMLink getText
Sub BOM_getText(control As IRibbonControl, ByRef returnedVal)
Debug.Print "Callback UpdateBOMLinkText"
MyRibbon.InvalidateControl ("BOMLink") ' Invalidates the cache of a single control
returnedVal = "test default text"
End Sub
'Callback for GoToBOMLink onAction
Sub OpenBOM(control As IRibbonControl)
Debug.Print "Callback OpenBOM"
End Sub
As it stands right now, I do not get the "CustomRibbonOnLoad called" debug print, but I do get the "Callback UpdateBOMLinkText" called. However the text does not change to the default test text as expected.
When I open the document and click the Custom Tab I get this in the debug:
>invokeVBA: ThisDocument.BOM_getText
Callback UpdateBOMLinkText
<invokeVBA: 0x80020009
I am attempting to fill it with "test default text" for now, but in the future want to call upon the Shapesheet and pull data from there to fill by default.
Any help would be appreciated, thanks!
You need to remove this line because it overwrites the value you set in the line before that.
'Callback for BOMLink getText
Sub BOM_getText(control As IRibbonControl, ByRef returnedVal)
Debug.Print "Callback UpdateBOMLinkText"
' MyRibbon.InvalidateControl ("BOMLink") ' <<<< remove this line
returnedVal = "test default text"
End Sub

Ribbon.InvalidateControl not working on PPT 2010

I have a an add-in within the Insert Ribbon of PPT 2010. Basically what I want to do is to make a selection in a combo box, then run a macro, then set the combo box to blank. Unfortunately I´v been unable to clear the selection.
I have searched all over the web with various methods and nothing helps. Hope someone can help.
My XML
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="VirtusInitialize">
<ribbon>
<tabs>
<tab idMso="TabInsert">
<group id="CustomGroup1" label="Sticker">
<box id="box1" boxStyle="horizontal">
<comboBox id="ddlItem" label="STICKER 1" onChange="Sticker1">
<item id="BU" label="BACK UP" />
</comboBox>
</box>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
VBA code:
Private MyRibbonV As IRibbonUI
Public Sub VirtusInitialize(ByVal ribbon As Office.IRibbonUI)
Set MyRibbonV = ribbon
End Sub
Sub Sticker1(ByVal control As IRibbonControl, text As String)
'do stuff
MyRibbonV.InvalidateControl ("ddlItem")
End Sub
Alternatively use a Dropdown control instead of combobox
I found some information here, at least enough to get started...
http://gregmaxey.mvps.org/word_tip_pages/customize_ribbon_main.html
Use XML like:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="VirtusInitialize">
<ribbon>
<tabs>
<tab idMso="TabInsert">
<group id="CustomGroup1" label="Sticker">
<box id="box1" boxStyle="horizontal">
<dropDown id="ddlItem" label="STICKER 1"
getItemCount="GetItemCount"
getItemLabel="GetItemLabel"
getSelectedItemIndex="GetSelectedItemIndex"
onAction="Sticker1" >
</dropDown>
</box>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
And VBA callbacks:
Private MyRibbonV As IRibbonUI
Public Sub VirtusInitialize(ByVal ribbon As Office.IRibbonUI)
Set MyRibbonV = ribbon
End Sub
Sub Sticker1(ByVal control As IRibbonControl, id As String, index As Integer)
'do stuff
Select Case index
Case 0
Case 1
'do stuff
Case 2
'do stuff
End Select
MyRibbonV.Invalidate
End Sub
'Callback for ddlItem getItemCount
Sub GetItemCount(control As IRibbonControl, ByRef returnedVal)
'puts 3 items in the dropdown and triggers the GetItemLabel
returnedVal = 3
End Sub
'Callback for ddlItem getItemLabel
Sub GetItemLabel(control As IRibbonControl, index As Integer, ByRef returnedVal)
Dim str$
Select Case index
Case 0
str = " "
Case 1
str = "BACK UP"
Case 2
str = "GO FORWARD"
End Select
returnedVal = str
End Sub
'Callback for ddlItem getSelectedItemIndex
Sub GetSelectedItemIndex(control As IRibbonControl, ByRef returnedVal)
'not used
End Sub
'Callback for ddlItem getText
Sub GetText(control As IRibbonControl, ByRef returnedVal)
'not used
End Sub
'Callback for ddlItem getItemID
Sub GetItemID(control As IRibbonControl, index As Integer, ByRef id)
'not used...
End Sub
try this:
Sub Sticker1(ByVal control As IRibbonControl, text As String)
'do stuff
Call MyRibbonV.InvalidateControl ("ddlItem")
End Sub
Notice the keyword "Call" added to the original code.

Microsoft Word: How can I listen to a document-level event (protect/unprotect) to update a ribbon toggle button?

The PHB wants me to create a ribbon toggle button for Microsoft Word that:
when pressed, restricts editing to filling in forms and protects the document without a password.
when unpressed, unprotects the document (without a password).
I have the following customUI.xml:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="RibbonOnLoad">
<commands>
<command idMso="ProtectOrUnprotectDocument" onAction="ProtectOrUnprotectDocumentOnAction"/>
</commands>
<ribbon>
<tabs>
<tab id="LawTab" label="Law">
<group id="ProtectGroup" label="Protect">
<toggleButton id="ToggleProtectionButton" imageMso="GreenBall" label="Protection" getPressed="ToggleProtectionButtonGetPressed" onAction="ToggleProtectionButtonOnAction"/>
<button id="InvalidateRibbonButton" imageMso="Refresh" label="Invalidate Ribbon" onAction="InvalidateRibbonButtonOnAction"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
and the following VBA code:
Private ribbon As IRibbonUI
Sub InvalidateRibbonButtonOnAction(control As IRibbonControl)
ribbon.Invalidate
End Sub
Sub ProtectOrUnprotectDocumentOnAction(control As IRibbonControl, ByRef cancelDefault)
ribbon.Invalidate
cancelDefault = False
End Sub
Sub RibbonOnLoad(ActiveRibbon As IRibbonUI)
Set ribbon = ActiveRibbon
End Sub
Sub ToggleProtectionButtonGetPressed(control As IRibbonControl, ByRef returnValue)
returnValue = ActiveDocument.ProtectionType <> wdNoProtection
End Sub
Sub ToggleProtectionButtonOnAction(control As IRibbonControl, ByVal pressed As Boolean)
If pressed Then
ActiveDocument.Protect wdAllowOnlyFormFields
Else
ActiveDocument.Unprotect
End If
End Sub
I can repurpose the built-in ProtectOrUnprotect command so the corresponding built-in button invalidates the ribbon and consequently updates my custom button, but if I protect/unprotect with the built-in task pane (Review > Restrict Editing) or programmatically (ActiveDocument.Protect/Unprotect), my custom button stays ignorant of the change. How can I listen to the document-level event to protect/unprotect so I can update the state of my toggle button?
How about forcing the built-in button to call your code?
To summarize, you can override the built-in Sheet Protect button to use your code (which hopefully causes your button to toggle) by adding the following to your ribbon XML (see the link for details):
<commands>
<command idMso="ProtectOrUnprotectDocument" onAction="ToggleProtectionButtonOnAction"/>
</commands>
I think Blackhawk's answer is on the right track: override the built-in command's onAction procedure. However, his XML example is for MS Excel and will not work in MS Word. This can be tweaked pretty easily, but unfortunately, I can't figure out how to solve this particular problem:
but if I use the built-in ProtectOrUnprotectDocument button to protect/unprotect, my custom button stays ignorant of the change. How can I listen to the document-level event to protect/unprotect so I can update the state of my toggle button?
There is no document-level event, even using a WithEvents application class, which responds to a change in the document's ProtectionType (in theory you should probably be able to use the ribbon .InvalidateControl method).
So the question (and possible resolution) is why you need a toggle button, when you can simply use the built-in button and hijack it's functionality with your own procedure to protect/unprotect as needed. You can even place the built-in button within your custom menu.
However, this looks promising:
http://sourcedaddy.com/ms-excel/getting-and-changing-control-values.html
REVISED
After some discussion and trial and error (you did a LOT of this on your own, and figured out what I could not), let's try this which is working for me.
Here is XML (you may need to modify the schemas if you're using older version).
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<commands>
<command idMso="ProtectOrUnprotectDocument" onAction="ToggleProtectionButtonOnAction" />
</commands>
<ribbon>
<tabs>
<tab id="LawTab" label="Law">
<group id="ProtectGroup" label="Protect">
<toggleButton id="ToggleProtectionButton" imageMso="GreenBall" label="Protection" getPressed="ToggleProtectionButtonGetPressed" onAction="ToggleProtectionButtonOnAction"/>
<button id="InvalidateButton" imageMso="Refresh" label="Invalidate" onAction="InvalidateButtonOnAction"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
What I did is to send both the ProtectOrUnprotectDocument command and your custom toggle button, ToggleProtectionButton, to the same onAction handler. Then I use some logic (which seems to be working) to selectively invalidate the ribbon, while a boolean variable captures the document's protection state, throughout the procedures and callbacks.
Here is VBA:
Option Explicit
Dim ribbon As IRibbonUI
Dim protectButton As IRibbonControl
Dim IsProtected As Boolean
Sub InvalidateRibbonButtonOnAction(control As IRibbonControl)
'MsgBox "Invalidate"
ribbon.Invalidate
End Sub
Sub RibbonOnLoad(ActiveRibbon As IRibbonUI)
'MsgBox "onLoad"
Set ribbon = ActiveRibbon
IsThisDocumentProtected
End Sub
Sub ToggleProtectionButtonGetPressed(control As IRibbonControl, ByRef returnValue)
'MsgBox "GetPressed"
IsThisDocumentProtected
returnValue = IsProtected
End Sub
Sub ToggleProtectionButtonOnAction(control As IRibbonControl, ByVal pressed As Boolean)
IsThisDocumentProtected
If pressed Then
If Not IsProtected Then
ActiveDocument.Protect wdAllowOnlyFormFields
Else
ActiveDocument.Unprotect
End If
Else
If IsProtected Then
ActiveDocument.Unprotect
End If
End If
If control.Id = "ProtectOrUnprotectDocument" Then
' MsgBox "Got here!"
' This will force ribbon invalidate only for the native command
ProtectOrUnprotectDocumentOnAction control, False
End If
End Sub
''''' This procedure is NOT a callback, but is called from the callbacks:
Private Sub IsThisDocumentProtected()
'''' Assigns value to module-level boolean variable for use throughout the module
IsProtected = ActiveDocument.ProtectionType <> wdNoProtection
End Sub
''''' This is NOT a callback, either, but is invoked only for particular control press:
Private Sub ProtectOrUnprotectDocumentOnAction(control As IRibbonControl, ByRef cancelDefault)
ribbon.Invalidate
cancelDefault = False
End Sub
Limitations
if I protect/unprotect with the built-in task pane (Review > Restrict Editing) or programmatically (ActiveDocument.Protect/Unprotect), my custom button stays ignorant of the change.
This will not work for protection that is applied programmatically. As for the Review > Restrict Editing, I think you simply need to hijack that command's onAction procedure in the same way we have done, above, by adding another command to the XML and referring it to the same onAction procedure.

How to set a text on a Editbox Ribbon via VBA (Excel)

How can I set a text in a Ribbon Editbox? I can't find it on internet :/
I just can find examples of click event but nothing about set a text from a Sub.
So for example, I want something like this:
Sub settingText()
editboxname = "my text"
end sub
The solution I found on this link: http://www.shulerent.com/2011/08/16/changing-the-value-of-an-editbox-office-ribbon-control-at-runtime/
Here is an example that I tested and it worked well:
'Global Variables:
Public MyRibbonUI As IRibbonUI
Public GBLtxtCurrentDate As String
Private Sub OnRibbonLoad(ribbonUI As IRibbonUI)
Set MyRibbonUI = ribbonUI
GBLtxtCurrentDate = ""
End Sub
Private Sub ocCurrentDate(control As IRibbonControl, ByRef text)
GBLtxtCurrentDate = text
MyRibbonUI.InvalidateControl (control.id)
End Sub
Private Sub onGetEbCurrentDate(control As IRibbonControl, ByRef text)
text = GBLtxtCurrentDate
End Sub
Public Sub MyTest()
'Here is an example which you are setting a text to the editbox
'When you call InvalidateControl it is going to refresh the editbox, when it happen the onGetEbCurrentDate (which is the Gettext) will be called and the text will be atributed.
GBLtxtCurrentDate = "09/09/2013"
MyRibbonUI.InvalidateControl ("ebCurrentDate")
End Sub
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="OnRibbonLoad">
<ribbon>
<tabs>
<tab id="Objects" label="Objects">
<group id="grp" label="My Group">
<editBox id="ebCurrentDate" label="Date" onChange="ocCurrentDate" getText="onGetEbCurrentDate"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
It's a little while since this answer was posted, and there looks to be a recent-ish change to the behaviour of the ribbon, which means the original answer posted may not be a solution any more. For the record, I'm using Excel 2013 with some updates that are dated after Braulio's answer.
The heart of the difference is that Invalidate and InvalidateControl on the ribbon don't behave the same way as previously. This means that InvalidateControl does not call the getText callback on the editBox. I replaced the InvalidateControl calls with Invalidate (so forces a re-draw on the entire ribbon), and that does trigger the callback as expected.
So here's the code of my solution for a filename/browse button (note I've included extra code for caching the ribbon UI reference on a very hidden sheet so that resets during development don't make the ribbon inaccessible).
Private sobjRibbon As IRibbonUI
Private strFilename As String
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (destination As Any, source As Any, ByVal length As Long)
Private Function GetRibbon() As IRibbonUI
If sobjRibbon Is Nothing Then
Dim objRibbon As Object
CopyMemory objRibbon, ThisWorkbook.Worksheets("Ribbon_HACK").Range("A1").Value, 4
Set sobjRibbon = objRibbon
End If
Set GetRibbon = sobjRibbon
End Function
'Callback for customUI.onLoad
Sub Ribbon_Load(ribbon As IRibbonUI)
Set sobjRibbon = ribbon
Dim lngRibPtr As Long
lngRibPtr = ObjPtr(ribbon)
' Write pointer to worksheet for safe keeping
ThisWorkbook.Worksheets("Ribbon_HACK").Range("A1").Value = lngRibPtr
strFilename = ""
End Sub
'Callback for FileName onChange
Sub OnChangeFilename(control As IRibbonControl, text As String)
strFilename = text
End Sub
'Callback for FileName getText
Sub GetFileNameText(control As IRibbonControl, ByRef returnedVal)
returnedVal = strFilename
End Sub
'Callback for FilenameBrowse onAction (I'm looking for XML files here)
Sub OnClickFilenameBrowse(control As IRibbonControl)
Dim objFileDialog As Office.FileDialog
Set objFileDialog = Application.FileDialog(msoFileDialogFilePicker)
With objFileDialog
.AllowMultiSelect = False
.Title = "Please select the file."
.Filters.Clear
.Filters.Add "XML", "*.xml"
If .Show = True Then
strFilename = .SelectedItems(1)
GetRibbon().Invalidate ' Note the change here, invalidating the entire ribbon not just the individual control
End If
End With
End Sub
For the record, here's the XML for the two objects I'm dealing with here:
<editBox id="FileName" onChange="OnChangeFilename" screentip="Filename of the XML file to upload" label="XML file name" showImage="false" getText="GetFileNameText" />
<button id="FilenameBrowse" imageMso="ImportExcel" onAction="OnClickFilenameBrowse" screentip="Find the file to upload" label="Browse" />