【问题标题】:Preventing users from deleting a row in a particular table with a certain value in a particular column in Excel防止用户删除 Excel 中特定列中具有特定值的特定表中的行
【发布时间】:2016-03-15 00:22:40
【问题描述】:

这可能类似于this question,但我相信它在复杂性方面更进了一步,这就是我问它的原因。

上下文:我正在构建一个预算电子表格,可以在表格中创建和删除行。在工作表中,我有两张桌子。一个包含基于类别的总计,而另一个表包含用户可以输入以填充另一个表中的总计的事务。我保护了工作表以防止用户破坏公式,并且只有他们应该编辑的单元格(即输入值)不受保护。我还有用于插入和删除表格上的一行或多行的宏(我对宏进行了编码以在宏运行完成之前和之后取消保护/保护工作表)。

问题:我的问题涉及first table。在该表中,我想确保不能删除“存款”行。问题是,在我的代码中,如何确保用户可以删除另一个包含“存款”的表中的所有其他行,同时防止删除该表中的“存款”行?我正在考虑以下伪代码,但请随时提出其他建议:

'If selected range contains cells in Column A
'and cell in selected range = Deposits
'Then pop error message
'Exit Sub

这是我的删除宏的代码

Sub DeleteRow()
'
' DeleteRow Macro
'
' Keyboard Shortcut: Ctrl+Shift+D
'

Dim loTtest         As ListObject
Dim loSet           As ListObject
Dim c               As Range
Dim arrRows()       As Variant
Dim arrTemp()       As Variant
Dim xFind           As Variant
Dim iCnt            As Long
Dim sMsg            As String

ActiveSheet.Unprotect Password:="PYS"
Erase arrRows()
iCnt = 1
For Each c In Selection.Cells
    If Not c.ListObject Is Nothing Then
        If loSet Is Nothing Then
            Set loSet = c.ListObject
        Else
            If c.ListObject <> loSet Then
                'different table
                MsgBox "You have more than one table selected.", vbInformation, "ERROR!"
                ActiveSheet.Protect Password:="PYS"
                GoTo MyExit
            End If
        End If

        If iCnt = 1 Then
            ReDim arrRows(1 To iCnt)
            arrRows(iCnt) = c.Row - loSet.HeaderRowRange.Row
            iCnt = iCnt + 1
        Else
            On Error Resume Next
            xFind = 0
            xFind = WorksheetFunction.Match(c.Row - loSet.HeaderRowRange.Row, arrRows(), 0)
            If xFind = 0 Then
                ReDim Preserve arrRows(1 To iCnt)
                arrRows(iCnt) = c.Row - loSet.HeaderRowRange.Row
                iCnt = iCnt + 1
            End If
            Err.Clear
            On Error GoTo 0
        End If

    Else
        'a cell is not in a table
        MsgBox "Your selection is all or partially outside of a table.", vbInformation, "ERROR!"
        ActiveSheet.Protect Password:="PYS"
        GoTo MyExit
    End If
Next c

Call SortArray(arrRows())
sMsg = "Are you sure you want to delete " & UBound(arrRows) & " rows from from this table?"
If MsgBox(sMsg, vbYesNo + vbDefaultButton2, "CONTINUE?") <> vbYes Then
    ActiveSheet.Protect Password:="PYS"
    Exit Sub
End If

For iCnt = UBound(arrRows) To LBound(arrRows) Step -1

    loSet.ListRows(arrRows(iCnt)).Delete
Next iCnt
ActiveSheet.Protect Password:="PYS"
Exit Sub

MyExit:

End Sub



Sub SortArray(MyArray() As Variant)

Dim iStart          As Long
Dim iEnd            As Long
Dim iStep           As Long
Dim iMove           As Long
Dim vTemp           As Variant

iStart = LBound(MyArray)
iEnd = UBound(MyArray)
For iStep = iStart To iEnd - 1
    For iMove = iStep + 1 To iEnd
        If MyArray(iStep) > MyArray(iMove) Then
            vTemp = MyArray(iMove)
            MyArray(iMove) = MyArray(iStep)
            MyArray(iStep) = vTemp
        End If
    Next iMove
Next iStep

End Sub

顺便说一句,这一切都不是我自己想出来的;我把大部分代码都拼凑了起来。 :) 如果您需要更多信息或上下文,请告诉我。提前致谢!

Here is the link to the budget workbook.

【问题讨论】:

  • 上传示例工作簿会很有帮助。一个有用的建议是在使用受保护的工作表时使用 Workbook_Open 事件,该事件将遍历工作表并在将 UserInterfaceOnly 参数设置为 true 的情况下保护它们打开。这允许 vba 对受保护的工作表进行更改,而无需每次都取消保护和保护工作表,但仍会阻止用户进行编辑。这是一个示例语法wbTCT.Sheets(i).Protect Password:="?CSandM!|", UserInterfaceOnly:=True 循环是For i = 1 to thisworkbook.sheets.count Next i
  • 请删除“宏”标签,它在 StackOverflow 上用于其他内容。
  • 只是为了您的考虑,我想指出 Excel 仅提供非常有限的安全性(如果有的话)。请注意,您选择的任何安全性仅与执行它的软件一样好。如果您使用 Microsoft Excel 打开 Excel 文件,则所有安全机制都将按预期实施。但是还有许多其他程序不这样做。还要考虑到并非所有计算机都运行宏。对于较新的 Excel 版本,大多数未签名的宏实际上都被阻止了(除非您按下按钮来启用它们)。
  • 使用LibreOffice 打开 Excel 文件甚至可以让您在没有密码保护的情况下查看 VBA 代码(允许用户查看代码中的密码)。我想说的是,如果您允许某人打开它,Excel 只会为您的数据提供非常有限的安全性。如果您确实需要保护您的数据,您可能需要选择使用 SQL 后端。
  • 感谢@Ralph,我并不真正关心人们查看密码以解除对单元格的保护,因为大多数普通用户无论如何都不知道该怎么做。密码保护只是为了防止用户破坏公式,而不是提高安全性。我还打算编写一个简单的用户指南,其中包括需要在此预算上启用宏才能使其工作。我意识到宏可能并不适合所有人,但这是我能做的最好的。

标签: vba excel


【解决方案1】:

If ActiveSheet.Cells(c.Row, 1).Value = "Deposits" ThenHere 是有效的 DeleteRow sub

Sub DeleteRow()
'
' DeleteRow Macro
'
' Keyboard Shortcut: Ctrl+Shift+D
'

    Dim loTtest         As ListObject
    Dim loSet           As ListObject
    Dim c               As Range
    Dim arrRows()       As Variant
    Dim arrTemp()       As Variant
    Dim xFind           As Variant
    Dim iCnt            As Long
    Dim sMsg            As String

    ActiveSheet.Unprotect Password:="PYS"
    Erase arrRows()
    iCnt = 1

    'This is the loop that I added before anything else to keep people from deleting the row with "Deposits"
    For Each c In Selection.Cells
        If ActiveSheet.Cells(c.Row, 1).Value = "Deposits" Then
            MsgBox "Your Selection contains 'Deposits'!" & vbCrLf & _
                "The 'Deposits' row cannot be deleted!", vbExclamation
            GoTo MyExit
        End If
    Next

    For Each c In Selection.Cells
        If Not c.ListObject Is Nothing Then
            If loSet Is Nothing Then
                Set loSet = c.ListObject
            Else
                If c.ListObject <> loSet Then
                    'different table
                    MsgBox "You have more than one table selected.", vbInformation, "ERROR!"
                    ActiveSheet.Protect Password:="PYS"
                    GoTo MyExit
                End If
            End If

            If iCnt = 1 Then
                ReDim arrRows(1 To iCnt)
                arrRows(iCnt) = c.Row - loSet.HeaderRowRange.Row
                iCnt = iCnt + 1
            Else
                On Error Resume Next
                xFind = 0
                xFind = WorksheetFunction.Match(c.Row - loSet.HeaderRowRange.Row, arrRows(), 0)
                If xFind = 0 Then
                    ReDim Preserve arrRows(1 To iCnt)
                    arrRows(iCnt) = c.Row - loSet.HeaderRowRange.Row
                    iCnt = iCnt + 1
                End If
                Err.Clear
                On Error GoTo 0
            End If

        Else
            'a cell is not in a table
            MsgBox "Your selection is all or partially outside of a table.", vbInformation, "ERROR!"
            ActiveSheet.Protect Password:="PYS"
            GoTo MyExit
        End If
    Next c

    Call SortArray(arrRows())
    sMsg = "Are you sure you want to delete " & UBound(arrRows) & " rows from from this table?"
    If MsgBox(sMsg, vbYesNo + vbDefaultButton2, "CONTINUE?") <> vbYes Then
        ActiveSheet.Protect Password:="PYS"
        Exit Sub
    End If

    For iCnt = UBound(arrRows) To LBound(arrRows) Step -1
        loSet.ListRows(arrRows(iCnt)).Delete
    Next iCnt
    ActiveSheet.Protect Password:="PYS"
    Exit Sub

MyExit:

End Sub

【讨论】:

  • 这很接近,但上面的代码只保护包含“存款”的单元格,所以如果我要在表格中选择该行中的另一个单元格,宏仍然会运行并删除存款排。此外,代码应允许删除此工作表中另一个表中的存款...而不是 A 列中包含“存款”的表行。再次感谢!
  • 这是真的...将If c.Value = "Deposits" Then 更改为If ActiveSheet.Cells(c.Row, 1).Value = "Deposits" Then 我也对上面的代码进行了编辑!