VBA code executes with message box, but not without - vba

I have a Sub in VBA that I need to be able to run multiple times. When I execute this piece of code to find the last row of the sheet:
Dim lastRow As Long
With ActiveSheet
lastRow = xlWs.Cells(.Rows.count, "A").End(xlUp).Row
End With
I get
object variable or with block variable not set
when I execute it a second time, but if I add a message box before the variable set:
Dim lastRow As Long
With ActiveSheet
MsgBox xlWs.Cells(.Rows.count, "A").End(xlUp).Row
lastRow = xlWs.Cells(.Rows.count, "A").End(xlUp).Row
End With
It works no matter how many times I run it. Has anyone run into this issue before? What is wrong with the first set of code, that is corrected with the second set?

You are using xlWs as a worksheet object inside the block of code that you are defining lastrow and I cannot see where you have defined it prior to calling it and that is why you get an error. If by any chance you have set that object in prior code executions it and somehow that worksheet is active you will not get any error message. We just don't have more information on what the other parameters are when you are running the code or how you are running it, but the best question for you here is how to follow a clean and standard definition of objects and using them inside a good code line. I would write a function for last row and make the worksheet and column number optional:
Function LastRowInColumn(Optional sh As Worksheet, Optional colNumber As Long = 1) As Long
'Finds the last row in a particular column which has a value in it
If sh Is Nothing Then
Set sh = ActiveSheet
End If
LastRowInColumn = sh.Cells(sh.Rows.Count, colNumber).End(xlUp).row
End Function
This function will manage setting the proper sheet for you, if you enter the sheet object it will use it, if not then it will use the active sheet. The same goes for the column you want to define the last row, if you enter any column number (e.g. colNumber=3 will be column C) it will find the last row in that column, if you leave it blank, it will consider column A.

I have not tested this, but I've had this problem before.
I think the issue is that you are not qualifying .Rows.count. Try changing it to xlWs.Rows.count. Without it you are getting the row count of the active worksheet.
The dot operator without a preceding object points to the object defined in the With statement, even when it's inside the cells() statement, which is qualified.)
' Try This
Dim lastRow As Long
With ActiveSheet
lastRow = xlWs.Cells(xlWs.Rows.count, "A").End(xlUp).Row
End With
I'm not sure why you are using "With Active Sheet". It looks like you are trying to get the last row in worksheet xlWs, which I assume is a worksheet object.
So, the code could be simplified to this:
' Better yet, try this
Dim lastRow As Long
lastRow = xlWs.Cells(xlWs.Rows.count, "A").End(xlUp).Row
The reason the message box fixed it is because the active worksheet was changed to xlWs by doing .End(xlUp)
' This code changes the active worksheet to worksheet xlWs
' by doing .End(xlUp)
MsgBox xlWs.Cells(.Rows.count, "A").End(xlUp).Row

Related

Wait until application.run is completed

I'm trying to write a macro on one file that opens another workbook, runs a macro to get some data, copies the data into the first workbook and closes the second workbook.
I have however encountered a problem because it seems like the selection of the range is executed while the macro in the second workbook is still running and therefore selects the entire column:
i.e. the ws2.Range(StartCell, ws2.Cells(LastRow, "A")).Select string in the code below selects the whole column, and not just the cells where there is data.
'run macro in IB API file to get portfolio data
Application.Run "TwsDde.xls!Sheet15.subscribeToPorts"
'select data in column A from IB API file
Dim LastRow As Long
Dim StartCell As Range
Set StartCell = Range("A8")
LastRow = ws2.Cells(ws2.Rows.Count, StartCell.Column).End(xlDown).Row
ws2.Range(StartCell, ws2.Cells(LastRow, "A")).Select
Has anyone encountered this problem before or has any ideas on how to solve it?
Your calculation of LastRow is wrong. It should be xlUp instead of xlDown.

run time error 1004 application defined or object defined error - looping over same range in different worksheets

Could you please help me in solving the following issue:
Sub PBLSearch()
Dim PBLRng As Range
Dim PBL As Range
Dim SearchRng As Range
Dim SourceWsNr As Integer
For SourceWsNr = 1 To 2
Debug.Print Workbooks("Book1.xlsx").Sheets(SourceWsNr).Name
Set PBLRng = Workbooks("Book1.xlsx").Sheets(SourceWsNr).Range(Range("A1"), Range("A1").End(xlDown))
For Each PBL In PBLRng.Cells
Debug.Print PBL.Value
Next PBL
Next SourceWsNr
End Sub
The code works fine when the SourceWsNr equals 1, but as soon as this changes to 2, i get the error mentioned in the subject.
Is it because i re-set the PBLRng variable? I couldn't find any solution to this problem...
Thank you very much in advance.
Best Regards,
S. Sz.
The problem is the line
Workbooks("Book1.xlsx").Sheets(SourceWsNr).Range(Range("A1"), Range("A1").End(xlDown))
The Range("A1") and Range("A1").End(xlDown) are referencing the cells on the active worksheet which is why it fails on the second sheet (the arguments for the range method of a sheet have to be on the same sheet). You have to specify the sheet each time when calling Range This should do it:
With ActiveWorkbook.Sheets(SourceWsNr)
Set PBLRng = .Range("A1", .Range("A1").End(xlDown))
End With
Edit: you might also consider using another way to find the last row, like .Cells(.Rows.count,1).End(xlUp). It depends on your specific needs though.

range names not in name manager

Running VBA code allowed me to detect that applying a filter in a sheet creates a range name that does NOT appear in the name manager (the range name either just associates to the headers or the whole table). Is there any way of knowing why this happens and/or a way of preventing it? Is it just an Excel glitch?
the part of the vba code that causes the error I tried to run was the following:
For Each Rng In ActiveWorkbook.Names
Set Rng2 = Range(Rng)
If Not Intersect(Rng2, Range(rng1.Offset(1, 0), rng1.End(xlDown)).EntireRow) Is Nothing Then ActiveWorkbook.Names(Rng.Name).Delete
Next Rng
When I was debugging, I noticed that my Rng (which is a name object, by the way) points towards a range I never created (and I know this because the sheet that it's in has no other range names and I never put any in it)
I used this vba code to verify that the name existed in this sheet:
Sub test()
Dim Rng As Name
For Each Rng In Sheets("WindHail Zone 2").Names
'ActiveWorkbook.Names(Rng.Name).Delete
MsgBox Rng
Next Rng
End Sub
I removed the comment block from the first line of the for loop to the second line to delete it. I removed the filter on that tab and reput it on, only to come to the same issue.
Thanks!
Not so clear to me what your're trying to achieve but as long as "hidden" names and range intersection are involved you must take into account what follows:
filtering does create hidden "names"
but all of them ends with the string "_FilterDatabase"
range intersection wold return an error if applied to ranges not belonging to the same sheet
so here what you should try
For Each Rng In ActiveWorkbook.Names
Set rng2 = Range(Rng)
If rng2.Parent.Name = rng1.Parent.Name And InStr(Rng.Name, "_FilterDatabase") = 0 Then 'the first check is for ranges belonging to the same worksheet and the second is for rng2 not deriving from any filtering
' now you can safely "Intersect"
If Not Intersect(rng2, Range(rng1.Offset(1, 0), rng1.End(xlDown)).EntireRow) Is Nothing Then ActiveWorkbook.Names(Rng.Name).Delete
End If
Next Rng

“Unable to get the VLookup property of the WorksheetFunction Class” vlookup in a loop

I'll just say right off the bat that I'm a newb in coding overall. I've learned a lot along the way creating various things at work for myself and I'm learning all the time.
The problem I have right now is with a loop using application.vlookup(...). I've done a lot of searching around but I gave up because I couldn't get anything to work. I've removed the .WorksheetFunction part from the original code because apparently it doesn't change anything but at least it doesn't give VBA errors when a value is not found and acts similar to the normal function (gives #N/A).
So in the below code I have my data in column A which I need to convert using vlookup on a Query in another worksheet (and put that in the same sheet as the data, in column C).
Sub vlookup()
Dim wsdata As Worksheet
Dim lr As Long
Dim rng As Range
Set wsdata = ThisWorkbook.Worksheets("Data")
wsdata.Columns("C").Delete
wsdata.Range("D1") = "Correct number"
lr = wsdata.Cells(Rows.Count, "A").End(xlUp).Row
Set rng = wsdata.Range("A1:D" & lr)
Dim lrQuery As Long, iNr As Long
Dim wsQuery As Worksheet
Dim LookUpRange As Range
Set wsQuery = ThisWorkbook.Sheets("IDMquery")
lrQuery = wsQuery.Cells(Rows.Count, "A").End(xlUp).Row
Set LookUpRange = wsQuery.Range("D:F")
For iNr = 2 To lr
wsdata.Cells(iNr, 4).Value = Application.VLookup(wsdata.Cells(iNr, 1).Value, LookUpRange, 3, 0)
Next iNr
End Sub
I have to add that there are some issues with the formatting (or whatever they are), because the original data is external. Eg. for "17935" VarType would give 8 (string) instead of a numeric type.
In the spreadsheet it's easy to fix it with the double unary and the following formula works correctly:
=VLOOKUP(--A2,IDMquery!D:F,3,false)
Of course I could use a helper column and just use that, which might be an option if I can't do it any other way, but I would prefer strictly within VBA.
Do you guys know if this is fixable?
Cheers!
EDIT: Btw, I also changed the formatting of column A to "0" in VBA but it doesn't actually want to change these values to numeric, not sure why.

VBA Excel error when code is moved from worksheet to module

I have a Excel file with multiple tabs. I have a worksheet with some code which is working fine. This code also refers to data on some "master" tabs. I need to duplicate this sheet so I moved the common functions from there to a module. Now I get a VBA 1004 error when trying to access a range on the same worksheet.
Dim selectedRange As Range
Set selectedRange = Worksheets(name).Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
This code worked fine till I moved it to a module. It works if I put a
Worksheets(name).Select
before it, but I will have to do it too many times. Based on this query: VBA error 1004 - select method of range class failed
the code should work fine without a .Select. And it does as long as the code is within the worksheet. Why does moving code to a module create a problem?
U use Range and Rows properties without an object qualifier. When used without an object qualifier, this properties are a shortcut for ActiveSheet.Range / ActiveSheet.Rows.
So the code does this:
Worksheets(Name).Range("A1", ActiveSheet.Range("A" & ActiveSheet.Rows.Count).End(xlUp))
But Worksheets(name) could be different from active sheet so better is:
Worksheets(Name).Range("A1", Worksheets(Name).Range("A" & Worksheets(Name).Rows.Count).End(xlUp))
In With-End With block:
With Worksheets(name)
Set selectedRange = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
End With
So it is ensured that the Range/Rows properties are applied on Worksheets(name) worksheet object.
When you do things on a sheet, you do not really need explicit declarations to that sheet.
However when working on a module and interacting with other sheets, you need to specify which Sheet you want to work with. So select the sheet before you can select the range. To say, SELECT PARENT BEFORE YOU SELECT THE CHILDREN :) Please note, following is just the logic explanantion. Not the exact code syntax.
So I suggest you create the following worksheet variable and set your worksheet object that you need into that.
e.g.
Dim WS as Worksheet
Dim selectedRange As Range
Set WS = Sheets("Shee1")
Set selectedRange = WS.Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
Or else if you want to refer to all sheets, you may use each sheet's index
E.g. ThisWorkBook.Sheets(i) 'i is an integer
Then loop or whatever as it deems to your programme structure.
Further you do not have to use Select on the worksheet to point to a range in that worksheet. As per above code you could set the worksheet and set the range you need to process. When optimizing VBA execution, select is usually a taboo. Also Excel 2007 does not retain the active sheet the way older versions used to.