【问题标题】:Password protection worksheet Excel VBA密码保护工作表 Excel VBA
【发布时间】:2019-05-27 05:21:15
【问题描述】:

我正在尝试为 Excel 工作簿中的多个工作表提供一种次要形式的访问保护。我知道这并不容易实现,并且仍然存在保护问题。

我在下面有关于如何实现这一目标的工作代码。但是,它仅适用于工作簿中的一张工作表。有没有办法在这段代码中添加多个工作表。

注意:我不想创建同一个工作簿的多个版本。我只想要一个简单的密码来访问工作表。我了解这并不提供万无一失的方法或限制访问

Private Sub Workbook_Open()

End Sub

    Private Sub Workbook_SheetActivate(ByVal Sh As Object)
    Dim MySheetName As String

    MySheetName = "Sheet1" 'The first sheet which I want to hide.
    MySheetName = "Sheet2" 'The second sheet which I want to hide.

    If Application.ActiveSheet.Name = MySheetName Then
        Application.EnableEvents = False
        Application.ActiveSheet.Visible = False
        response = Application.InputBox("Password", "Enter Password", "", Type:=2)

            If response = "1234" Then 'Unhide Password.
                Application.Sheets(MySheetName).Visible = True
                Application.Sheets(MySheetName).Select
            End If
    End If

    Application.Sheets(MySheetName).Visible = True

    Application.EnableEvents = True
    End Sub

此代码仅适用于单个工作表。它可以适应在多张纸上提供保护吗?

【问题讨论】:

  • 您分配MySheetName = "Sheet1" 然后立即用MySheetName = "Sheet2" 覆盖它。这就是为什么它只适用于 sheet2。此外,您有一个事件参数Sh,它指的是刚刚激活的工作表,使用它而不是ActiveSheet。第三,当您隐藏活动工作表时,另一个工作表将变为活动状态。那可能是您要保护的另一张纸。

标签: excel vba passwords


【解决方案1】:

我认为这就是您所关注的。您需要一个集合,或者更一般地说,一个可以保存多个值的数据结构。从这里,您可以将您的值列表与当前激活的工作表进行比较。

Option Explicit
Private PreviousSheet As Worksheet

Private Sub Workbook_SheetActivate(ByVal Sh As Object)
    Dim SheetNames As Collection: Set SheetNames = New Collection
    Dim SheetName  As Variant
    Dim response   As String
    Dim ws         As Excel.Worksheet

    'List of sheet names you want to hide
    SheetNames.Add "Sheet1"
    SheetNames.Add "Sheet2"

    For Each SheetName In SheetNames
        On Error Resume Next
        Set ws = ThisWorkbook.Worksheets(SheetName)
        On Error GoTo 0

        If Not ws Is Nothing Then
            If ws.Name = Sh.Name Then
                Application.EnableEvents = False

                response = Application.InputBox("Password", "Enter Password", "", Type:=2)

                If response = "1234" Then
                    ws.Visible = xlSheetVisible
                    ws.Activate
                ElseIf response = "False" Or response = vbNullString Then
                    If Not PreviousSheet Is Nothing Then PreviousSheet.Activate
                Else
                    ws.Visible = xlSheetHidden
                End If

            End If

            Application.EnableEvents = True
        End If
    Next

End Sub

Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
    Set PreviousSheet = Sh
End Sub

【讨论】:

  • 太棒了。在这种情况下,以下情况不成立:'ws.Name = Sh.Name' 在使用它的行的上下文中?我会将此代码放在一个模块中,并在 Open 事件中另外启用它。
  • 如果激活的工作表不在集合中但存在于工作簿中,则不会出现这种情况。例如。您在工作簿中有 Sheet1、Sheet2、Sheet3,并激活了 Sheet3。用户似乎想有条件地对某些工作表强制执行此行为。我认为隐藏工作簿加载事件中不需要的所有工作表很有意义。
  • 抱歉这个愚蠢的问题,我现在可以看到了。有没有办法在打开工作簿时触发此代码,即当我选择保存它时,例如Sheet1,它会在没有保护的情况下使用 Sheet1 启动,即没有将代码放入模块中?
  • @RyanWildry 小点,VBA 不会简化布尔评估,对吧?这意味着这行 If Not ws Is Nothing And ws.Name = Sh.Name Then 中的两个检查都将运行,即使第一个评估为 False?这意味着有可能调用Nothing 的.Name 成员(当ws 是Nothing)即使Nothing 没有这样的成员(导致错误),对吧?我可能错了。此外,sh.Name 可能需要为 sheetName,尽管我可能会感到疲倦和误读。
  • @chillin 是的,没错。该术语实际上是short-circuit evaluationen.wikipedia.org/wiki/Short-circuit_evaluation。试图快速进行的疏忽,我已经更新了代码。仅当您在集合中添加了工作表但工作表不再存在时,这才会成为问题。 Sh.Name 是正确的,我们想要激活的工作表。
【解决方案2】:

这是真正快速而肮脏的方法。

MySheetName = "Sheet1 Sheet2 Sheet_Named_Fred"

....只需列出所有工作表名称,它们之间有一个空格(或其他任何内容)。

然后替换你的行 如果 Application.ActiveSheet.Name = MySheetName 则

有

如果 Instr(MySheetName,Sh.Name)>0 那么

如果您愿意,请使用“Application.Activesheet.Name”而不是我的“Sh.Name”。 “Sh”是传递给该标准事件函数的参数,并且是刚刚被激活的工作表,因此如果您将“Application.ActiveSheet.Name”替换为“Sh.name”,您的代码会看起来更简洁。

如果第二个字符串出现在第一个字符串的任何位置(特别是字符位置编号),“INSTR”函数将返回 >0。因此,如果 activesheet 名称出现在长长的工作表名称字符串中的任何位置,它将接受您的密码测试。

这是“快速而肮脏的”,因为如果您的任何工作表名称是另一个工作表名称的子字符串,它将失败。例如,如果您有名为“Totals”和“GrandTotals”的工作表,那么如果您将“GrandTotals”放在长字符串中,那么激活“Totals”也会触发 INSTR 函数。 [删除代码] 我刚刚对其进行了编辑,以通过检查工作表名称数组来删除“肯定”执行此操作的代码。下面的其他人建议使用集合或其他数据结构。但是有一种简单的方法可以防止上述最简单的解决方案发生故障。不允许在工作表名称中使用 *(或其他几个字符)。所以需要密码的名字字符串可以是:

MySheetName = "*Sheet1* *Sheet2* *Sheet_Named_Fred*"

然后您的 INSTR 搜索名称只需要在工作表名称的两侧加上星号:

IF INSTR(MySheetName, "*"+Sh.Name+"*")>0 那么

...你就完成了。不需要集合或其他复杂的名称搜索。

【讨论】:

  • 是的,但他的意思是手动挑选其中的几个,而不是每一个。使用字符串搜索确实提供了这样一种可能性,即他可以用“PWDsheet1”、“PWDsheet3”等名称中的“pwd”命名他想要密码的工作表,然后只需搜索“PWD”即可获得他们都是,没有其他人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-09
  • 1970-01-01
  • 2017-11-09
  • 2012-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多