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

社会主义新天地 提交于 2021-02-04 06:24:59

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!