如果 Excel 或 Word 已经打开,ThisWorkbook Workbook_Open 无法显示用户表单

ThisWorkbook Workbook_Open fails to show userform if Excel or Word already open

我用的是Office365。我有一个名为 Test.xlsm 的 Excel 文件,其中包含一个名为 frmMain.
的用户窗体 在 ThisWorkbook 对象中,然后在 Workbook_Open 事件中,我有以下代码:

    Private Sub Workbook_Open()
       Worksheets("Main").Activate
       frmMain.Show
    End Sub

每次 Test.xlsm 文件打开时启动(显示)frmMain 用户窗体。
但是,如果另一个 Excel 或 Word 文件已经打开,frmMain 用户表单将无法 launch/show.
有没有办法让 Excel 或 Word 文件已经打开并且仍然能够启动和使用 Test.xlsm 文件及其用户窗体 frmMain ?这是 Office365 的问题吗?

更新:

还尝试将应用程序安全性设置为低,以防它默认为 msoAutomationSecurityByUI:

Private Sub Workbook_Open()

dim frm As frmMain
Dim secAutomation As MsoAutomationSecurity

set frm = New frmMain
secAutomation = Application.AutomationSecurity

Application.AutomationSecurity = msoAutomationSecurityLow
Worksheets("Main").Activate
frmMain.Show

End Sub

更新:

还尝试将它放在一个独立的模块上(不在 ThisWorkbook 中):

Private Sub runForm()
   frmMain.Show
End Sub

然后从 ThisWorkbook > Workbook_Open 事件中这样调用它:

Private Sub Workbook_Open()
   Application.OnTime VBA.Now, "name of file '!runForm.runForm"
End Sub

关闭所有其他 Excel,这也会打开 Test.xlsm 和用户窗体,但是当 .xlsx 已经打开时,它是同样的问题 - 打开文件但没有打开打开用户表单。

也许您可以将打开逻辑从 ThisWorkbook 对象中移出并移至具有 Auto_Open 方法的模块中:

Private Sub Workbook_Open()
   Dim frm as frmMain
   Set frm = new frmMain

   Worksheets("Main").Activate
   frm.Show
End Sub

编辑: 因为我不确定为什么 ThisWorkbook.Workbook_Open 事件不起作用,所以我自己尝试了一下,它对我来说效果很好......但是让我知道切换到 Auto_Open 是否有效?

打开另一个工作簿时,Workbook_Open 事件不会为新打开的工作簿正确触发。这是一个错误。

解决此问题的一种方法是利用自定义 RibbonUI 对象,这样您就可以在工作簿打开时触发事件。

设置起来并不简单,但您只需设置一次。需要 3 个步骤:

1) 在ThisWorkbook对象中设置一个Friend方法
ThisWorkbook 模块中写入以下代码:

Option Explicit

Private m_openAlreadyRan As Boolean

Friend Sub FireOpenEventIfNeeded(Optional dummyVarToMakeProcHidden As Boolean)
    If Not m_openAlreadyRan Then Workbook_Open
End Sub

Private Sub Workbook_Open()
End Sub

注意几点:
a) 需要虚拟参数来隐藏 Macros 框中的方法 (Alt+F8)
b) 该方法被声明为 Friend 所以它只能被这个项目访问
c) 需要一个布尔变量 (m_openAlreadyRan)。这将在第 3 阶段稍后使用

2) 将 CustomRibbon 嵌入您的工作簿
首先,我们需要一些代码来调用在步骤 1 中创建的方法。
在您的工作簿中创建一个标准模块并将其命名为 CustomUI。将以下代码添加到 CustomUI 模块:

Option Explicit

Public Sub InitRibbon(ribbon As IRibbonUI)
    ThisWorkbook.FireOpenEventIfNeeded
End Sub

该方法需要在Ribbon初始化时调用。也就是说,这个方法是Ribbon使用的回调方法。

这是棘手的部分。这可以通过多种方式完成,您可以在网上找到一些工具。但是,我将向您展示如何手动操作:
a) 关闭并保存您的工作簿

b) 如果您不使用归档程序,请下载一个。我使用免费的7-Zip

c) 打开存档器并浏览到您的工作簿文件夹

d) Right-click 工作簿并选择 在里面打开

e) 创建一个名为 customUI 的文件夹。您会注意到 Workbook 文件实际上是文件的归档

f) 打开记事本或任何文本编辑器并使用以下 xml:

创建一个新文件
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="InitRibbon"></customUI>

请注意 onLoad 回调的名称与在 VBA 中创建的方法匹配,即 InitRibbon

g) 将文本文件(任意位置)保存为 customUI.xml(确保您没有双重扩展名(例如 .xml.txt)

h) 将 .xml 文件拖放到存档器中的 customUI 文件夹中

i) 返回上一级并打开 _rels 文件夹。你应该看到一个 .rels 文件

j) 编辑 .rels 文件(right-click 然后编辑应该打开一个记事本)

k) 添加 xml:

<Relationship Id="rId10" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/>

这需要 结束 </Relationships> 标签之前。不是之后。 我使用了 rId10,但您可以查看文件中的所有其他 rId 编号并选择下一个可用的编号。 确保您没有复制现有的 rId

l) 将您的编辑保存到文件并退出存档,同时确保您也保存了存档编辑(如果您使用带有 Ok/Cancel 框的 7-Zip,应该会提示您)

m) 关闭存档器。你完成了它

3) 设置Workbook_Open事件
我们需要考虑在步骤 1 中创建的布尔值(这样我们就不会 运行 两次使用相同的代码)和 window 视图的受保护状态。
将步骤 1 中的代码(在 ThisWorkbook 中)替换为:

Option Explicit

Private m_openAlreadyRan As Boolean
Private m_isOpenDelayed As Boolean

Friend Sub FireOpenEventIfNeeded(Optional dummyVarToMakeProcHidden As Boolean)
    If Not m_openAlreadyRan Then Workbook_Open
End Sub

Private Sub Workbook_Activate()
    If m_isOpenDelayed Then
        m_isOpenDelayed = False
        InitWorkbook
    End If
End Sub

Private Sub Workbook_Open()
    m_openAlreadyRan = True
    Dim objProtectedViewWindow As ProtectedViewWindow
    '
    On Error Resume Next
    Set objProtectedViewWindow = Application.ProtectedViewWindows(Me.Name)
    On Error GoTo 0
    '
    m_isOpenDelayed = Not (objProtectedViewWindow Is Nothing)
    If Not m_isOpenDelayed Then InitWorkbook
End Sub

Private Sub InitWorkbook()
    If VBA.Val(Application.Version) < 12 Then
        MsgBox "This Workbook requires Excel 2007 or later!", vbCritical, "Closing"
        Me.Close False
        Exit Sub
    End If
    '
    With New frmMain
        .Show
        'Other code
        '
        '
        '
    End With
End Sub

注意以下几点:
a) _Open 事件代码延迟到 _Activate 事件,以防 Window 视图受到保护
b) _Open_Activate 都指向 InitWorkbook method.This 是你添加当工作簿打开
时,您需要 运行 的代码 c) m_openAlreadyRan_Open 事件中设置为 True 以便 FireOpenEventIfNeeded 方法不会不必要地调用 _Open(即当没有其他书籍打开时错误没有发生)
d) 我使用了 NewfrmMain 实例,正如 @ArcherBird 提到的那样。通过调用 frmMain.Show 使用表单的全局实例被认为是不好的做法。此外,您可以创建一个变量而不是 With New frmMain

Dim f As New frmMain
f.Show