Search code examples
excelvbaworksheet

Worksheet_Activate Code for New Sheets


I have three questions about VBA and controlling/manipulating new windows.

I have several sheets set up.

Master | Worksheet1 | Worksheet2 | Notes | Work Orders | Contact Info

1) I have WorkSheet_Activate functions set up on Notes, Work Orders, Contact Info that open up all three sheets in seperate windows and arrange them vertically.

Private Sub WorkSheet_Activate()

    ActiveWindow.NewWindow
    ActiveWindow.NewWindow
    Windows.Arrange ArrangeStyle:=xlVertical
    Sheets("Notes").Select
    Windows("Mastersheet.xlsm:2").Activate
    Sheets("Work Orders").Select
    Windows("Mastersheet.xlsm:1").Activate
    Sheets("Contact Info").Select

End Sub

The problem with it is that if I can activate these sheets again, it will open more windows. I would like the code to detect if the windows are already open and break if it is.

2) Now, when I navigate to a different sheet, such as Master, I would like the extra windows to close and for the Master sheet to be active. I was using the following code on the Master sheet.

Private Sub WorkSheet_Activate()


    Windows("Mastersheet.xlsm:2").Activate
    ActiveWindow.Close
    Windows("Mastersheet.xlsm:1").Activate
    ActiveWindow.Close
    ActiveWindow.WindowState = xlMaximized

End Sub

The problem with this code is that if the extra windows aren't open then it will error out. Can I do a logic check of some sort to get this to work? I don't know what values to check...

3) The last problem is that there are new sheets generated dynamically by macros within the workbook. Those new worksheets won't carry the above code that closes multiple windows and focuses on the activesheet. Is there a different object that I should be putting the code to so that it applies to the Master | Worksheet1 | Worksheet2 sheets and any new sheets?


Solution

  • That's a lot of questions. :) For 3, you need to move your events out of where they are and into a custom class module that handles application level events. Start by inserting a new class module into your project (Insert - Class Module). Name that module CAppEvents (F4 to show the property sheet where you can change the name). Then paste this code into the class module

    Option Explicit
    
    Private WithEvents mobjWb As Workbook
    
    Private Sub Class_Terminate()
    
        Set mobjWb = Nothing
    
    End Sub
    
    Public Property Get wb() As Workbook
    
        Set wb = mobjWb
    
    End Property
    
    Public Property Set wb(objwb As Workbook)
    
        Set mobjWb = objwb
    
    End Property
    
    Private Sub mobjWb_SheetActivate(ByVal Sh As Object)
    
        Dim wn As Window
    
        If IsSplitSheet(Sh) Then
            If Not IsSplit(Sh) Then
                CreateSplitSheets Sh
            End If
        Else
            If IsSplit(Sh) Then
                For Each wn In Me.wb.Windows
                    If wn.Caption Like Me.wb.Name & ":#" Then
                        wn.Close
                    End If
                Next wn
                ActiveWindow.WindowState = xlMaximized
                Sh.Activate
            End If
        End If
    
    End Sub
    
    Private Function IsSplitSheet(Sh As Object) As Boolean
    
        Dim vaNames As Variant
        Dim i As Long
    
        IsSplitSheet = False
        vaNames = GetSplitSheetNames
    
        For i = LBound(vaNames) To UBound(vaNames)
            If vaNames(i) = Sh.Name Then
                IsSplitSheet = True
                Exit For
            End If
        Next i
    
    End Function
    
    Private Function IsSplit(Sh As Object) As Boolean
    
        Dim wn As Window
    
        IsSplit = False
    
        For Each wn In Me.wb.Windows
            If wn.Caption Like Sh.Parent.Name & ":#" Then
                IsSplit = True
                Exit For
            End If
        Next wn
    
    End Function
    
    Private Sub CreateSplitSheets(Sh As Object)
    
        Dim vaNames As Variant
        Dim i As Long
        Dim wn As Window
        Dim wnActive As Window
    
        vaNames = GetSplitSheetNames
        Set wnActive = ActiveWindow
    
        For i = LBound(vaNames) To UBound(vaNames)
            If vaNames(i) <> Sh.Name Then
                Set wn = Me.wb.NewWindow
                wn.Activate
                On Error Resume Next
                    wn.Parent.Sheets(vaNames(i)).Activate
                On Error GoTo 0
            End If
        Next i
    
        Sh.Parent.Windows.Arrange xlVertical
        wnActive.Activate
        Sh.Activate
    
    End Sub
    
    Private Function GetSplitSheetNames() As Variant
    
        GetSplitSheetNames = Array("Notes", "Work Orders", "Contact Info")
    
    End Function
    

    Then insert a standard module (Insert - Module) and paste this code

    Option Explicit
    
    Public gclsAppEvents As CAppEvents
    
    Sub Auto_Open()
    
        Set gclsAppEvents = New CAppEvents
        Set gclsAppEvents.wb = ThisWorkbook
    
    End Sub
    

    Here's what's happening: When you open the workbook, Auto_Open will run and it will create a new instance of your CAppEvents object. Since gclsAppEvents is public (aka global) it won't lose scope for as long as the workbook is open. It will sit there listening for events (because we used the WithEvents keyword in the class).

    In the class there's a sub called mobjWb_SheetActivate. This is what will fire whenever any sheet in this workbook is activated. First it checks if the sheet you just activated (the Sh variable) is one of the ones you want to split (using IsSplitSheet). If it is, it then checks to see if it already has been split. If not, it splits them.

    If Sh (the sheet you just activated) is not one of the 'split sheets', then it checks to see if a split has been done (IsSplit). IF it has, it closes all the split windows.

    If you even want to add, change, or delete sheets that cause a split, you go to the GetSplitSheetNames function and change the Array arguments.

    Because we're using a custom class and sniffing for events at the workbook level, you can add and delete sheets all you want.