问题
I use Office365. I have an Excel file called Test.xlsm which contains a userform named frmMain.
In the ThisWorkbook object and then in the Workbook_Open event I have the following code:
Private Sub Workbook_Open()
Worksheets("Main").Activate
frmMain.Show
End Sub
It is meant to launch (show) the frmMain userform every time the Test.xlsm file opens.
However, if another Excel or Word file is already open, the frmMain user form fails to launch/show.
Is there a way to have an Excel or Word file already open and still be able to launch and use the Test.xlsm file and its userform frmMain ? is this a problem with Office365?
UPDATE:
Also tried setting the application security to low in case it was defaulting to 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
UPDATE:
Also tried placing this on a stand alone module (not in ThisWorkbook):
Private Sub runForm()
frmMain.Show
End Sub
Then calling it like this from the ThisWorkbook > Workbook_Open event:
Private Sub Workbook_Open()
Application.OnTime VBA.Now, "name of file '!runForm.runForm"
End Sub
With all other Excel closed, this also opens Test.xlsm and the userform, but when an .xlsx is already open then it is the same problem - opened the file but it didn't open the userform.
回答1:
When another Workbook is opened, Workbook_Open events do not fire properly for newly opened workbooks. This is a bug.
One way to solve this is to take advantage of a Custom RibbonUI Object so that you can have an event firing when the Workbook is opened.
This is not simple to set up but you only have to do it once. There are 3 steps needed:
1) Set up a Friend method in the ThisWorkbook object
Write the following code inside the ThisWorkbook module:
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
Note a few things:
a) the dummy parameter is needed to hide the method from the Macros box (Alt+F8)
b) the method is declared as Friend so it is only accessible to this project
c) a boolean variable is needed (m_openAlreadyRan). This will be used later at stage 3
2) Embed a CustomRibbon into your Workbook
First we need some code to call the method created at step 1.
Create a standard module in your Workbook and call it CustomUI. Add the following code to the CustomUI module:
Option Explicit
Public Sub InitRibbon(ribbon As IRibbonUI)
ThisWorkbook.FireOpenEventIfNeeded
End Sub
This method needs to be called when the Ribbon is initialized. In other words, this method is a callback method used by the Ribbon.
This is the tricky part. This can be done is several ways and you can find some tools on the web. However, I will show you how to do it manually:
a) Close and Save your Workbook
b) Download an archiver program if you don't use one. I use 7-Zip which is free
c) Open the archiver and browse to your Workbook folder
d) Right-click the Workbook and choose Open inside
e) Create a folder called customUI. You will have noticed that the Workbook file is actually an achive of files
f) Open Notepad or any text editor and create a new file with the following xml:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="InitRibbon"></customUI>
Notice that the name of the onLoad callback matches the method created in VBA i.e. InitRibbon
g) Save text file (anywhere) as customUI.xml (make sure you don't have double extension (e.g. .xml.txt)
h) Drag and drop the .xml file in the customUI folder inside the archiver
i) Go back one level and open the _rels folder. You should see a .rels file
j) Edit the .rels file (right-click then Edit should open a Notepad)
k) Add the xml:
<Relationship Id="rId10" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/>
This needs to go before the ending </Relationships>
tag. Not after.
I've used rId10 but you can look at all the other rId numbers in the file and choose the next available one. Make sure you do not duplicate an existing rId
l) Save your edits to the file and exit the archive while making sure you save the archive edits as well (you should be prompted if you use 7-Zip with an Ok/Cancel box)
m) Close the archiver. You're done with it
3) Set up the Workbook_Open event
We need to take into account the boolean created at step 1 (so that we don't run same code twice) and the protected state of the window view.
Replace the code at step 1 (in ThisWorkbook) with this:
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
Notice the following:
a) The _Open event code is delayed to the _Activate event in case Window View is protected
b) Both _Open and _Activate point to the InitWorkbook method.This is where you add the code you need to run when the workbook opens
c) m_openAlreadyRan is set to True in the _Open event so that the FireOpenEventIfNeeded method does not call _Open unnecessarily (i.e. when the bug is not happening because there are no other books open)
d) I've used a New instance of frmMain exactly as @ArcherBird mentioned. It is considered bad practice to use the global instance of the form by calling frmMain.Show
. Also, instead of With New frmMain
you could just create a variable:
Dim f As New frmMain
f.Show
回答2:
Perhaps you can move your opening logic out of the ThisWorkbook
object and into a Module with a Auto_Open
method:
Private Sub Workbook_Open()
Dim frm as frmMain
Set frm = new frmMain
Worksheets("Main").Activate
frm.Show
End Sub
Edit:
Because I'm not sure why the ThisWorkbook.Workbook_Open event wouldn't work, I just tried it for myself and it worked fine for me... But let me know if switching to Auto_Open
worked?
来源:https://stackoverflow.com/questions/63134625/thisworkbook-workbook-open-fails-to-show-userform-if-excel-or-word-already-open