Getting error 'object variable or With block variable not set' when trying to run sub - excel

I did not create the code but am trying to troubleshoot an excel file and the original author is not available (layed off from company and not willing to help).
The following line is generating the error, 'object variable or With block variable not set'
Private Sub Workbook_Open()
Sheet1.Starttimer
End Sub
I looked at Sheet1 code and found the below, so I'm not sure what the problem is:
Sub Starttimer()
Application.DisplayAlerts = False
If Not Sheet4.ListObjects(1).DataBodyRange Is Nothing Then
Sheet4.ListObjects(1).DataBodyRange.Rows.Delete
End If
ActiveWorkbook.RefreshAll
Application.Calculate
SetProductionZeros
ActiveWorkbook.Save
ThisWorkbook.Close
End Sub
UPDATE
After setting the debug to break on all errors, the line that causes the error appears to be "r = Sheet4.ListObjects(1).DataBodyRange.Rows.Count" from the sub below:
Sub SetProductionZeros()
Dim tb1 As ListObject
Dim x As Long
Dim y As Long
Dim r As Long
Dim c As Long
'Set path for Table variable'
Set tb1 = Sheet4.ListObjects(1)
Sheet4.Activate
r = Sheet4.ListObjects(1).DataBodyRange.Rows.Count
c = Sheet4.ListObjects(1).DataBodyRange.Columns.Count
'Loop Through Each DataBody Row in Table
For y = 1 To r
'Loop Through Each Column in Table
For x = 1 To c
If IsEmpty(Sheet4.ListObjects(1).DataBodyRange.Cells(y, x)) Then Sheet4.ListObjects(1).DataBodyRange.Cells(y, x) = 0
Next x
Next y
Sheet4.Columns(5).EntireColumn.Delete
Dim lastrow As Long, lastcol As Long, thiscol As Long
Dim totalrow As Long, totalcol As Long, thisrow As Long
totalrow = 7 + Sheet4.ListObjects(1).Range.Rows.Count
totalcol = 2 + Sheet4.ListObjects(1).Range.Columns.Count
On Error GoTo Errorcatch
'lastrow = Cells(Rows.Count, 1).End(xlUp).row
'lastcol = Cells(1, Columns.Count).End(xlToLeft).Column
Sheet4.Cells(totalrow, 3).Value = "Total"
For thiscol = 5 To totalcol - 1
Sheet4.Cells(totalrow, thiscol).Select
ActiveCell.Value = WorksheetFunction.Sum(Sheet4.Range(Sheet4.Cells(1, ActiveCell.Column), ActiveCell))
Next
Sheet4.Rows(totalrow).Font.Bold = True
Sheet4.Cells(7, totalcol).Value = "Total"
For thisrow = 8 To totalrow
Sheet4.Cells(thisrow, totalcol).Select
ActiveCell.Value = WorksheetFunction.Sum(Sheet4.Range(Sheet4.Cells(ActiveCell.row, 5), ActiveCell))
Next
Sheet4.Columns(totalcol).Font.Bold = True
'Sheet4.Columns(2).HorizontalAlignment = xleft
For y = totalrow To 8 Step -1
If Sheet4.Cells(y, 2) = "T" And Sheet4.Cells(y, totalcol).Value = 0 Then
Sheet4.Rows(y).EntireRow.Delete
End If
Next
Exit Sub
Errorcatch:
MsgBox Err.Description
End Sub

Follow the logic:
When you open the workbook, you call Sheet1.StartTimer
Sheet1.StartTimer includes
If Not Sheet4.ListObjects(1).DataBodyRange Is Nothing Then
Sheet4.ListObjects(1).DataBodyRange.Rows.Delete
End If
At this point Sheet4.ListObjects(1).DataBodyRange will be Nothing (because you deleted all its rows)
Then you call SetProductionZeros
SetProductionZeros includes r = Sheet4.ListObjects(1).DataBodyRange.Rows.Count
But because Sheet4.ListObjects(1).DataBodyRange is Nothing this throws an error. (Same applies to .Columns.Count)
You can wrap references to DataBodyRange in
If Not Sheet4.ListObjects(1).DataBodyRange Is Nothing Then
' ...
End If
but you need to consider what you want to achieve when there are no rows in Sheet4.ListObjects(1)

This error seems to indicate that you are assigning an object to r without set. Nothing is an object. So in your case you are likely getting Nothing from Sheet4.ListObjects(1).DataBodyRange.Rows.Count. After Set tb1 = Sheet4.ListObjects(1), verify that tb1 is not nothing.
FYI, For code clarity, you should be using r = tb1.DataBodyRange.Rows.Count (same for c =).

Related

Looping through a range to find a value

I have a worksheet that has columns 1-8, rows 3 through the last row. I would like to loop through each cell to find out if a value of 1 is present. If it is then that row is copied and inserted for each value of 1, additionally that new row will have a text inserted in cell (13,row) then moved to the next row. This is as far as I got....thanks!
Sub Workcenter()
Application.ScreenUpdating = False
On Error Resume Next
Application.DisplayAlerts = False
On Error GoTo 0
Dim Test As Worksheet
Set TS = Worksheets("Test")
Application.DisplayAlerts = True
For k = 1 To 8
For j = 4 To TS.Cells(Rows.Count, k).End(xlUp).Row
If TS.Cells(j, k).Value = 1 Then TS.Cells.Activate
'TS.Cells.Activate.Row.Select
Rows(ActiveCell.Row).Select
Selection.Copy
Selection.Insert Shift:=xlDown
'ShopOrderNumRow = j
Next j
Next k
End Sub
Will try giving some example knowing that I still don't understand how the inserting is occurring for each cell of a row.
Providing more detail, or example of before/after in your post may help.
As for an example, since you're marking only a single cell in each row, I would suggest Find() for value of 1 to determine if you need to write to that specific cell.
'untested code
sub test()
toggle false
dim rowNum as long
for rowNum = firstRow to lastRow Step 1
with sheets(1)
with .range(.cells(rowNum,1),.cells(rowNum,8))
dim foundCell as range
set foundCell = .find(1)
if not foundCell is nothing then .cells(rowNum,13).value = "text"
end with
end with
next iterator
toggle true
end sub
private sub toggle(val as boolean)
with application
.screenupdating = val
.enableevents = val
end with
end sub
Edit1: Looks like countif() may be the saviour here.
Edit2: Tested code input (untested code part of Edit1)
Sub test()
Dim lastRow As Long: lastRow = 10
Dim firstRow As Long: firstRow = 1
toggle False
Dim rowNum As Long
For rowNum = lastRow To firstRow Step -1
With Sheets(1)
Dim countRange As Range
Set countRange = .Range(.Cells(rowNum, 1), .Cells(rowNum, 8))
Dim countOfOnes As Long
countOfOnes = Application.CountIf(countRange, 1)
If countOfOnes > 0 Then
With .Rows(rowNum)
.Copy
.Offset(1).Resize(countOfOnes).Insert Shift:=xlDown
End With
.Cells(rowNum, 13).Value = "text"
End If
End With
Next rowNum
toggle True
End Sub
Private Sub toggle(val As Boolean)
With Application
.ScreenUpdating = val
.EnableEvents = val
End With
End Sub
Tested using this data:
Output from running code:

Excel VBA - Delete empty columns between two used ranges

I would like to delete all empty columns between 2 used ranges, based on the screenshot:
However, these two used ranges may have varying column length, thus the empty columns are not always Columns D to K.
Here is my code:
Sub MyColumns()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Workbooks.Open ("BOOK2.xlsx")
Workbooks("BOOK2.xlsx").Activate
Workbooks("BOOK2.xlsx").Sheets(1).Activate
Workbooks("BOOK2.xlsx").Sheets(1).Cells(1, 4).Value = "NON-EMPTY"
Dim finalfilledcolumn As Long
finalfilledcolumn = Workbooks("BOOK2.xlsx").Sheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
Dim iCol As Long
Dim i As Long
iCol = firstfilledcolumn + 1
'Loop to delete empty columns
For i = 1 To firstfilledcolumn + 1
Columns(iCol).EntireColumn.Delete
Next i
Workbooks("BOOK2.xlsx").Close SaveChanges:=True
MsgBox "DONE!"
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
However, the empty columns still remain.
Do note that the last filled column for the first used range, Place = "USA", Price = "110" and Category = "Mechanical" may not be fixed at Column C, but could go to Column D, E, etc.
Many thanks!
Please, try the next way:
Sub deleteEmptyColumn()
Dim sh As Worksheet, lastCol As Long, rngColDel As Range, i As Long
Set sh = ActiveSheet 'use here your necessary sheet, having the workbook open
'if not open, you can handle this part...
lastCol = sh.cells(1, sh.Columns.count).End(xlToLeft).column
For i = 1 To lastCol
If WorksheetFunction.CountA(sh.Columns(i)) = 0 Then
If rngColDel Is Nothing Then
Set rngColDel = sh.cells(1, i)
Else
Set rngColDel = Union(rngColDel, sh.cells(1, i))
End If
End If
Next i
If Not rngColDel Is Nothing Then rngColDel.EntireColumn.Delete
End Sub
Try this ..
Dim rng As Range, i As Long
Set rng = Workbooks("BOOK2.xlsx").Sheets(1).UsedRange
For i = rng.Columns.Count To 1 Step -1
If WorksheetFunction.CountA(rng.Columns(i)) = 0 Then
rng.Columns(i).EntireColumn.Delete
End If
Next i

Passing a dynamic range to charts

I want to check the status of a sheet and when changed automatically run some calculations. I also wish refresh a graph with the new data from that sheet.
I used the Worksheet_Change function. It calls the sub with the calculations and calls the sub that contains the chart modification code. They run as planned with one exception. The range that gets passed to the Chrt1 sub (responsible for the chart functionality) does not get updated on the graph once it has been called out for the first time.
I'm aware that this can be overcome with Excel built-in tables function but I'd like to code this simple routine in anyways.
The Worksheet_Change function:
Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
AutoChangeTest
Application.EnableEvents = True
End Sub
The main module code:
Sub AutoChangeTest()
Dim s1 As Worksheet, s2 As Worksheet
Dim i As Integer, j As Integer, lrow As Integer, lrow2 As Integer
Set s1 = Sheets("Arkusz3")
On Error GoTo Err1
lrow = s1.Cells(s1.Rows.Count, 1).End(xlUp).Row
For i = 1 To lrow
s1.Cells(i, 2) = s1.Cells(i, 1) * 2
Next
Call Chrt1(Range(s1.Cells(1, 1), s1.Cells(lrow, 2)), s1)
Err1:
If Not IsNumeric(s1.Cells(i, 1)) Then
s1.Cells(i, 1).Activate
End If
End Sub
Sub Chrt1(r1 As Range, s1 As Worksheet)
Dim c1 As Shape
Dim s As Worksheet
Dim cht As ChartObject
Dim i As Integer
i = 0
Set r = r1
Set s = s1
For Each cht In s.ChartObjects
i = i + 1
Next
If i = 0 Then
Set c1 = s.Shapes.AddChart
End If
c1.Chart.SetSourceData (r)
End Sub
Some suggestions in the code below:
Sub AutoChangeTest()
Dim ws As Worksheet 'avoid variable names with 1/l - too unclear
Dim i As Long, lrow As Long 'always use long over integer
Set ws = ThisWorkbook.Worksheets("Arkusz3")
lrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).row
On Error GoTo exitHere
Application.EnableEvents = False 'don't re-trigger this sub...
For i = 1 To lrow
With ws.Cells(i, 1)
'easier to test than to trap an error if non-numeric
If IsNumeric(.Value) Then
ws.Cells(i, 2) = .Value * 2
Else
ws.Select
.Select
MsgBox "Non-numeric value found!"
GoTo exitHere 'acceptable use of Goto I think
End If
End With
Next
'don't think you need a separate method for this...
If ws.ChartObjects.Count = 0 Then ws.Shapes.AddChart 'no need to loop for a count
'assuming there will only be one chart...
ws.ChartObjects(1).Chart.SetSourceData ws.Range(ws.Cells(1, 1), ws.Cells(lrow, 2))
exitHere:
If Err.Number <> 0 Then Debug.Print Err.Description
Application.EnableEvents = True
End Sub
In your Chrt1 procedure, this bit
For Each cht In s.ChartObjects
i = i + 1
Next
If i = 0 Then
Set c1 = s.Shapes.AddChart
End If
can be replaced by the following:
If s.ChartObjects.Count = 0 Then
Set c1 = s.Shapes.AddChart
End If
But what is c1 if you don't have to add a chart? You haven't defined it, and the On Error means you never find out that it's broken.
Assuming you want the last chart object to be the one that is changed:
If s.ChartObjects.Count = 0 Then
Set c1 = s.Shapes.AddChart
Else
Set c1 = s.ChartObjects(s.ChartObjects.Count)
End If
And you should declare c1 as a ChartObject.
Finally, remove the parentheses around r in this line:
c1.Chart.SetSourceData r
Thank you all for support. The basic code that works is shown below. It isn't the best looking but it does the job.
Sub Chrt1(r1 As Range, s1 As Worksheet)
Dim c1 As Shape
Dim s As Worksheet
Dim cht As ChartObject
Dim i As Integer
i = 0
Set r = r1
Set s = s1
For Each cht In s.ChartObjects
i = i + 1
Next
If i = 0 Then
Set c1 = s.Shapes.AddChart
End If
Set cht = s.ChartObjects(1)
cht.Chart.SetSourceData Source:=r
End Sub

Creating new sheet works only first and for the next data it throws an error 'Run Time Error 9'

I am trying to create a program that will copy a row based on the value in column P into another sheet in the same workbook. Column P can be:
Design
Production
Process
Safety
Quality
Purchasing
I want the program to look at the Column P and if it says "design" then copy and paste that row into the sheet labeled "Design" and so on and so forth.
Can anyone help me?
Line
Set tosheet = Worksheets("" & fromsheet.Cells(r, "P"))
works fine initially then throw off an error of 'Run Time Error 9 after the first iteration.
Sub lars_ake_copy_rows_to_sheets()
Dim firstrow, lastrow, r, torow As Integer
Dim fromsheet, tosheet As Worksheet
firstrow = 2
Set fromsheet = ActiveSheet
lastrow = ActiveSheet.Cells(Rows.Count, "P").End(xlUp).Row
For r = firstrow To lastrow
If fromsheet.Cells(r, "P") <> "" Then 'skip rows where column P is empty
On Error GoTo make_new_sheet
Set tosheet = Worksheets("" & fromsheet.Cells(r, "P"))
On Error GoTo 0
GoTo copy_row
make_new_sheet:
Set tosheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
tosheet.Name = fromsheet.Cells(r, "P")
copy_row:
torow = tosheet.Cells.SpecialCells(xlCellTypeLastCell).Row + 1
fromsheet.Cells(r, 1).EntireRow.Copy
tosheet.Cells(torow, 1).PasteSpecial Paste:=xlPasteValues
End If
Next r
Application.CutCopyMode = False
fromsheet.Activate
End Sub
I want this code to create new worksheet if already not created.
But this code create new sheet for only 1st record of column p which is design, if this sheet not created before but for the next record which is Production if the worksheet by the name of Production is not created before then this code throw an error of Run Time 9. Anyone who can fix this for me.
As I mentioned in my comment, you are not properly handling the "Going out of the error handler". You can look Good Patterns For VBA Error Handling for some details on how handling errors.
This code should solve your problem (but I didn't test it)
Sub lars_ake_copy_rows_to_sheets()
Dim firstrow As Long, lastrow As Long, r As Long, torow As Long
Dim fromsheet As Worksheet, tosheet As Worksheet
firstrow = 2
Set fromsheet = ActiveSheet
lastrow = ActiveSheet.Cells(Rows.Count, "P").End(xlUp).Row
For r = firstrow To lastrow
If fromsheet.Cells(r, "P") <> "" Then 'skip rows where column P is empty
On Error GoTo make_new_sheet
Set tosheet = Worksheets("" & fromsheet.Cells(r, "P"))
On Error GoTo 0
torow = tosheet.Cells.SpecialCells(xlCellTypeLastCell).Row + 1
fromsheet.Cells(r, 1).EntireRow.Copy
tosheet.Cells(torow, 1).PasteSpecial Paste:=xlPasteValues
End If
Next r
Application.CutCopyMode = False
fromsheet.Activate
Exit Sub
make_new_sheet:
Set tosheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
tosheet.Name = fromsheet.Cells(r, "P")
resume next
End Sub

Remove rows if existing in another sheet

I'm trying to search sheet_A for values in sheet_B / column A (starting from A2) and if they exist in sheet_A (column C, starting in C2) they get removed from sheet_A.
Sub Remover_Duplicados()
'Backup to another sheet
Const strSheetName As String = "BKP_sheet"
Set wsTest = Nothing
On Error Resume Next
Set wsTest = ActiveWorkbook.Worksheets(strSheetName)
On Error GoTo 0
If wsTest Is Nothing Then
Worksheets.Add.Name = strSheetName
End If
Sheets("sheet_A").Range("A1:BK3500").Copy Destination:=Sheets(strSheetName).Range("A1")
'Search and destroy
Dim searchableRange As Range
Dim toRemoveRange As Range
Dim lLoop As Long
Set searchableRange = Worksheets("sheet_B").Range("A2", "A3500")
Set toRemoveRange = Worksheets("sheet_A").Range("C2", "C3500")
For lLoop = searchableRange.Rows.Count To 2 Step -1
If WorksheetFunction.CountIf(searchableRange, toRemoveRange(lLoop).Value) > 0 Then
Worksheets("sheet_A").Rows(lLoop).Delete shift:=xlUp
End If
Next lLoop
End Sub
Sheet A, B and the result:
Some don't get removed.
I've gone through your code and amended it slightly to be more dynamic with the ranges, I've also used an Array to populate the values to be removed and then looped though that array to decide whether the row should be deleted or not:
Sub Remover_Duplicados()
'Backup to another sheet
Const strSheetName As String = "BKP_sheet"
Dim wsA As Worksheet: Set wsA = ThisWorkbook.Worksheets("Sheet_A")
Dim wsB As Worksheet: Set wsB = ThisWorkbook.Worksheets("Sheet_B")
Dim arrToRemove()
Set wsTest = Nothing
On Error Resume Next
Set wsTest = ThisWorkbook.Worksheets(strSheetName)
On Error GoTo 0
If wsTest Is Nothing Then
Worksheets.Add.Name = strSheetName
End If
LastRowA = wsA.Cells(wsA.Rows.Count, "A").End(xlUp).Row
wsA.Range("A1:BK" & LastRowA).Copy Destination:=Sheets(strSheetName).Range("A1")
LastRowB = wsB.Cells(wsB.Rows.Count, "A").End(xlUp).Row
arrToRemove = wsB.Range("A2:A" & LastRowB).Value
For iRow = LastRowA To 2 Step -1
For iArray = LBound(arrToRemove) To UBound(arrToRemove)
If wsA.Cells(iRow, "C").Value = arrToRemove(iArray, 1) Then
wsA.Rows(iRow).EntireRow.Delete shift:=xlUp
End If
Next iArray
Next iRow
End Sub

Resources