Customizing the PowerPoint Ribbon at Run-Time

2020-02-12 08:55发布

问题:

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?

回答1:

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.