VBA - 如何暂时禁用 Worksheet.Activate 事件?

VBA - How to temporarily disable the Worksheet.Activate Event?

我正在处理一个使用 SAP 的 BusinessObjects Analysis 插件的 Excel 工作簿。每当工作簿打开时,都会触发一个变量 selection 提示,并允许用户输入值,然后将这些值用于 select 来自后端的数据,并以 table 格式显示在Excel(Excel 中 SAP BW 查询的表示)。我面临的问题如下...... 每当提示 selection 完成并且正在处理 selection 时,如果您垃圾邮件单击工作表选项卡,则会触发 Worksheet.Activate 事件。问题是 Worksheet.Activate 处理程序试图使本身尚未设置的功能区对象无效。我想防止这种情况发生,并且我希望功能区仅在设置后才失效。问题是,当工作表更改时,功能区必须无效,以便它在功能区中加载每个工作表唯一的特定按钮。在激活工作表时不失效不是一种选择。最好在我们的自定义功能区加载之前消除更改工作表的可能性。

这是我尝试过但没有成功的方法:

  1. 我尝试在提示出现后立即禁用 Application.Interactive = False 的交互模式,并在执行 AOCust_OnLoad(ribbon As IRibbonUI) 功能区 onLoad 回调时重新启用它,并且色带已设置。

  2. 我尝试在出现提示后立即禁用所有使用 Application.EnableEvents = False 的事件,并在执行功能区 onLoad 回调并设置功能区时重新启用它们。

  3. 我尝试将以下内容添加到 Worksheet.Activate 回调中,但它永远不会退出无限循环,这可能是预期的,因为功能区 onLoad 不是系统事件,因此永远不会触发。

    while ribbon is Nothing
      'waiting for ribbon onload to fire and set the value
      DoEvents
    wend
    

第二种情况令人惊讶的是 Worksheet.Activate 事件是如何触发的,据我所知,这些事件是禁用的。

您是否知道如何解决这个问题,以便用户在设置功能区之前无法更改工作表?

如果您希望我在此说明中添加代码片段,请告诉我。

谢谢

编辑 - 添加代码

在 Microsoft Excel ThisWorkbook 中的对象

Private Sub Workbook_SheetActivate(ByVal Sh As Object)
  If EnableEvents = True Then
      Debug.Print "invalidate"
      Call AOCust_Callbacks.AOCust_InvalidateRibbon
  End If
End Sub

在模块中 "Custom_Ribbon"

'Callback for Selections data onAction
 Sub PROMPT(control As IRibbonControl)
   EnableEvents = False
   'more prompt-related code
 End Sub

在模块“AOCust_Callbacks”中

Public EnableEvents As Boolean

'Callback for Ribbon OnLoad
Sub AOCust_OnLoad(ribbon As IRibbonUI)
  Debug.Print "setting the ribbon"
  'Start Highlighting for Workbook
  StartHighlighting
  Set AOCustRibbon = ribbon
  Debug.Print "the ribbon is now set"
  EnableEvents = True
End Sub

当提示 selection 完成并且 selection 仍在处理中时,当我垃圾邮件单击工作表选项卡时,我不再遇到变量未设置的问题由于 if 语句,我尝试使功能区无效,但问题是没有执行 onload 功能区功能。

这可能不是最佳解决方案,但可以解决。

您可以使用全局变量来处理它。

在模块中放置如下内容

Public EventsEnabled As Boolean
Public Sub ModuleWithDisabledEvents()
    EventsEnabled = False
    Debug.Print EventsEnabled
    EventsEnabled = True
    Debug.Print EventsEnabled
End Sub

然后在您的工作表激活中将您的 Activate 代码包装在查看此变量的 if 块中

Private Sub Worksheet_Activate()
    If Not EventsEnabled Then
        ' Do activate code here
    End If
End Sub

通过更改 EventsEnabled 变量,您可以让代码忽略 Activate 过程,即使事件已启用。


更新: 你用丝带做什么?这就是我过去实现自定义功能区的方式,但没有遇到你的问题:

在我的 XML 文件中,我有以下内容:

<customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
    <ribbon startFromScratch="false">
        <tabs>
            <tab id="customTab" label="Custom Tab">
                <group id="customGroup" label="Custom Group">
                    <button id="customButton" label="Custom Button" imageMso="HappyFace" size="large" onAction="Callback" getVisible="GetVisible" />
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

然后在我的文件中有一个名为 'Ribbon' 的模块,其中包含:

Option Explicit
Option Private Module
Dim rib As IRibbonUI

Private Declare Function ShellExecute _
                         Lib "shell32.dll" Alias "ShellExecuteA" ( _
                         ByVal hWnd As Long, _
                         ByVal Operation As String, _
                         ByVal Filename As String, _
                         Optional ByVal Parameters As String, _
                         Optional ByVal Directory As String, _
                         Optional ByVal WindowStyle As Long = vbMinimizedFocus _
                         ) As Long

#If VBA7 Then
    Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
#Else
    Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
#End If
#If VBA7 Then
Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
#Else
Function GetRibbon(ByVal lRibbonPointer As Long) As Object
#End If
Dim objRibbon As Object
CopyMemory objRibbon, lRibbonPointer, LenB(lRibbonPointer)
Set GetRibbon = objRibbon
Set objRibbon = Nothing
End Function
Public Sub RibbonOnLoad(ribbon As IRibbonUI)
    Set rib = ribbon
    Debug.Print "ribbon:-", ObjPtr(ribbon)
    Sheet2.Cells(1, 1).Value = ObjPtr(ribbon)
End Sub
Public Sub RefreshRibbon()
    On Error GoTo RibbonError
    If rib Is Nothing Then
        Set rib = GetRibbon(Sheet2.Cells(1, 1).Value)
        If rib Is Nothing Then GoTo RibbonError
    Else
        rib.Invalidate
        Exit Sub
    End If
    Exit Sub
RibbonError:
    Debug.Print "There is an issue with the menu bar. Please restart the tool"
End Sub

Public Sub GetVisible(control As IRibbonControl, ByRef visible)
    visible = True
End Sub

加载功能区时,它会将其内存值设置为 Sheet2,然后在刷新功能区时使用该值重新加载 运行 RefreshRibbon

通过在加载选择提示之前或第一次使用 Application.interactive = False 禁用鼠标事件并在加载功能区后重新启用它们,问题已解决。问题是 SAP 决定对名为 onBeforeFirstPromptsDisplay 的第一个提示使用不同的回调。结果,我对工作簿中提示回调的更改从未真正触发,因为该回调仅在您触发时执行通过工作簿提示自己。实施 onBeforeFirstPromptsDisplay 后一切正常。 感谢您的输入!