'Application-defined or object-defined error when any cell in worksheet is edited

I am relatively new to VBA and have managed to edit a worksheet_change sub from example code found online (Thanks Tim Williams!) that allows multiple selection and deselection in drop down-lists within specified rows and at the same time clears dependent cell contents when parent drop-down lists are edited.

I did have another version that worked a treat and included multiple separators ",", "and" and "to" to construct short sentences/lists but didn't allow deselection. I've been struggling to combine an example code that includes deselection with other code that clears data in a single Private Sub. I have found many different methods but using some code posted by Tim Williams on another thread is the closest I've got to finding something that works. But I get an "Application-defined or object-defined error" pop-up in excel each time I try to edit a cell and I'm not sure why.

The worksheet that it is associated with will be copied 10's or possibly even 100's of times in the same workbook and the workbooks themselves will be copied hence there are no worksheet or workbook references.

I would be very grateful if someone could spot my error(s) and suggest how to fix them. Thanks. Here is the code.

Private Sub Worksheet_Change(ByVal Target As Range)
    
    Const SEP As String = ", "
    Dim c As Range, Newvalue As String, Oldvalue As String, arr, v, lst, removed As Boolean
    Application.EnableEvents = True
    On Error GoTo Exitsub
    

    If Target.Row = 14 And Target.Validation.Type = 3 Then
        Target.Offset(1, 0).ClearContents
        Target.Offset(2, 0).ClearContents
    End If
        If Target.Row = 20 And Target.Validation.Type = 3 Then
        Target.Offset(2, 0).ClearContents
    End If

    If Target.Row = 15 Or 16 Or 17 Or 21 Or 22 Or 28 Or 29 Or 31 Or 33 Then
    
    If Target.CountLarge > 1 Then Exit Sub '<< only handling single-cell changes

    Select Case Target.Row
        Case 15, 16, 17, 21, 22, 28, 29, 31, 33
            Set c = Target
        Case Else: Exit Sub
        End Select
    
    If Len(c.Value) > 0 And Not c.Validation Is Nothing Then

        Application.EnableEvents = False
        Newvalue = c.Value
        Application.Undo
        Oldvalue = c.Value

        If Oldvalue = "" Then
            c.Value = Newvalue
        Else
            arr = Split(Oldvalue, SEP)
            'loop over previous list, removing newvalue if found
            For Each v In arr
                If Trim(CStr(v)) = Newvalue Then
                    removed = True
                Else
                    lst = lst & IIf(lst = "", "", SEP) & v
                End If
            Next v
            'add the new value if we didn't just remove it
            If Not removed Then lst = lst & IIf(lst = "", "", SEP) & Newvalue
            c.Value = lst
        End If
    End If
    End If    'has validation and non-empty
    Exitsub:
    If Err.Number <> 0 Then MsgBox Err.Description
    Application.EnableEvents = True
End Sub


Solution 1:[1]

Lightly tested:

Private Sub Worksheet_Change(ByVal Target As Range)
    
    Const SEP As String = ", "
    Dim c As Range, Newvalue As String, Oldvalue As String, arr, v, lst, removed As Boolean
    
    'Application.EnableEvents = True 'this only runs if EnableEvents is *already* True...
    On Error GoTo Exitsub
    
    If Target.CountLarge > 1 Then Exit Sub '<< only handling single-cell changes
    
    Set c = Target
    If Not HasListValidation(c) Then Exit Sub 'exit if cell has no list validation
    
    'you can handle all the row checking here...
    Select Case c.Row
        Case 14
            c.Offset(1, 0).Resize(2, 1).ClearContents
            Exit Sub  'added
        Case 20
            c.Offset(2, 0).ClearContents
            Exit Sub  'added
        Case 15, 16, 17, 21, 22, 28, 29, 31, 33 'ok to proceed
        Case Else: Exit Sub
    End Select
    
    If Len(c.Value) > 0 Then

        Application.EnableEvents = False
        Newvalue = c.Value
        Application.Undo
        Oldvalue = c.Value

        If Oldvalue = "" Then
            c.Value = Newvalue
        Else
            arr = Split(Oldvalue, SEP)
            'loop over previous list, removing newvalue if found
            For Each v In arr
                If Trim(CStr(v)) = Newvalue Then
                    removed = True
                Else
                    lst = lst & IIf(lst = "", "", SEP) & v
                End If
            Next v
            'add the new value if we didn't just remove it
            If Not removed Then lst = lst & IIf(lst = "", "", SEP) & Newvalue
            c.Value = lst
        End If
    End If  'c not empty
    
Exitsub:
    If Err.Number <> 0 Then MsgBox Err.Description
    Application.EnableEvents = True
End Sub

'does a cell have a List validation applied?
Function HasListValidation(c As Range) As Boolean
    Dim v
    On Error Resume Next
    v = c.Validation.Type
    On Error GoTo 0
    HasListValidation = (v = xlValidateList) '3
End Function

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1