4

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

  1. 一个特定的工作表总是在打开时显示,即使在未启用宏的情况下打开工作簿也是如此。
  2. 工作簿用户可以在处理任何工作表时保存工作簿。
  3. 保存不能干扰用户 - 不能导航到不同的工作表,不能有消息框等。
  4. 常规保存功能(Ctrl- S,单击保存)必须保持可用,并且在使用时必须遵守上述标准。

我想避免我在这个问题底部列出的尝试解决方案。

详细信息:
工作簿是在 Windows 7 计算机上使用 Office 2007 创建的。这是一个 .xlsm 工作簿,有 2 个工作表,“调度程序”和“信息”。工作表选项卡不可见。打开工作簿时,并非所有用户都会启用宏。

打开工作簿后,用户将只看到一张工作表,如下所示:

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

如果打开工作簿并禁用宏,则必须首先显示“信息”。

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

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

5 回答 5

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
于 2013-10-02T18:22:01.013 回答
4

我没有时间对此进行测试,但您可以Application.OnTime在 BeforeSave 事件处理程序中使用它。就像是:

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

然后在模块 1 中:

Public PreviousSheet As Worksheet

Public Sub ActivatePreviousSheet()
    If Not PreviousSheet Is Nothing Then
        PreviousSheet.Activate
        Set PreviousSheet = Nothing
    End If
End Sub
于 2013-10-02T20:27:16.433 回答
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”。

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

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

于 2013-10-02T20:23:27.507 回答
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 

可以在此处找到示例文件。

于 2013-10-06T05:36:10.107 回答
1

如何使用“代理工作簿”。

“代理工作簿”

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

“目标工作簿”

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

我手头没有 Office 2007 来测试它,但我认为它应该可以。

于 2013-10-08T15:58:03.610 回答