当打开另一个工作簿时,新打开的工作簿的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](https://istack.dev59.com/s7yDO.webp)
e) 创建一个名为customUI的文件夹。您会注意到工作簿文件实际上是一个文件归档
![enter image description here](https://istack.dev59.com/hUFBv.webp)
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](https://istack.dev59.com/W1P93.webp)
i) 返回上一层并打开 _rels 文件夹。你应该会看到一个 .rels 文件![enter image description here](https://istack.dev59.com/bXjnf.webp)
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) 我使用了一个
frmMain的
New实例,就像
@ArcherBird提到的那样。通过调用
frmMain.Show
来使用表单的全局实例被认为是不好的做法。而且,除了使用
With New frmMain
之外,您还可以创建一个变量:
Dim f As New frmMain
f.Show
Application.EnableEvents
没有被设置为False
吗?试试这个方法。在 VBE 的即时窗口中输入Application.EnableEvents = True
,然后再打开工作簿进行检查… - Siddharth Rout