Aaron Thomas
Aaron Thomas

Reputation: 5281

Excel VBA - always show worksheet on open

How can the following conditions be met with VBA code?

  1. A particular worksheet is always displayed on open, even if the worbook is opened without enabling macros.
  2. A workbook user may save the workbook while working on any worksheet.
  3. The save must not interfere with the user - no navigating away to a different sheet, no messageboxes, etc.
  4. The regular save functions (Ctrl-S, clicking Save) must remain available and when used must obey the criteria above.

I'd like to avoid the attempted solutions I've listed at the bottom of this question.

Details:
The workbook is created using Office 2007 on a Windows 7 machine. It is an .xlsm workbook with 2 worksheets, "Scheduler" and "Info." Sheet tabs are not visible. Not all users will enabled macros when the workbook is opened.

Upon opening the workbook, a user will only be exposed to one sheet as follows:

"Info" must show up first thing if the workbook is opened and macros are disabled.

Attempted Solutions (I'm looking for better solutions!):

Upvotes: 4

Views: 12711

Answers (5)

fan711
fan711

Reputation: 716

How about using a 'proxy workbook'.

The 'proxy workbook'

  • is the only workbook which is directly opened by the users
  • contains the info sheet
  • contains VBA to open your 'real workbook' using Workbooks.Open (As I've checked with Workbooks.Open documentation by default it will not add the file name to your recent files history unless you set the AddToMru argument to true)
  • if required the VBA code could even make sure that your 'target workbook' is trusted (I found some sample code here)

The 'target workbook'

  • contains your Schedule and any other sheets
  • is only opened if the VBA code in 'proxy workbook' was executed
  • can be saved by the user at any time as usual

I've got no Office 2007 at hand to test this but think it should do.

Upvotes: 1

Reafidy
Reafidy

Reputation: 8471

This problem has been flogged to death in the past, its just hard to find a solution that actually works. Take a look at this code which should do what you need. Basically it shows a splash screen, with all other sheets hidden if the user does not enable macros. It will still save normally if the user clicks save and wont interfere with their work. If they save with there worksheet open it will still show only the splash screen when next opened. Download the sample file below and you can test for yourself, make sure you download the file posted by Reafidy it has over 400 views. If you need it modified further let me know.

Private Sub Workbook_BeforeClose(Cancel As Boolean) 
    bIsClosing = True 
End Sub 
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean) 
    Dim wsArray() As Variant 
    Dim iCnt As Integer 
    Application.ScreenUpdating = 0 

    Splash.Visible = True 

    For Each wsSht In ThisWorkbook.Worksheets 
        If Not wsSht.CodeName = "Splash" Then 
            If wsSht.Visible = True Then 
                iCnt = iCnt + 1: Redim Preserve wsArray(1 To iCnt) 
                wsArray(iCnt) = wsSht.Name 
            End If 
            wsSht.Visible = xlSheetVeryHidden 
        End If 
    Next 

    Application.EnableEvents = 0 
    ThisWorkbook.Save 
    Application.EnableEvents = 1 

    If Not bIsClosing Then 
        For iCnt = 1 To UBound(wsArray) 
            Worksheets(wsArray(iCnt)).Visible = True 
        Next iCnt 
        Splash.Visible = False 
        Cancel = True 
    End If 

    Application.ScreenUpdating = 1 
End Sub 
Private Sub Workbook_Open() 
    Dim wsSht As Worksheet 

    For Each wsSht In ThisWorkbook.Worksheets 
        wsSht.Visible = xlSheetVisible 
    Next wsSht 

    Splash.Visible = xlSheetVeryHidden 

    bIsClosing = False 
End Sub 

A sample file can be found here.

Upvotes: 1

Daniel
Daniel

Reputation: 13132

Edit 2: Here is a re-write that does not utilize AfterSave. You may need to tweak the dialog created from GetSaveAsFilename according to your needs.

This relies on overriding default save behavior and handling the save yourself.

Private actSheet As Worksheet
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Cancel = True
    PrepareForSave
    manualSave SaveAsUI
    AfterSave ThisWorkbook.Saved
End Sub
Private Sub PrepareForSave()
    Set actSheet = ThisWorkbook.ActiveSheet
    ThisWorkbook.Sheets("Info").Activate
    hidesheets
End Sub
Private Sub manualSave(ByVal SaveAsUI As Boolean)
    On Error GoTo SaveError 'To catch failed save as
    Application.EnableEvents = False
    If SaveAsUI Then
        If Val(Application.Version) >= 12 Then
            sPathname = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xlsm), *.xlsm")
            If sPathname = False Then 'User hit Cancel
                GoTo CleanUp
            End If
            ThisWorkbook.SaveAs Filename:=sPathname, FileFormat:=52
        Else
            sPathname = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xls), *.xls")
            If sPathname = False Then
                GoTo CleanUp
            End If
            ThisWorkbook.SaveAs Filename:=sPathname, FileFormat:=xlNormal
        End If
    Else
        ThisWorkbook.Save
    End If
SaveError:
    If Err.Number = 1004 Then
        'Cannot access save location
        'User clicked no to overwrite
        'Or hit cancel
    End If
CleanUp:
    Application.EnableEvents = True
End Sub

Private Sub AfterSave(ByVal bSaved As Boolean)
    showsheets
    If actSheet Is Nothing Then
        ThisWorkbook.Sheets("Scheduler").Activate
    Else
        actSheet.Activate
        Set actSheet = Nothing
    End If
    If bSaved Then
        ThisWorkbook.Saved = True
    End If
End Sub
Private Sub hidesheets()
    For Each ws In ThisWorkbook.Worksheets
        If ws.Name <> "Info" Then
            ws.Visible = xlVeryHidden
        End If
    Next
End Sub
Private Sub showsheets()
    For Each ws In ThisWorkbook.Worksheets
        ws.Visible = True
    Next
End Sub
Private Sub Workbook_Open()
    AfterSave True
End Sub

The only way to make Info display first without macros enabled is if that is how the workbook was saved. This is most reasonably handled when saving.

Unless I misunderstood your issue, not using BeforeSave seems misguided. Just make sure to use AfterSave as well. Here's an example:

Private actSheet As Worksheet
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
    showsheets
    actSheet.Activate
    Set actSheet = Nothing
    Thisworkbook.Saved = true 'To prevent save prompt from appearing
End Sub

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Set actSheet = ThisWorkbook.activeSheet
    ThisWorkbook.Sheets("Info").Activate
    hidesheets
End Sub

Private Sub Workbook_Open()
    showsheets
    ThisWorkbook.Sheets("Scheduler").Activate
End Sub
Private Sub hidesheets()
    For Each ws In ThisWorkbook.Worksheets
        If ws.Name <> "Info" Then
            ws.Visible = xlVeryHidden
        End If
    Next
End Sub
Private Sub showsheets()
    For Each ws In ThisWorkbook.Worksheets
        ws.Visible = True
    Next
End Sub

The use of the private object actSheet allows the "ActiveSheet" to be reselected after save.

Edit: I noticed you had more requirements in the comments. The code has been updated so that now upon saving, only the Info sheet will be visible, but when opened or after saving, every sheet will reappear.

This makes it so that any user opening the file without macros will not be able to save with a different sheet activated, or even view the other sheets. That would certainly help motivate them to enable macros!

Upvotes: 1

mr_plum
mr_plum

Reputation: 2437

Will this not work? Updated to handle Saving gracefully

Private Sub Workbook_Open()
    ThisWorkbook.Worksheets("Scheduler").Activate
End Sub

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    ThisWorkbook.Worksheets("Info").Activate
    If (ShouldSaveBeforeClose()) Then
        Me.Save
    Else
        Me.Saved = True ' Prevents Excel Save prompt.
    End If
End Sub

Private Function ShouldSaveBeforeClose() As Boolean
    Dim workbookDirty As Boolean
    workbookDirty = (Not Me.Saved)
    If (Not workbookDirty) Then
        ShouldSaveBeforeClose= False
        Exit Function
    End If

    Dim response As Integer
    response = MsgBox("Save changes to WorkBook?", vbYesNo, "Attention")
    ShouldSaveBeforeClose= (response = VbMsgBoxResult.vbYes)
End Function

Upvotes: 5

to StackOverflow
to StackOverflow

Reputation: 124766

I don't have time to test this out, but you might be able to do this using Application.OnTime in your BeforeSave event handler. Something like:

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Dim objActiveSheet
    Set objActiveSheet = Me.ActiveSheet
    If objActiveSheet Is InfoSheet Then Exit Sub
    If Module1.PreviousSheet Is Nothing Then
        Set Module1.PreviousSheet = objActiveSheet
        InfoSheet.Activate
        Application.OnTime Now, "ActivatePreviousSheet"
    End If
End Sub

Then in Module1:

Public PreviousSheet As Worksheet

Public Sub ActivatePreviousSheet()
    If Not PreviousSheet Is Nothing Then
        PreviousSheet.Activate
        Set PreviousSheet = Nothing
    End If
End Sub

Upvotes: 4

Related Questions