Customizing the PowerPoint Ribbon at Run-Time - vba

I am developing a PowerPoint add-in and would like to temporarily disable some of the Ribbon controls while the add-in application is running.
I have developed a solution that works as expected when the Add-In is enabled, but this is not really adequate, because it disables some commonly used controls, like SlideMaster, SlideSorter, etc.
I am using PowerPoint 2010.
Here is a sample XML which is well-formed:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon startFromScratch="false">
<tabs>
<tab idMso="TabView">
<group idMso="GroupMasterViews" getVisible="GetVisible"/>
</tab>
</tabs>
</ribbon>
</customUI>
Here is a sample callback, taken from this SO answer:
Sub GetVisible(control As IRibbonControl, ByRef returnedVal As Boolean)
If TrapFlag Then
returnedVal = False ' control is hidden
Else:
returnedVal = True ' control is not hidden
End If
End Sub
When I navigate to the View ribbon, an alert informs me that:
The macro cannot be found or has been disabled because of your security settings.
Presumably this is referring to the GetVisible macro? My macro settings are:
Enable all macros (not recommended)
Trust access to the VBA project object model
I have been struggling with what I have found so far but so far unable to implement suggestions. Most answers are specific to Excel. I have not really found anything specific to PowerPoint, but figured it should not be terribly difficult to port code from one application to another, as I have done this for many other things in VBA.
I have also tried this method, but the SetCustomUI is not available in PowerPoint at the Application or Presentation level, perhaps it is unique or only applicable to Visual Studio?

After quite a bit of trial & error, I believe I have a functional solution, although there are some things I am not certain about which I will describe below.
I have tested this in a PPTM file with a subroutine to control the public TrapFlag variable, which determines whether to hide/disable certain controls. I have also tested this in a PPAM where this flag is set when the application launches, not when the Add-In is loaded.
This allows me to manipulate the RibbonUI at runtime.
Here is the XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`
<customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<commands>
<command idMso="ViewSlideSorterView" getEnabled="EnableControl"/>
<command idMso="ViewNotesPageView" getEnabled="EnableControl"/>
<command idMso="ViewSlideShowReadingView" getEnabled="EnableControl"/>
<command idMso="ViewSlideMasterView" getEnabled="EnableControl"/>
<command idMso="ViewHandoutMasterView" getEnabled="EnableControl"/>
<command idMso="ViewNotesMasterView" getEnabled="EnableControl"/>
<command idMso="WindowNew" getEnabled="EnableControl"/>
</commands>
<ribbon startFromScratch="false">
<tabs>
<tab idMso="TabView">
<group idMso="GroupMasterViews" getVisible="VisibleGroup"/>
<group idMso="GroupPresentationViews" getVisible="VisibleGroup"/>
</tab>
</tabs>
</ribbon>
Here is the VBA callbacks, generated from the CustomUI Editor application, modified as per my requirements.
Option Explicit
Public TrapFlag As Boolean
Public Rib As IRibbonUI
Public xmlID As String
Public Sub SetFlag()
Dim mbResult As Integer
mbResult = MsgBox("Do you want to disable some controls on the Ribbon?", vbYesNo)
If mbResult = vbYes Then
TrapFlag = True
Else:
TrapFlag = False
End If
End Sub
'Callback for customUI.onLoad
Sub RibbonOnLoad(ribbon As IRibbonUI)
'MsgBox "onLoad"
Set Rib = ribbon
End Sub
'I use this Callback for disabling some Controls:
' ViewSlideSorterView
' ViewNotesPageView
' ViewSlideShowReadingView
' ViewSlideMasterView
' ViewHandoutMasterView
' ViewNotesMasterView
' WindowNew
Sub EnableControl(control As IRibbonControl, ByRef returnedVal)
returnedVal = Not TrapFlag 'TrapFlag = True indicates the Application is running.
'MsgBox ("GetEnabled for " & control.Id)
'Debug.Print control.Id & " enabled = " & CStr(returnedVal)
Call RefreshRibbon(control.Id)
End Sub
'I use this Callback for disabling/hiding some tab groups:
' GroupMasterViews
' GroupPresentationViews
Sub VisibleGroup(control As IRibbonControl, ByRef returnedVal)
returnedVal = Not TrapFlag 'TrapFlag = True indicates the Application is running.
'MsgBox "GetVisible for " & control.Id
'Debug.Print control.Id & " enabled = " & CStr(returnedVal)
Call RefreshRibbon(control.Id)
End Sub
Sub RefreshRibbon(Id As String)
xmlID = Id
'MsgBox "Refreshing ribbon for " & Id, vbInformation
If Rib Is Nothing Then
MsgBox "Error, Save/Restart your Presentation"
Else
Rib.Invalidate
End If
End Sub
Some uncertainties
I am still not entirely sure what some Ron deBruin's code does (here), or whether it is necessary. I have done some testing and I do not really sure that the public variable xmlID is necessary in this case. He uses that somehow which I cannot understand.
Also, I am not able to use the same callback on the tab group as I
use on the command in the XML, so I use the tag getEnabled for
the commands, but I have to use getVisible for the groups. These
are tied to the callback functions EnableControl and
VisibleGroup, respectively. In any case, VisibleGroup seems to
disable the groups, so functionally it is the same.
I also believe that the getEnabled tag will prevent hotkey and programmatic access to those commands that I disable.

Related

How to add a macro to mutiple excel files using VBA

Is there any way to write a VBA Macro to input another VBA Macro into multiple excel workbooks? If so, how do I start?
Any and all help is greatly appreciated.
you'll need a reference first
Microsoft Visual Basic For Applications Extensibility 5.3
And here you go. Have fun
Public Sub AddNewModule()
Dim proj As VBIDE.VBProject
Dim comp As VBIDE.VBComponent
Set proj = ActiveWorkbook.VBProject
Set comp = proj.VBComponents.Add(vbext_ct_StdModule)
comp.Name = "MyNewModule"
Set codeMod = comp.CodeModule
With codeMod
lineNum = .CountOfLines + 1
.InsertLines lineNum, "Public Sub ANewSub()"
lineNum = lineNum + 1
.InsertLines lineNum, " MsgBox " & """" & "I added a module!" & """"
lineNum = lineNum + 1
.InsertLines lineNum, "End Sub"
End With
End Sub
You can also just use the workbook with the code in it as a reference as well. Then you can call the module remotely.
As #BruceWayne mentioned, there is also sotring it in the personal book.
tl;dr - there's a few options that can get you there.
I recommend storing them in the Personal.xslb file which is accessible across Excel.
See this page or this page for more detail, but generally a quick way to get started is:
Press ALT+F11 to open the VBEditor.
Right click the "VBAProject (PERSONAL.XLSB)" and Add a new module
Add your code in the module.
Now, when you go to View --> Macros, you can choose to see those stored in the Personal.xlsb file:
(I "whited out" my macros for privacy, but they'll be listed by name)
Note: If you do not have a "Personal.xlsb", then you must create it. Simply record a new macro, but choose to store it in "Personal Macro Workbook". Then you should see it in the VBEditor.
I would think the easiest way to have the same code in slightly different Excel files is to have one 'template' and save it several times as several slightly different files. Or, if you want to get fancy, you can create an AddIn to make an Excel Macro available to all workbooks.
Option Explicit
Dim cControl As CommandBarButton
Private Sub Workbook_AddinInstall()
On Error Resume Next 'Just in case
'Delete any existing menu item that may have been left.
Application.CommandBars("Worksheet Menu Bar").Controls("Super Code").Delete
'Add the new menu item and Set a CommandBarButton Variable to it
Set cControl = Application.CommandBars("Worksheet Menu Bar").Controls.Add
'Work with the Variable
With cControl
.Caption = "Super Code"
.Style = msoButtonCaption
.OnAction = "MyGreatMacro"
'Macro stored in a Standard Module
End With
On Error GoTo 0
End Sub
Private Sub Workbook_AddinUninstall()
On Error Resume Next 'In case it has already gone.
Application.CommandBars("Worksheet Menu Bar").Controls("Super Code").Delete
On Error GoTo 0
End Sub
This code will be all you need to add a single menu item (called Super Code) to the end of the existing Worksheet Menu Bar as soon as the Add-in is installed by the user via Tools>Add-ins. When the Super Code menu item is clicked a macro (that is within a standard module of the add-in) is run. As mentioned earlier, the above code MUST be placed in the Private Module of ThisWorkbook for the Add-in.
If you want the Super Code menu item added, say before the Format menu item, you could use some code like this.
Option Explicit
Dim cControl As CommandBarButton
Private Sub Workbook_AddinInstall()
Dim iContIndex As Integer
On Error Resume Next 'Just in case
'Delete any existing menu item that may have been left
Application.CommandBars("Worksheet Menu Bar").Controls("SuperCode").Delete
'Pass the Index of the "Format" menu item number to a Variable.
'Use the FindControl Method to find it's Index number. ID number _
is used in case of Customization
iContIndex = Application.CommandBars.FindControl(ID:=30006).Index
'Add the new menu item and Set a CommandBarButton Variable to it.
'Use the number passed to our Integer Variable to position it.
Set cControl = Application.CommandBars("Worksheet Menu Bar").Controls.Add(Before:=iContIndex)
'Work with the Variable
With cControl
.Caption = "Super Code"
.Style = msoButtonCaption
.OnAction = "MyGreatMacro"
'Macro stored in a Standard Module
End With
On Error GoTo 0
End Sub
There would be no need to change the Workbook_AddinUninstall() code in this case.
We have covered ID numbers while working with CommandBars etc in a P rior Newsletter Issue The link to the Microsoft site that has a BIG list of all the ID numbers for working with CommandBars can be Found Here
The above examples actually have the all the menu item code in the Workbook_AddinInstall and Workbook_AddinUnInstall Not a problem when the code is only adding one menu item. If however, you will be adding more then one and perhaps even Sub menus, you should place it in a Procedure (or 2) inside a standard Module. Then use some code as shown below
Private Sub Workbook_AddinInstall()
Run "AddMenus"
End Sub
Private Sub Workbook_AddinUninstall()
Run "DeleteMenu"
End Sub
Then in the standard module put some code perhaps like this
Sub AddMenus()
Dim cMenu1 As CommandBarControl
Dim cbMainMenuBar As CommandBar
Dim iHelpMenu As Integer
Dim cbcCutomMenu As CommandBarControl
'(1)Delete any existing one.We must use On Error Resume next _
in case it does not exist.
On Error Resume Next
Application.CommandBars("Worksheet Menu Bar").Controls("&NewMenu").Delete
'(2)Set a CommandBar variable to Worksheet menu bar
Set cbMainMenuBar = Application.CommandBars("Worksheet Menu Bar")
'(3)Return the Index number of the Help menu. We can then use _
this to place a custom menu before.
iHelpMenu = cbMainMenuBar.Controls("Help").Index
'(4)Add a Control to the "Worksheet Menu Bar" before Help
'Set a CommandBarControl variable to it
Set cbcCutomMenu = cbMainMenuBar.Controls.Add(Type:=msoControlPopup, Before:=iHelpMenu)
'(5)Give the control a caption
cbcCutomMenu.Caption = "&New Menu"
'(6)Working with our new Control, add a sub control and _
give it a Caption and tell it which macro to run (OnAction).
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "Menu 1"
.OnAction = "MyMacro1"
End With
'(6a)Add another sub control give it a Caption _
and tell it which macro to run (OnAction)
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "Menu 2"
.OnAction = "MyMacro2"
End With
'Repeat step "6a" for each menu item you want to add.
'Add another menu that will lead off to another menu
'Set a CommandBarControl variable to it
Set cbcCutomMenu = cbcCutomMenu.Controls.Add(Type:=msoControlPopup)
' Give the control a caption
cbcCutomMenu.Caption = "Next Menu"
'Add a control to the sub menu, just created above
With cbcCutomMenu.Controls.Add(Type:=msoControlButton)
.Caption = "&Charts"
.FaceId = 420
.OnAction = "MyMacro2"
End With
On Error GoTo 0
End Sub
Sub DeleteMenu()
On Error Resume Next
Application.CommandBars("Worksheet Menu Bar").Controls("&NewMenu").Delete
On Error GoTo 0
End Sub
You can find all details here.
http://www.ozgrid.com/VBA/excel-add-in-create.htm

Compile Error in Hidden Module: Module 1

I've an Addin for Excel-2010 in VBA. If I execute the code from the VBA editor it works fine. But when I execute the macro using the button in the Ribbon generated for the Addin it throws this error: Compile Error in Hidden Module: Module 1
My code:
Sub QE_eventhandler(control As IRibbonControl)
If MsgBox("Esta acción no se podrá deshacer. ¿Desea Continuar?", vbExclamation + vbOKCancel, "Confirmar -Quitar Espacios-") = vbOK Then
QuitaEspacios
End If
End
Sub QuitaEspacios()
Dim celda As Range
For Each celda In Selection
If TypeName(celda.Value) = "String" Then
celda.Value = Application.WorksheetFunction.Trim(celda.Value)
End If
Next
End Sub
The code generated with the Custom UI Editor:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon>
<tabs>
<tab id="customTab" label="GARSA Tools">
<group id="customGroup1" label="Reformateo Texto">
<button id="customButton3" label="Quitar Espacios" size="large" onAction="QE_eventhandler" imageMso="TextEffectTracking" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
You are missing an End Sub at the end of the callback - you just have End:
Sub QE_eventhandler(control As IRibbonControl)
If MsgBox("Esta acción no se podrá deshacer. ¿Desea Continuar?", vbExclamation + vbOKCancel, "Confirmar -Quitar Espacios-") = vbOK Then
QuitaEspacios
End If
End Sub
Check out the following links which describe a similar issue:
You receive a "Compile error in hidden module" error message when you start Word or Excel
compile error in Excel

Create a new Ribbon tab with vba that only appears selecting certain shapes

I want to make appear a new ribbon tab that only appears when I select a shape that I want. I know to make normal tabs using Custom UI Editor For Microsoft Office or also with VBA using the following example:
Dim oToolbar As CommandBar
Dim oButton As CommandBarButton
Dim MyToolbar As String
' Give the toolbar a name
MyToolbar = "Kewl Tools"
On Error Resume Next
' so that it doesn't stop on the next line if the toolbar's already there
' Create the toolbar; PowerPoint will error if it already exists
Set oToolbar = CommandBars.Add(Name:=MyToolbar, _
Position:=msoBarFloating, Temporary:=True)
If Err.Number <> 0 Then
' The toolbar's already there, so we have nothing to do
Exit Sub
End If
On Error GoTo ErrorHandler
' Now add a button to the new toolbar
Set oButton = oToolbar.Controls.Add(Type:=msoControlButton)
' And set some of the button's properties
With oButton
.DescriptionText = "This is my first button"
'Tooltip text when mouse if placed over button
.Caption = "Do Button1 Stuff"
'Text if Text in Icon is chosen
.OnAction = "Button1"
'Runs the Sub Button1() code when clicked
.Style = msoButtonIcon
' Button displays as icon, not text or both
.FaceId = 52
' chooses icon #52 from the available Office icons
End With
' Repeat the above for as many more buttons as you need to add
' Be sure to change the .OnAction property at least for each new button
' You can set the toolbar position and visibility here if you like
' By default, it'll be visible when created. Position will be ignored in PPT 2007 and later
oToolbar.top = 150
oToolbar.left = 150
oToolbar.Visible = True
NormalExit:
Exit Sub ' so it doesn't go on to run the errorhandler code
ErrorHandler:
'Just in case there is an error
MsgBox Err.Number & vbCrLf & Err.Description
Resume NormalExit:
But I want to make it appear and disappear in certain moments. For example in Powerpoint when you select a video, there appears 2 new tabs (FORMAT AND PLAYBACK) with options for videos. When you select another shape that is not video this tab just disappear and other tabs appears with the proper options for the shape that you select and when you don't select any shape those special tabs just disappear.
Is it possible to make it using VBA?
Yes this is possible. There are three main things you need to implement to make this occur.
Enable Events in the add-in to capture the selection of a shape. When the shape selection event fires, this will be called to determine if the shape is what you want to show your tab etc.
In the XML that defines the ribbon ensure you have a 'Visible' callback function.
VBA code for the callback function of 'Visible'.
For example
In a module named 'Ribbon'
Private theRibbon As IRibbonUI 'Holds a variable for the ribbon when loaded on startup
Private MyTag As String 'A variable to tell the ribbon to show or what Tag to hide
'Callback for the Ribbon loading from XML
Public Sub RibbonOnLoad(Ribbon As IRibbonUI)
Set theRibbon = Ribbon
MyTag = "show"
End Sub
'Get visible callback function.
Sub GetVisible(control As IRibbonControl, ByRef visible)
If MyTag = "show" Then
visible = True
Else
If control.Tag Like MyTag Then
visible = True
Else
visible = False
End If
End If
End Sub
'This is a custom sub that invalidates the ribbon as needed.
'When invalidated it has to redraw itself
Sub RefreshRibbon(Tag As String)
MyTag = Tag
If theRibbon Is Nothing Then
MsgBox "Error, Save/Restart your presentation"
Else
theRibbon.Invalidate
End If
End Sub
In a module named 'Events'
'Define the new events class
Dim cPPTEvent As New clsEvents
Sub Auto_Open()
'Enable the events when the aad-in is loaded
Set cPPTEvent.PPTEvent = Application
End Sub
Sub Auto_Close()
'Disable when it is closed
Set cPPTEvent.PPTEvent = Nothing
Set cPPTEvent = Nothing
End Sub
In a class module named 'clsEvents'. This will check the shapes in the range and if any are of the movie media type the tab will be shown on the ribbon, otherwise it'll be hidden.
Public WithEvents PPTEvent As Application
Private Sub PPTEvent_WindowSelectionChange(ByVal Sel As Selection)
Dim ppCurShape As PowerPoint.Shape
If Sel.Type = ppSelectionNone Then
RefreshRibbon ""
Exit Sub
End If
For Each ppCurShape In Sel.ShapeRange
If ppCurShape.Type = msoMedia Then
If ppCurShape.MediaType = ppMediaTypeMovie Then
RefreshRibbon "show"
Exit Sub
End If
End If
Next
RefreshRibbon ""
End Sub
And of course the ribbon XML code (taken from the first reference down the bottom)
<customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon>
<tabs>
<tab id="MyCustomTab" label="My Tab" insertAfterMso="TabHome" getVisible="GetVisible" tag="MyPersonalTab" >
<group id="customGroup1" label="Group 1">
<button id="customButton1" label="Caption 1" size="normal" onAction="Macro1" imageMso="DirectRepliesTo" />
<button id="customButton2" label="Caption 2" size="normal" onAction="Macro2" imageMso="AccountMenu" />
<button id="customButton3" label="Caption 3" size="normal" onAction="Macro3" imageMso="RegionLayoutMenu" />
</group>
<group id="customGroup2" label="Group 2">
<button id="customButton4" label="Caption 4" size="normal" onAction="Macro4" imageMso="TextAlignGallery" />
<button id="customButton5" label="Caption 5" size="normal" onAction="Macro5" imageMso="PrintPreviewClose" />
<button id="customButton6" label="Caption 6" size="normal" onAction="Macro6" imageMso="PrintPreviewShrinkOnePage" />
<separator id="MySeparator1" />
<button id="customButton7" label="Caption 7" size="large" onAction="Macro7" imageMso="ReviewPreviousComment" />
</group>
<group id="customGroup3" label="Group 3">
<menu id="MyDropdownMenu" label="My Menu" size="large" imageMso="TextAlignGallery" >
<button id="customButton8" label="Caption 8" onAction="Macro8" imageMso="TextAlignGallery" />
<button id="customButton9" label="Caption 9" onAction="Macro9" imageMso="TextAlignGallery" />
<button id="customButton10" label="Caption 10" onAction="Macro10" imageMso="TextAlignGallery" />
<button id="customButton11" label="Caption 11" onAction="Macro11" imageMso="TextAlignGallery" />
<button id="customButton12" label="Caption 12" onAction="Macro12" imageMso="TextAlignGallery" />
</menu>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
For more reading:
Hiding the tabs and controls: http://msdn.microsoft.com/en-us/library/ee390805%28v=office.11%29#odc_office2007_DisplayHideRibbonControls_ChangingVisibilityRibbonTabs
Powerpoint events (thanks Steve): http://www.pptfaq.com/FAQ00004_Make_your_VBA_code_in_PowerPoint_respond_to_events.htm
List of events you can capture: http://officeone.mvps.org/vba/events_version.html

VB Personal Workbook Error where Macro runs every time I open Excel

I have a macro that I found on the net and have been using to allow me to export all chart objects within the active workbook. It seems to work just fine when I have it in its own normal workbook.
However, I want this to be a generic function that can be used on any given workbook so I have placed this code inside of my personal excel workbook. After doing this I notice that the code now runs every time I open my excel application and I basically get a ton of windows opening that are empty ".png" files.
How can I prevent this code from running every time the application opens? Is it an excel setting or something inherently wrong with the macro code?
I have other Macros in my personal workbook that appear to be working normally (only running when selected through the Tools > VB > Macro menu) so I feel like there is something going wrong with the code. Any help would be greatly appreciated.
'the main problematic function'
Sub ExportAllPossibleCharts()
Dim i As Integer, exportCount As Integer
Dim fileNum As String, fileBase As String
Dim sheetObj As Worksheet
Dim chartObj As Chart
fileBase = ActiveWorkbook.FullName
fileBase = Replace(fileBase, ".xlsx", "")
exportCount = 0
'First, export all charts that are in their own sheets'
For Each chartObj In ActiveWorkbook.Charts
fileNum = NiceFileNumber(exportCount)
exportCount = exportCount + 1
'Do the export'
chartObj.Export fileBase & "_chart" & fileNum & ".png"
Next
'Then, export all charts that are embedded inside normal sheets'
For Each sheetObj In ActiveWorkbook.Worksheets
For i = 1 To sheetObj.ChartObjects.Count
fileNum = NiceFileNumber(exportCount)
exportCount = exportCount + 1
'Do the export'
sheetObj.ChartObjects(i).Activate
ActiveChart.Export fileBase & "_chart" & fileNum & ".png"
Next i
Next
End Sub
'small nicety to ensure two-digits for better file sorting'
Function NiceFileNumber(num As Integer) As String
If num < 10 Then
NiceFileNumber = "0" & num
Else
NiceFileNumber = num
End If
End Function
I would not place this macro in a "run everytime you open excel" location. That's just not your intend. Instead, I would save this macro in a addin and add a simple button into the Office-Ribbon.
This way, your macro should show up, but don't do anything until you are clicking the button.
Building a Button for a macro is not so complicated, it consists of the following steps:
1. Store your macro as addin (*.xlam). You will redirected to a folder in %Appdata% and thats ok. Excel stores all addins there.
1a: Beware: Don't throw away your normal xlsm file - it is difficult to edit a addin, so I usually edit the macro and simply save again.
1b: Beware: Make sure ysou delete uneccessary sheets from the macro before saving it as addin. If you forget this, your addin will become big and slowdown excel startup considerably.
2: Close all Excel instances and fire up a xml editor for office like "Custom UI Editor For Microsoft Office"
3: Insert a Office 2007 custom ui part into the file
4: Insert the following xml to the file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon>
<tabs>
<tab id="Best" label="Best Tools">
<group id="MyGroup" visible="true" label="GroupLabel">
<button id="MyMacroID" imageMso="TableIndexes" size="large"
label="Export"
onAction="DoMacro" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
This will call you macro, provided you have a "Public Sub DoMacro (obj as variant)" in your addin.
5. activate your addin in the excel-options (this is a little different four excel-versions, so you have to look up it yourself.
Additional Information:
More general Information about Ribbon-Xml can be found here:
http://gregmaxey.mvps.org/word_tip_pages/customize_ribbon_main.html
The image for the button (imageMso="TableIndexes" in xml) can be customized to a large number of buttons, download the file from microsoft to see which are available: http://www.microsoft.com/en-us/download/details.aspx?id=11675

Powerpoint VBA/Macro: Deactivate (Grey out) Button on Ribbon, if no shape is selected

I have a macro in Powerpoint that gives me Information of a Shape. To bypass the error if no shape is selected I insert an errormask. However, this is very annoying.
Is it therefore possible to grey out the button if e.g. no Shape is selected. That way the user would npot even have a chance to click it.
Custom UI XML:
http://pastebin.com/T6NQ8WF8
Assuming you are using a 2007+ version of PowerPoint, the only way to manipulate the ribbon controls, buttons, etc., is through ribbon extensibility. It is possible to do this at run-time, with a vba hook, but it is much more difficult than in previous versions of PowerPoint where you could just use VBA to manipulate the controls' .Enabled or .Visible properties.
Here is an example of using ribbon extensibility to customize the ribbon at run-time. As you can see, it is not easy. I will show this in Option 2, below.
In this case, you have an error condition that you can easily identify using the .Type property of the Selection.ShapeRange. I think that attempting to conditionally disable this button at run-time (Option 2, below) is probably more trouble than it is worth.
Update
Is there a setting that greys your all buttons that don't have an effect.
No. The macros are the "effect", even if the result of the macro is that no action is performed. What you are asking is whether there is a setting which can compile and interpret your macros, determine whether that macro performs "an action" (e.g., manipulates a shape, changes a property assignment, etc.) and then disable buttons based on this determination. There is no such setting.
OPTION 1 -- Simply Do Not Display the MsgBox; Perform No Action if Invalid Selection
I will make some edits to clean up your code and use a better method of avoiding that error:
Sub Infos()
Dim n as String
Dim w as String
Dim h as String
Dim l as String
Dim T as String
With ActiveWindow.Selection.ShapeRange
Select Case .Type
Case 0
'MsgBox ("No shape selected.")
Exit Sub
Case Else
n = .Name
w = .Width
h = .Height
l = .Left
T = .Top
MsgBox "Name: " & n & Chr$(CharCode:=13) & "Länge: " & w & _
Chr$(CharCode:=13) & "Höhe: " & h & Chr$(CharCode:=13) & _
"Linkeposition: " & l & Chr$(CharCode:=13) & "Höhenposition: " & T
End Select
End Sub
OPTION 2 -- Use an Application Event Handler and Manipulate Ribbon at Run-Time
I mentioned that this is not easy. I uploaded an example file to Google Docs Presentation1.pptm. This should get you started. You can see now how much difficult this method is. If you are creating a PPAM/Add-In file, there are further considerations and complexities you may encounter. Good luck!
There are several errors in your code.
1. Your XML is not valid when I check in Custom UI Editor. I edited it here:
http://pastebin.com/SpG0Rtqq
2. Your Infos macro contains errors. You omit the End With statement, also, your n assignment will fail (and the rest of them will produce strange result) if the selection is multiple shapes. You can fix that by:
n = IIf(.ShapeRange.Count > 1, "Multiple shapes", .ShapeRange(1).Name)
w = IIf(.ShapeRange.Count > 1, "Multiple shapes", .ShapeRange(1).Width)
h = IIf(.ShapeRange.Count > 1, "Multiple shapes", .ShapeRange(1).Height)
l = IIf(.ShapeRange.Count > 1, "Multiple shapes", .ShapeRange(1).Left)
T = IIf(.ShapeRange.Count > 1, "Multiple shapes", .ShapeRange(1).Top)
Once you have fixed those components...
Add a module called mod_EventHandler, which includes this code. This will create an application event-handler class object, cEventClass:
Option Explicit
Public cPPTObject As New cEventClass
Public TrapFlag As Boolean
Sub TrapEvents()
'Creates an instance of the application event handler
If TrapFlag = True Then
MsgBox "Relax, my friend, the EventHandler is already active.", vbInformation + vbOKOnly, "PowerPoint Event Handler Example"
Exit Sub
End If
Set cPPTObject.PPTEvent = Application
TrapFlag = True
End Sub
Sub ReleaseTrap()
If TrapFlag = True Then
Set cPPTObject.PPTEvent = Nothing
Set cPPTObject = Nothing
TrapFlag = False
End If
End Sub
Since we need this class object, add a class module to your PowerPoint file, named cEventClass. In this module, put this code below. This code forces a refresh of the ribbon. This procedure implicitly calls the EnabledBtInfo subroutine, which then tests if the current selection is Shape(s).
Option Explicit
Public WithEvents PPTEvent As Application
Private Sub PPTEvent_WindowSelectionChange(ByVal Sel As Selection)
'Force refresh of the "btInfo" button:
RefreshRibbon "btInfo"
End Sub
And finally, another standard code module with this code to control the Button's visibility/enabled. Note that EnabledBtInfo is the VBA Hook for this button, and it tests whether Selection is shapes, before refreshing the ribbon:
Option Explicit
Public Rib As IRibbonUI
Public xmlID As String
'Callback for customUI.onLoad
Sub RibbonOnLoad(ribbon As IRibbonUI)
TrapEvents 'instantiate the event handler
Set Rib = ribbon
End Sub
Sub EnabledBtInfo(control As IRibbonControl, ByRef returnedVal)
'Check the ActiveWindow.Selection.ShapeRange
returnedVal = (ActiveWindow.Selection.Type = ppSelectionShapes)
Call RefreshRibbon(control.Id)
End Sub
Sub RefreshRibbon(Id As String)
xmlID = Id
If Rib Is Nothing Then
MsgBox "Error, Save/Restart your Presentation"
Else
Rib.Invalidate
End If
End Sub
When a shape(s) is selected, the magnifying glass icon is enabled:
When shape(s) is not selected, button is disabled:
And finally, when multiple shapes are selected: