Search code examples
excelvbadelete-row

Delete Data from a Worksheet If Selection from Dropdown List is Changed


Follow up question to a previously answered question: Excel VBA - Run a macro based on a range of dropdown lists.

Current: This is for a personal expense spreadsheet and I am using Column G on my Master worksheet to classify line item expenses imported from a .csv provided by my credit union. Each cell in Column G has a dropdown list which is the name of the other worksheets in my workbook: Power, Gas, Groceries, etc. Currently, when you make a selection from the Column G dropdown list, it copies A1:F1 of the current row and pastes it to the next empty row of whatever worksheet was selected, e.g. Power or Gas or Groceries. All of that is finally working fine.

Problem: However, if I re-classify a line expense, e.g. from my original selection Gas and I change it to Power it will again copy A1:F1 of the current row and move to the Power worksheet. That is great BUT I need it to remove the line we copied from our Gas tab.

Possible Solution?: The only way I can think of this is adding something like this... IF the dropdown is not blank and I change the original selection THEN I need to find an exact text copy of A1:F1 (A1: Date, B1: No., C1: Description, D1: Debit, E1: Credit, F1: Notes - these will ("should") never be duplicate) from the original selection worksheet (Gas) and delete those cells and move up the below rows. I'm asking for help for someone to please write that above scenario in code and show me what it will look like in my current code (I understand VBA at a novice level - at best).

Here is my current code that runs once a dropdown value is changed:

Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range
Set rng = Intersect(Target, Range("G2:G1001"))
If Not rng Is Nothing Then
    For Each c In rng.Cells
        Select Case c.Value
            Case "Power": Power c
            Case "Gas": Gas c
            Case "Water": Water c
            Case "Groceries, etc.": GroceriesEtc c
            Case "Eating Out": EatingOut c
            Case "Amazon": Amazon c
            Case "Home": Home c
            Case "Entertainment": Entertainment c
            Case "Auto": Auto c
            Case "Medical": Medical c
            Case "Dental": Dental c
            Case "Income": Income c
            Case "Other": Other c
        End Select
    Next c
End If
End Sub

Here is the case macro that is fired off from the above code (there is a similar macro for each case):

Sub Gas(c As Range)

Dim rng As Range

Set rng = c.EntireRow.Range("A1:F1") '<< A1:F1 here is *relative to c.EntireRow*

'copy the values
With Worksheets("Gas").Cells(Rows.Count, 1).End(xlUp)
    .Offset(1, 0).Resize(1, rng.Cells.Count).Value = rng.Value
End With

End Sub

Any Suggestions?


Solution

  • Try this. You probably need to tweak it a bit, but it should get you going. I have added a global variable that you can store the previous value from the dropdown list.
    In the SelectionChange I have tried to create an error handling to take care of multiple cells selected. If just 1 cell selected then that value will be bound to the global variable. Then you can use that variable to find the sheet of the previous value in the dropdown list, loop through the sheet, and delete the value.

    First I have added this to your Gas, Power, etc. subs. to make them dynamic.

    Sub Power(c As Range)
    
        Dim rng As Range
    
        Set rng = Nothing
        Set rng = Range("A" & c.Row & ":F" & c.Row) '<< A1:F1 here is *relative to c.EntireRow*
    
        'copy the values
        With Worksheets("Power").Cells(Rows.Count, 1).End(xlUp)
            .Offset(1, 0).Resize(1, rng.Cells.Count).Value = rng.Value
    
            ' Copy formating from Master Sheet
            With Worksheets("Master")
                Range("A" & c.Row & ":F" & c.Row).Copy
            End With
            .Offset(1, 0).PasteSpecial xlPasteFormats
            Application.CutCopyMode = False
    
        End With
    
    End Sub
    

    Under the Master sheet (not module), I have added this:

    ' Add this to the absolute top of the sheet, must be outside a procedure (sub)
    Option Explicit
    Public cbxOldVal As String
    Dim PrevVal As Variant
    
    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    If Target.Rows.Count > 1 Then Exit Sub
    If Target.Columns.Count > 1 Then Exit Sub
    
    cbxOldVal = Target.Value
    End Sub
    
    Private Sub Worksheet_Activate()
        If Selection.Rows.Count = 1 And Selection.Columns.Count = 1 Then
            PrevVal = Selection.Value
        Else
            PrevVal = Selection
        End If
    End Sub
    

    Add this to your Worksheet_Change event.

    Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rng As Range, c As Range
    Set rng = Intersect(Target, Range("G2:G1001"))
    
    If Not Intersect(Target, Columns("G")) Is Nothing Then
        If PrevVal <> "" Or cbxOldVal <> "" Then
            If cbxOldVal = Target.Value Then
                MsgBox "You have to click on another cell " & vbNewLine & "and then click back on " & Target.Address & " to change the value", vbExclamation, "Error"
                Cells(Target.Row, Target.Column) = PrevVal
                Exit Sub
            ElseIf Target.Value = "" Or Target.Value = PrevVal Then Exit Sub
            End If
        End If
    End If
    
    If Not rng Is Nothing Then
    ' Your loop
    

    Then I have added some code to your Worksheet_Change event. Add this to after the End Select.

        If cbxOldVal = "" Then
        ' do nothing
    
        Else
    
            With Worksheets(cbxOldVal)
    
                Dim i As Integer
                Dim strFindA As String, strFindB As String, strFindC As String
                Dim strFindD As String, strFindE As String, strFindF As String
                strFindA = Sheets("Master").Range("A" & c.Row)
                strFindB = Sheets("Master").Range("B" & c.Row)
                strFindC = Sheets("Master").Range("C" & c.Row)
                strFindD = Sheets("Master").Range("D" & c.Row)
                strFindE = Sheets("Master").Range("E" & c.Row)
                strFindF = Sheets("Master").Range("F" & c.Row)
    
                For i = 1 To 100    ' replace with lastrow
    
                If .Cells(i, 1).Value = strFindA _
                And .Cells(i, 2).Value = strFindB _
                And .Cells(i, 3).Value = strFindC _
                And .Cells(i, 4).Value = strFindD _
                And .Cells(i, 5).Value = strFindE _
                And .Cells(i, 6).Value = strFindF _
                Then
    
                .Rows(i).EntireRow.Delete
                MsgBox "deleted row " & i
                GoTo skip:
    
                End If
    
                Next i
    
    
            End With
        End If
    skip: