如果Excel或Word已经打开,此工作簿的Workbook_Open无法显示用户窗体。

3

我使用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用户窗体无法启动/显示。
有没有办法让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 文件,则会出现相同的问题——它打开了文件,但没有打开用户窗体。


3
这并没有回答你的问题,但你真的应该使用你的表单实例而不是默认实例。像这样 - ArcherBird
不,我的意思是创建一个“新”的窗体实例,而不是使用预声明的默认实例。我的建议/答案展示了如何做到这一点。 - ArcherBird
你确定已经打开的工作簿中Application.EnableEvents 没有被设置为 False 吗?试试这个方法。在 VBE 的即时窗口中输入 Application.EnableEvents = True,然后再打开工作簿进行检查… - Siddharth Rout
是的,其他工作簿都是普通的 .xlsx 格式,在启动 Test.xlsm 之前没有运行其他宏。EnableEvents 的默认值应该是 True。无论如何,在 Workbook_Open 事件中的 frm.Show 前面我只是添加了 Application.EnableEvents = True。在后台打开另一个空白工作簿的情况下启动 Test.xlsm。Test.xlsm 打开了,但 frm.Show 没有运行,用户窗体也没有显示。 - Badda_Bing
打开了另一个工作簿的VBE。在即时窗口中键入Application.EnableEvents = True。启动Test.xlsm,它打开并启动了表格!但是,关闭了Test.xlsm。保留VBE和其他工作簿打开。再次启动Test.xlsm,它打开了工作簿但没有打开表格。与之前相同的问题。 - Badda_Bing
显示剩余4条评论
2个回答

5

当打开另一个工作簿时,新打开的工作簿的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) dummy参数是为了隐藏该方法不出现在“宏”框(Alt+F8)中
b) 该方法被声明为Friend,因此只对本项目可访问
c) 需要一个布尔变量(m_openAlreadyRan)。这将在第三阶段使用。

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

Option Explicit

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

这个方法需要在Ribbon初始化时调用。换句话说,这个方法是由Ribbon使用的回调方法。

这是棘手的部分。有几种方法可以做到这一点,你可以在网上找到一些工具。然而,我会向你展示如何手动完成:
a)关闭并保存工作簿

b)如果您没有使用文件压缩软件,请下载一个。我使用的是免费的7-Zip

c)打开压缩软件并浏览到您的工作簿文件夹

d) 右键单击工作簿,选择内部打开
enter image description here

e) 创建一个名为customUI的文件夹。您会注意到工作簿文件实际上是一个文件归档
enter image description here

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 文件夹中。
enter image description here

i) 返回上一层并打开 _rels 文件夹。你应该会看到一个 .rels 文件enter image description here

j) 编辑 .rels 文件(右键单击,然后选择编辑应该会打开记事本)

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步创建的布尔变量(以便我们不会运行相同的代码两次)和窗口视图的受保护状态。
将步骤1(在此工作簿中)中的代码替换为以下内容:

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) 如果Window View受到保护,则_Open事件代码被延迟到_Activate事件中。
b) _Open_Activate都指向InitWorkbook方法。这是您需要添加的代码,以在工作簿打开时运行。
c) 在_Open事件中将m_openAlreadyRan设置为True,这样当bug未发生(没有其他工作簿打开)时,FireOpenEventIfNeeded方法不会不必要地调用_Open
d) 我使用了一个frmMainNew实例,就像@ArcherBird提到的那样。通过调用frmMain.Show来使用表单的全局实例被认为是不好的做法。而且,除了使用With New frmMain之外,您还可以创建一个变量:
Dim f As New frmMain
f.Show

我按照所有步骤操作,你的解决方案完美地解决了问题。对于未来的用户,只需要注意第二步中的模块需要命名为“customUI”,如.xlm所引用的,而不是“CustomUI”。非常感谢Cristian,太棒了! - Badda_Bing
@Badda_Bing 很高兴它起作用了。标准模块的名称并不重要,可以是任何东西。无论模块名称如何,InitRibbon方法都会被调用。但是,在文件存档中,文件夹名称和文件名/customUI/customUI.xml必须与.rels文件中第2步子步骤k)添加的xml片段匹配。只要它们匹配,它们也可以被命名为不同的名称。 - Cristian Buse

1
也许您可以将开头的逻辑从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 能行的话,请让我知道。


我在Auto_Open模块(不在ThisWorkbook模块内)中使用了您的代码,但它没有起作用,也就是什么都没有发生。我把它放在ThisWorkbook Workbook_Open事件模块中使用,有时候它可以工作!顺便说一下,我正在测试另一个无关的Excel文件是否打开。有时Test.xlsm会打开并出现frmMain,有时则不会!我完全搞不清楚出了什么问题... - Badda_Bing
你的表单是模态的吗?有可能它会在屏幕外打开或者其他什么问题吗? - ArcherBird
@Badda_Bing,不知道你是否能够发布(如果你有的话)表单的初始化代码? - ArcherBird
@Badda_Bing,如果表单是模态的,并且您能够在工作表周围单击 - 我认为这意味着该表单没有被创建。您是否尝试实例化一个类型为frmMain的新对象,并在其上调用.Show?也许您可以尝试在表单初始化中放置一个MsgBox弹出窗口,以查看表单是否已经初始化并崩溃,或者它是否根本没有尝试执行.Show - ArcherBird
@Badda_Bing 嗯,我已经猜不出来了。你能否尝试在Workbook_Open事件中加入一个msgbox,以查看是否真的被执行了?在代码中(无论是在此工作簿中还是在任何其他已打开的工作簿中),是否有任何可能关闭Application.EnableEvents的地方? - ArcherBird
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接