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

后端 未结 2 895
小鲜肉
小鲜肉 2020-12-20 01:55

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 t

相关标签:
2条回答
  • 2020-12-20 02:33

    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?

    0 讨论(0)
  • 2020-12-20 02:34

    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
    
    0 讨论(0)
提交回复
热议问题