Excel VBA Macro Issue: MoveRowsTo Module Only Moving to One Row
I have a task list in Excel where I need to move rows from an “Open Items” sheet to a “Closed Items” sheet when column G is marked as “Closed”. My VBA macro works, but it’s only showing the most recently closed item in row 2 of the target sheet, rather than maintaining a list of all closed items.
Current Code:
Sub MoveRowsToClosedItems()
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim lastRow As Long
Dim i As Long
' Set the source and target sheets
Set sourceSheet = ThisWorkbook.Worksheets("Open Items")
Set targetSheet = ThisWorkbook.Worksheets("Closed Items")
' Find the last row in the source sheet
lastRow = sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row
' Loop through each row in the source sheet
For i = 2 To lastRow
' Check if cell in column G contains "Closed"
If sourceSheet.Cells(i, "G").Value = "Closed" Then
' Copy the entire row to the target sheet
sourceSheet.Rows(i).Copy Destination:=targetSheet.Cells(targetSheet.Rows.Count, "A").End(xlUp).Offset(1)
' Delete the row from the source sheet
sourceSheet.Rows(i).Delete
' Decrement the loop counter as the rows are shifting up
i = i - 1
' Update the last row value
lastRow = lastRow - 1
End If
Next i
End Sub
Problem:
When I run this macro, it successfully moves rows from the “Open Items” sheet to the “Closed Items” sheet, but only the most recently closed item appears in the target sheet. I need all closed items to be listed in the target sheet.
Desired Outcome:
I want the macro to maintain a complete list of all closed items in the “Closed Items” sheet, with each new closed item being added to the bottom of the list rather than replacing previous entries.
How can I modify my VBA code to ensure all closed items are properly accumulated in the target sheet?
When moving rows from one sheet to another in Excel VBA, the issue of only the last row appearing typically stems from incorrect last row detection or improper row deletion during the loop. Your code is on the right track, but there are several improvements needed to ensure all closed items are properly accumulated in the target sheet.
The main issues in your current code are:
- The last row detection in the target sheet may not work correctly if the sheet is empty or has formatting issues
- Deleting rows during the loop can cause unexpected behavior
- The copying mechanism may not be appending correctly
Contents
- Improved VBA Solution
- Alternative Approach Without Row Deletion
- Debugging and Error Handling
- Best Practices for Row Movement
- Complete Working Solution
Improved VBA Solution
Here’s a modified version of your code that addresses the issues:
Sub MoveRowsToClosedItems()
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim lastRow As Long
Dim targetLastRow As Long
Dim i As Long
' Turn off screen updating for better performance
Application.ScreenUpdating = False
' Set the source and target sheets
Set sourceSheet = ThisWorkbook.Worksheets("Open Items")
Set targetSheet = ThisWorkbook.Worksheets("Closed Items")
' Find the last row in the source sheet
lastRow = sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row
' Find the last row in the target sheet (more reliable method)
On Error Resume Next
targetLastRow = targetSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
If Err.Number <> 0 Then
targetLastRow = 1 ' If no data found, start from row 1
End If
On Error GoTo 0
' Loop through each row in the source sheet (bottom to top to avoid shifting issues)
For i = lastRow To 2 Step -1
' Check if cell in column G contains "Closed"
If sourceSheet.Cells(i, "G").Value = "Closed" Then
' Copy the entire row to the target sheet
sourceSheet.Rows(i).Copy Destination:=targetSheet.Rows(targetLastRow + 1)
' Increment target last row
targetLastRow = targetLastRow + 1
' Delete the row from the source sheet
sourceSheet.Rows(i).Delete Shift:=xlUp
End If
Next i
' Turn screen updating back on
Application.ScreenUpdating = True
MsgBox "Rows moved successfully!", vbInformation
End Sub
Alternative Approach Without Row Deletion
For better performance and reliability, consider this approach that doesn’t delete rows immediately:
Sub MoveRowsToClosedItemsAlternative()
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim lastRow As Long
Dim targetLastRow As Long
Dim i As Long
Dim rowsToDelete As Collection
Dim rowToDelete As Variant
' Turn off screen updating for better performance
Application.ScreenUpdating = False
' Set the source and target sheets
Set sourceSheet = ThisWorkbook.Worksheets("Open Items")
Set targetSheet = ThisWorkbook.Worksheets("Closed Items")
' Initialize collection to store rows to delete
Set rowsToDelete = New Collection
' Find the last row in the source sheet
lastRow = sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row
' Find the last row in the target sheet
On Error Resume Next
targetLastRow = targetSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
If Err.Number <> 0 Then
targetLastRow = 1
End If
On Error GoTo 0
' First pass: identify and copy rows to move
For i = 2 To lastRow
If sourceSheet.Cells(i, "G").Value = "Closed" Then
' Add row index to collection (store as string to avoid type issues)
rowsToDelete.Add i
' Copy the row to target sheet
sourceSheet.Rows(i).Copy Destination:=targetSheet.Rows(targetLastRow + 1)
targetLastRow = targetLastRow + 1
End If
Next i
' Second pass: delete rows from source (in reverse order to avoid shifting issues)
For Each rowToDelete In rowsToDelete
sourceSheet.Rows(rowToDelete).Delete Shift:=xlUp
Next rowToDelete
' Turn screen updating back on
Application.ScreenUpdating = True
MsgBox "Successfully moved " & rowsToDelete.Count & " rows to Closed Items!", vbInformation
End Sub
Debugging and Error Handling
If you’re still experiencing issues, add debugging to help identify the problem:
Sub MoveRowsWithDebug()
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim lastRow As Long
Dim targetLastRow As Long
Dim i As Long
Dim movedCount As Long
movedCount = 0
' Set the source and target sheets
Set sourceSheet = ThisWorkbook.Worksheets("Open Items")
Set targetSheet = ThisWorkbook.Worksheets("Closed Items")
' Debug information
Debug.Print "Starting row movement..."
Debug.Print "Source sheet last row: " & sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row
' Find the last row in the target sheet
On Error Resume Next
targetLastRow = targetSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
If Err.Number <> 0 Then
targetLastRow = 1
Debug.Print "Target sheet is empty, starting from row 1"
Else
Debug.Print "Target sheet last row: " & targetLastRow
End If
On Error GoTo 0
' Loop through each row in the source sheet (bottom to top)
For i = sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row To 2 Step -1
Debug.Print "Checking row " & i & ", value in G: " & sourceSheet.Cells(i, "G").Value
If sourceSheet.Cells(i, "G").Value = "Closed" Then
' Copy the entire row to the target sheet
targetSheet.Rows(targetLastRow + 1).Value = sourceSheet.Rows(i).Value
' Increment counter
movedCount = movedCount + 1
targetLastRow = targetLastRow + 1
Debug.Print "Moved row " & i & " to target row " & targetLastRow
' Delete the row from the source sheet
sourceSheet.Rows(i).Delete Shift:=xlUp
End If
Next i
Debug.Print "Total rows moved: " & movedCount
MsgBox "Moved " & movedCount & " rows successfully!", vbInformation
End Sub
Best Practices for Row Movement
When working with row movement in Excel VBA, follow these best practices:
- Loop backwards: Always loop from bottom to top when deleting rows to avoid shifting issues.
- Use reliable last row detection: The
Findmethod is more reliable thanEnd(xlUp)for detecting the last row. - Turn off screen updating: This improves performance significantly for large datasets.
- Use collections for complex operations: When you need to track multiple rows, use a collection.
- Consider using values instead of copy: For better performance, copy values rather than formatting.
- Add error handling: Include error handling to prevent crashes if sheets don’t exist.
Complete Working Solution
Here’s the most robust solution that combines all the best practices:
Sub MoveRowsToClosedItemsFinal()
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim lastRow As Long
Dim targetLastRow As Long
Dim i As Long
Dim movedCount As Long
On Error GoTo ErrorHandler
' Turn off screen updating for better performance
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
movedCount = 0
' Set the source and target sheets with error handling
Set sourceSheet = ThisWorkbook.Worksheets("Open Items")
Set targetSheet = ThisWorkbook.Worksheets("Closed Items")
' Find the last row in the source sheet
lastRow = sourceSheet.Cells(sourceSheet.Rows.Count, "G").End(xlUp).Row
If lastRow < 2 Then
MsgBox "No data found in Open Items sheet", vbExclamation
Exit Sub
End If
' Find the last row in the target sheet
On Error Resume Next
targetLastRow = targetSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
If Err.Number <> 0 Then
targetLastRow = 1 ' If no data found, start from row 1
End If
On Error GoTo 0
' Loop through each row in the source sheet (bottom to top)
For i = lastRow To 2 Step -1
' Check if cell in column G contains "Closed"
If sourceSheet.Cells(i, "G").Value = "Closed" Then
' Copy only values (faster and avoids formatting issues)
targetSheet.Rows(targetLastRow + 1).Value = sourceSheet.Rows(i).Value
' Increment counter and target last row
movedCount = movedCount + 1
targetLastRow = targetLastRow + 1
' Delete the row from the source sheet
sourceSheet.Rows(i).Delete Shift:=xlUp
End If
Next i
' Restore Excel settings
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "Successfully moved " & movedCount & " rows to Closed Items!", vbInformation
Exit Sub
ErrorHandler:
' Restore Excel settings in case of error
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "Error occurred: " & Err.Description, vbCritical
End Sub
This final solution addresses all the common issues with row movement in Excel VBA and should reliably move all closed items to your target sheet while maintaining a complete list of all closed items.