2

Objective: in a table, allow users to select rows (the rows where the SELECTION is), press a short cut and delete those rows. No matter if they are filtered and the selection is in non contiguous ranges or not.

I have the code below I got from another site and modified it:

The problem varies, from runtime error 1004: can't move cells in a filtered range or table to delete method of class failed (or something, it happens less often than the first one)

Sub DeleteTableRows()
    'PURPOSE: Delete table row based on user's selection
    'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault

    Dim rng As Range
    Dim DeleteRng As Range
    Dim cell As Range
    Dim TempRng As Range
    Dim Answer As Variant
    Dim area As Range
    Dim ReProtect As Boolean
    Dim copyRange As Range
    Dim pasteRange As Range
    Dim wb As Workbook
    Dim a As Long

    'Set Range Variable
      On Error GoTo InvalidSelection
        Set rng = Selection
      On Error GoTo 0


    'Unprotect Worksheet
      With ThisWorkbook.ActiveSheet
        If .ProtectContents Or ProtectDrawingObjects Or ProtectScenarios Then
          On Error GoTo InvalidPassword
          .Unprotect Password
          ReProtect = True
          On Error GoTo 0
        End If
      End With

      Set wb = ThisWorkbook

    'Loop Through each Area in Selection
      For Each area In rng.Areas
        For Each cell In area.Cells.Columns(1)
          'Is selected Cell within a table?
            InsideTable = True

          'Gather rows to delete
            If InsideTable Then
              On Error GoTo InvalidActiveCell
              Set TempRng = Intersect(cell.EntireRow, ActiveCell.ListObject.DataBodyRange)
              On Error GoTo 0

              If DeleteRng Is Nothing Then
                Set DeleteRng = TempRng
              Else
                Set DeleteRng = Union(TempRng, DeleteRng)
              End If

            End If

        Next cell
      Next area


    'Error Handling
      If DeleteRng Is Nothing Then GoTo InvalidSelection
      If DeleteRng.Address = ActiveCell.ListObject.DataBodyRange.Address Then GoTo DeleteAllRows
      If ActiveCell.ListObject.DataBodyRange.Rows.Count = 1 Then GoTo DeleteOnlyRow

    'Ask User To confirm delete (since this cannot be undone)
        DeleteRng.Select

        If DeleteRng.Rows.Count = 1 And DeleteRng.Areas.Count = 1 Then
          Answer = MsgBox("Are you sure you want to delete the currently selected table row? " & _
           " This cannot be undone...", vbYesNo, "Delete Row?")
        Else
          Answer = MsgBox("Are you sure you want to delete the currently selected table rows? " & _
           " This cannot be undone...", vbYesNo, "Delete Rows?")
        End If

    'Delete row (if wanted)
      If Answer = vbYes Then

        'this part is giving me troubles
        For a = DeleteRng.Areas.Count To 1 Step -1
            Debug.Print DeleteRng.Areas.Count
            DeleteRng.Areas(a).EntireRow.Delete
        Next a

      End If

    'Protect Worksheet
      If ReProtect = True Then wb.Worksheets("Open Orders").Protect Password:=Password, DrawingObjects:=True, Contents:=True, Scenarios:=False _
    , AllowSorting:=True, AllowFiltering:=True, AllowUsingPivotTables:=True, AllowFormattingRows:=True, AllowFormattingColumns:=True, AllowFormattingCells:=True  

End Sub
Community
  • 1
  • 1
rzenva
  • 87
  • 1
  • 1
  • 11
  • Can you give an example of which rows they are selecting? eg., does this happen if you select one row only? Does it make a difference whether multiple rows are consecutive? if consecutive rows, are they clicking individually to highlight them one at a time, or "dragged" to select (and therefore possibly containing both hidden *and* visible rows)? – ashleedawg Jul 24 '18 at 04:07
  • Multiple rows, no matter if continuous ranges or non continuous. If you select one row only it works. They drag to select, by which I tried adding special type cells but they didnt really help. – rzenva Jul 24 '18 at 04:09
  • alright - what if you select multiple rows,but **one at a time** (example:, click the first row, then **hold down CTRL** and click the 2nd, then the 3rd, etc... without dragging the mouse across multiple rows) ...? (if it works like this then I might know what's going on) – ashleedawg Jul 24 '18 at 04:10
  • It works by selecting one at a time! I added `set deleterng = deleterng.specialtype(xltypevisible)` and it worked. But the part _delete if wanted_ can just delete one area until I get an object or aplication defined error – rzenva Jul 24 '18 at 04:15
  • No, you're not selecting rows. (If you did, then that would clear the user's selected rows). You're *iterating through* the selection. So, are you saying you're okay with hidden rows being deleted sometimes and not others? (it will depend on how the user selects the rows.) – ashleedawg Jul 24 '18 at 04:28
  • I selected a cell one at a time (the code selects the row by itself, so the user does not need to select the entire row, just a cell of it) and I could delete only one area before I got an error. The user can filter the table, resulting in about 30 non consecutive visible rows lets say, and out of those 30 the user can select, say, 10 rows. I want it to only delete the visible rows and ignore the hidden ones. I am iterating through the selection because I need to delete rows in a backward manner as far as I know or the references will be lost. – rzenva Jul 24 '18 at 04:31
  • I know what's going on, give me a few mins for an answer – ashleedawg Jul 24 '18 at 04:36

2 Answers2

5

I think you've got a couple issues going on here, but one for sure, which might seem counter-intuitive.

When programmatically deleting multiple non-consecutive rows/columns/cells/areas, it's best to do so in reverse.


When you delete a row, Excel shifts the rows beneath it up. Therefore, the subsequent row numbers become easily confused, causing errors or, worse yet, unintentionally lost data.

Example

Imagine you want to delete rows 1, 4, 5 and 7. If you delete them one at a time, starting at the top, then you'll delete row 1, which makes the other rows numbers to delete 3, 4 and 6. Delete 3 and now you need to delete 3 and 5.

To remove rows 1, 4, 5 and 7 one at a time, starting at the top, you'll actually need to delete rows 1, 3, 3, and 4 (yes, you'd be deleting row 3 twice).


There are a couple ways around this:

  1. Delete all the rows at once. You could join each of the selected rows with the Union method and then delete the entire range at one.

Or, my preference:

  1. Iterate through the rows backwards, starting at the bottom of your data and working your way up. Since For..Each loop can't be made to go in reverse, you'll need to switch to a For..Next.

    You can find the last populated row (using column A in my example) with the Range.End property, and then use the Intersect method to compare each row to the user's .Selection of rows and/or cells. If they two intersect, then you can .Delete the row.


Example :

Sub DeleteSelectedRows()
    Dim rw As Long, lastRow As Long, del As Long
    With Workbooks("book1").Sheets("Sheet1")
        lastRow = .Cells(ws.Rows.Count, 1).End(xlUp).Row 'find last row of Column #1 (A)
        For rw = lastRow To 1 Step -1 'loop through rows backwards
            If Not Intersect(Selection, Rows(rw).Cells) Is Nothing Then
                'This row is within the selected worksheets range(s)
                Rows(rw).Delete 'delete row
                del = del + 1 'count deletion (only for troubleshooting)
            End If
        Next rw
    End With
    MsgBox del & " rows were deleted."
End Sub

The above procedure will need some minor changes to adjust for the location of data on your worksheet, but tested perfectly for me.

Note that there are several links in my post above... always read the official documentation before using commands with which you are unfamiliar. This will also help with terminology, as there's a lot of it to get used to! ...such as how you were misusing the term Selection... VBA isn't selecting rows unless you're using the Select method. Common mistake. :-) Good luck!

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
  • Is `ws` declared and set somewhere else? Without it `lastRow = .Cells(ws.Rows.Count, 1).End(xlUp).Row` should error, shouldn't it? – Mark Fitzgerald Jul 24 '18 at 08:31
  • Thanks! I will read through all of it and let you know my results in a couple of hours! – rzenva Jul 24 '18 at 14:48
  • It works! It even worked in a workbook where the original code was not working! The solution was so simple! Thank you very much, Ashlee! You are a huge lifesaver and I learned quite a bit too. I actually have another question, if you got the time can you please head over there and help me as well? [Link](https://stackoverflow.com/questions/51528296/vba-insert-user-defined-amount-of-copies-of-a-row-selected-by-user) – rzenva Jul 25 '18 at 22:00
  • I like this routine because it does do multiple deletions if cells in the same row but different columns are individually selected. Ie B3 and F3 are both selected, row 3 only gets deleted once! – Forward Ed Sep 10 '19 at 15:16
1

Great explanation Ashleedawg, as Mark Fitzgerald said, the ".ws" wasn't not declared and got some error after I declare.

I just erased and worked awesome!

Sub DeleteSelectedRows()
    Dim rw As Long, lastRow As Long, del As Long, ws As Long
    With Planilha12
        lastRow = .Cells(Rows.Count, 3).End(xlUp).Row 'find last row of Column #1 (A)
        For rw = lastRow To 1 Step -1 'loop through rows backwards
            If Not Intersect(Selection, Rows(rw).Cells) Is Nothing Then
                'This row is within the selected worksheets range(s)
                Rows(rw).Delete 'delete row
                del = del + 1 'count deletion (only for troubleshooting)
            End If
        Next rw
    End With
    Range("C19").Select 'select the cell that I want
    Planilha11.Calculate 'calculate the sheet I want
    MsgBox del & " rows were deleted." 
End Sub
Guilherme Matheus
  • 573
  • 10
  • 30