Excel VBA - 始终在打开时显示工作表

时间:2013-09-30 16:31:01

标签: excel excel-vba vba

如何使用VBA代码满足以下条件?

  1. 即使在未启用宏的情况下打开工具簿,特定工作表也始终显示在打开状态。
  2. 工作簿用户可以在处理任何工作表时保存工作簿。
  3. 保存不得干扰用户 - 不会导航到其他工作表,没有消息框等。
  4. 常规保存功能( Ctrl - S ,点击保存)必须保持可用,使用时必须遵守上述条件。
  5. 我想避免在本问题的底部列出的尝试解决方案。

    详细信息:
    该工作簿是在Windows 7计算机上使用Office 2007创建的。它是一个包含2个工作表的.xlsm工作簿,“Scheduler”和“Info”。工作表标签不可见。并非所有用户都会在打开工作簿时启用宏。

    打开工作簿后,用户将只能按如下方式显示在一个工作表中:

    • “信息”显示是否禁用了宏,并且基本上告诉任何打开工作簿的人需要为完整工作簿功能启用宏。如果此时启用了宏,则会激活“调度程序”。
    • “Scheduler”是存储和编辑数据的地方,如果启用了宏,则会自动显示。在未启用宏的情况下打开工作簿时,不会向用户显示该文件。
    如果打开工作簿并禁用宏,

    “Info”必须首先显示。

    尝试解决方案(我正在寻找更好的解决方案!):

    • Workbook.BeforeSave事件中放置代码。这会保存并激活“信息”,以便在打开工作簿时显示。但是,如果用户在“调度程序”中并且没有完成,我在此事件中找不到一种方法可以在保存后重新激活“调度程序”。
    • 使用Application.OnKey重新映射 Ctrl - s Ctrl - S 击键。不幸的是,这省去了使用鼠标保存的用户(单击文件...保存或Office按钮...保存)。
    • 在每个操作期间进行检查,并在需要时激活“计划程序”。换句话说,在Workbook.SheetActivate.SheetChange事件中插入代码以将“计划程序”放回在激活“信息”保存后进入焦点。这会不断地运行VBA代码,这让我觉得这是让工作簿中的其他代码陷入麻烦的好方法。
    • 将代码放入Worksheet("Info").Activate事件,将焦点更改回“计划程序”。这导致“调度程序”的结果,而不是“信息”,显示工作簿何时打开,即使禁用了宏。

5 个答案:

答案 0 :(得分:5)

这不起作用吗? 已更新以便妥善保存

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

答案 1 :(得分:4)

我没有时间对此进行测试,但您可以在BeforeSave事件处理程序中使用Application.OnTime执行此操作。类似的东西:

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

然后在Module1中:

Public PreviousSheet As Worksheet

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

答案 2 :(得分:1)

编辑2:这是一个不使用AfterSave的重写。您可能需要根据需要调整从GetSaveAsFilename创建的对话框。

这取决于覆盖默认保存行为并自行处理保存。

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

在没有启用宏的情况下首先显示Info的唯一方法是,这是否保存了工作簿。这是保存时最合理的处理方式。

除非我误解了你的问题,否则不使用BeforeSave似乎是错误的。只需确保使用AfterSave。这是一个例子:

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

使用私有对象actSheet允许在保存后重新选择“ActiveSheet”。

编辑:我注意到您在评论中有更多要求。代码已更新,现在保存后,只有信息表可见,但在打开或保存后,每张工作表都会重新出现。

这使得任何打开没有宏的文件的用户都无法使用激活的其他工作表进行保存,甚至无法查看其他工作表。这肯定有助于激励他们启用宏!

答案 3 :(得分:1)

这个问题在过去已经被扼杀,很难找到真正有效的解决方案。看看这个代码应该做你需要的。基本上它显示一个启动画面,如果用户不启用宏,则隐藏所有其他工作表。如果用户点击保存并且不会干扰他们的工作,它仍将正常保存。如果他们保存并打开工作表,它将在下次打开时仍然只显示启动画面。下载下面的示例文件,您可以自己测试,确保下载Reafidy发布的文件,它有超过400个视图。如果您需要进一步修改,请告诉我。

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 

可以找到示例文件here.

答案 4 :(得分:1)

如何使用'代理工作簿'。

'代理工作簿'

  • 是唯一由用户直接打开的工作簿
  • 包含信息表
  • 包含VBA以使用Workbook.Open打开你的'真实工作簿'(正如我已经检查过Workbooks.Open文档默认情况下它不会将文件名添加到你最近的文件历史中,除非你将AddToMru参数设置为true)
  • 如果需要,VBA代码甚至可以确保您的“目标工作簿”是可信的(我找到了一些示例代码here

'目标工作簿'

  • 包含您的日程表和任何其他表格
  • 仅在“代理工作簿”中的VBA代码执行时才会打开
  • 可以像往常一样由用户随时保存

我手边没有Office 2007来测试它,但认为应该这样做。