Thanks for reading my post. I'm new to Excel VBA and have run into a wall debugging a call to Find(). I've gone through several posts on this site and others but so far each fix I've tried has been unsuccessful.
I am writing code to process elements out of financial reports. Each report contains one or more multi-row & multi-column blocks of cells with details describing a project. The size of each block isn't consistent, but each always begins in the top left with "Client Name". So I want to iterate through these blocks keying off that text, then pulling out needed elements.
There's no while loop here yet, as I'm running into the error just setting up the first condition.
Run-time error '91': Object variable or With block variable not set
Here's the section of code from within the Sub, with the error coming in the final line assigning cursorProject:
' store the next report to process
Dim nextReport As String
Dim sourceSheetName As String
Dim sheetSource As Worksheet
nextReport = rptMedia
' copy the worksheet into rptBurn and get that worksheet's name
sourceSheetName = GetSheet(nextReport)
Set sheetSource = Workbooks(rptBurn).Worksheets(sourceSheetName)
sheetSource.Cells.EntireRow.Hidden = False
sheetSource.Cells.EntireColumn.Hidden = False
Workbooks(rptBurn).Activate
' process the sheetSource into sheetCurrent
' set constants
Const constCursorKey As String = "Client Name"
Const constClientColumn As String = "B"
Const constClientNameOffset As Integer = 2
Const constProjectLeft As Integer = 2
Const constProjectRight As Integer = 52
' get range in Client Name column of project entries
Dim cursorStart As Long
Dim cursorEnd As Long
Dim cursorProject As Range
Dim rangeProject As Range
Dim rangeSearch As Range
cursorStart = sheetSource.Columns(2).Find(constCursorKey).Row + constClientNameOffset
' find the last project entry in the sheet
cursorEnd = sheetSource.Range("B" & Rows.Count).End(xlUp).Row
Set rangeSearch = sheetSource.Range(Cells(cursorStart + 1, constProjectLeft), _
Cells(cursorEnd, constProjectLeft))
cursorProject = rangeSearch.Find(What:=constCursorKey, LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, _
SearchFormat:=False)
This is very sloppy currently as parts will be moved out to its own Sub called while iterating over the reports (hence nextReport is hardcoded here to a specific report name). The various constants are fixed parameters of the reports. The unlisted values like "rptBurn" are globals. The GetSheet function works well, but if you want to see it:
Private Function GetSheet(rpt As String) As String
Workbooks.Open rootPath + rpt
ActiveSheet.Copy after:=Workbooks(rptBurn).Sheets(Workbooks(rptBurn).Sheets.Count)
GetSheet = ActiveSheet.Name
Workbooks(rpt).Close
End Function
I've tried several variations on this. The Locals all look promising up to the error. I set the Hidden properties to False based on another post. I've tried simplifying the call down to the basics and using With, like this:
Set rangeSearch = Sheets(3).Range("B:B")
rangeSearch.Select
With rangeSearch
cursorProject = .Find("Client Name")
End With
But I'm always getting an error on cursorProject. There are definitely many "Client Name" entries in the worksheet I'm testing. I put in the Select to verify I'm grabbing the correct range; oddly I find that "B:AX" get highlighted (AX is the rightmost used column in the report) in the simple version, but the selection I expect in the original. Regardless there are "Client Name" instances in either selection--I can select B4 and see "Client Name".
What am I doing wrong?
Cursorproject is an object variable (range). You can't simply assign a value to an object variable, you have to set it.
dim strSomeTextVarible as string
dim rngSomeCellsObjectVariable as range
strSomeTextVarible = "abc"
set rngSomeCellsObjectVariable = range("a1:c3")
Related
I'm currently developing a function that will search for the row number based on a string input that I have. In the following function however I get the error as stated in the title and i have no idea what to do about this :L
Here is the Find Row Function
Function MPNTRowECU(ByVal ECUVariantName) As Range
Debug.Print ECUVariantName
Dim wsMPNT As Worksheet
Set wsMPNT = Worksheets("Module Part Number Tracker")
MPNTRowECU = wsMPNT.Range("C:C").Find(What:=ECUVariantName, LookIn:=xlValues, SearchOrder:=xlByRows)
End Function
The Debug.Print line is there to make sure that ECUVariantName is indeed a string and it does return the string that i have everytime. At the MPNTRowECU is where I get the error. Any suggestions guys? Fairly new to VBA
Try the Function code below, instead of returning the Range, I modifed your Function to return the Row number (as described in your post).
Also, you need to make sure, that Find is successful , by using the If Not MPNTRng Is Nothing Then criteria.
Code
Function MPNTRowECU(ByVal ECUVariantName) As Long
Dim MPNTRng As Range
Debug.Print ECUVariantName
Dim wsMPNT As Worksheet
Set wsMPNT = Worksheets("Module Part Number Tracker")
Set MPNTRng = wsMPNT.Range("C:C").Find(What:=ECUVariantName, LookIn:=xlValues, SearchOrder:=xlByRows)
If Not MPNTRng Is Nothing Then ' confirm that Find was successful
MPNTRowECU = MPNTRng.Row
Else ' Find failed to find "ECUVariantName"
MPNTRowECU = -10000 ' just put a high negative value to raise an error
End If
End Function
Trying to hammer out bugs in my code. Currently trying to do some very simple, open worksheet, copy and paste data over. Trying to do it all without using .Select or .Activate. Hitting "Application-defined or Object defined error", which, from reading the other threads on the matter, probably means that my statements aren't fully qualified. However, I can't figure out how they're not fully qualified - other posts on the topic seem to be missing a "." somewhere in the code, but my attempts to fix it haven't gotten anywhere. Heavily truncated code as follows (If you don't see it dimmed/defined, it's elsewhere)
Sub CopyPaste()
Dim CitiReportEUR As Workbook
Dim CitiReportPathEUR As String
CitiReportPathEUR = Range("CitiReportPathEUR")
Workbooks.Open Filename:=CitiReportPathEUR
Set CitiReportEUR = ActiveWorkbook
LastRowCiti = CitiReportEUR.Sheets(1).Range("I" & Rows.Count).End(xlUp).Row
Set RngCitiEUR = CitiReportEUR.Sheets(1).Range("A1:CT" & LastRowCiti).SpecialCells(xlCellTypeVisible)
Set CabReport.Sheets("CITI").Range("C1").Resize(RngCitiEUR.Rows.Count).Value = RngCitiEUR.Value
End Sub
Currently the error is occurring when I define the range. I've had problems historically with pasting into the range as well... but that's an issue for when I can actually get the code to run that far!
Rows.Count is implicitly working with the ActiveSheet.
The use of Set when assigning values to the Value property of a Range is inappropriate. Set should only be used when assigning a reference to an object.
The Resize probably needs to cater for the number of columns in the source as well as rows.
This code is more explicit:
Sub CopyPaste()
Dim CitiReportEUR As Workbook
Dim CitiReportPathEUR As String
CitiReportPathEUR = Range("CitiReportPathEUR")
Set CitiReportEUR = Workbooks.Open(Filename:=CitiReportPathEUR)
With CitiReportEUR.Sheets(1)
LastRowCiti = .Range("I" & .Rows.Count).End(xlUp).Row
Set RngCitiEUR = .Range("A1:CT" & LastRowCiti).SpecialCells(xlCellTypeVisible)
End With
CabReport.Sheets("CITI").Range("C1").Resize(RngCitiEUR.Rows.Count, RngCitiEUR.Columns.Count).Value = RngCitiEUR.Value
End Sub
I've started to use Macros this weekend (I tend to pick up quickly in regards to computers). So far I've been able to get by with searching for answers when I have questions, but my understanding is so limited I'm to a point where I'm no longer understanding the answers. I am writing a function using VBA for Excel. I'd like the function to result in a range, that can then be used as a variable for another function later. This is the code that I have:
Function StartingCell() As Range
Dim cNum As Integer
Dim R As Integer
Dim C As Variant
C = InputBox("Starting Column:")
R = InputBox("Starting Row:")
cNum = Range(C & 1).Column
Cells(R, cNum).Select
The code up to here works. It selects the cell and all is well in the world.
Set StartingCell = Range(Cell.Address)
End Function
I suppose I have no idea how to save this location as the StartingCell(). I used the same code as I had seen in another very similar situation with the "= Range(Cell.Address)." But that's not working here. Any ideas? Do I need to give more information for help? Thanks for your input!
Edit: I forgot to add that I'm using the InputBox to select the starting cell because I will be reusing this code with multiple data sets and will need to put each data set in a different location, each time this will follow the same population pattern.
Thank you A.S.H & Shai Rado
I've updated the code to:
Function selectQuadrant() As Range
Dim myRange As Range
Set myRange = Application.InputBox(Prompt:="Enter a range: ", Type:=8)
Set selectQuadrant = myRange
End Function
This is working well. (It appears that text is supposed to show "Enter a range:" but it only showed "Input" for the InputBox. Possibly this could be because I'm on a Mac?
Anyhow. I was able to call the function and set it to a new variable in my other code. But I'm doing something similar to set a long (for a color) so I can select cells of a certain color within a range but I'm getting all kinds of Object errors here as well. I really don't understand it. (And I think I'm dealing with more issues because, being on a mac, I don't have the typical window to edit my macros. Just me, basically a text box and the internet.
So. Here also is the Function for the Color and the Sub that is using the functions. (I've edited both so much I'm not sure where I started or where the error is.)
I'm using the functions and setting the variables to equal the function results.
Sub SelectQuadrantAndPlanets()
Dim quadrant As Range
Dim planetColor As Long
Set quadrant = selectQuadrant()
Set planetColor = selectPlanetColor() '<This is the row that highlights as an error
Call selectAllPlanets(quadrant, planetColor)
End Sub
This is the function I'm using to select the color that I want to highlight within my range
I would alternately be ok with using the interior color from a range that I select, but I didn't know how to set the interior color as the variable so instead I went with the 1, 2 or 3 in the input box.
Function selectPlanetColor() As Long
Dim Color As Integer
Color = InputBox("What Color" _
& vbNewLine & "1 = Large Planets" _
& vbNewLine & "2 = Medium Planets" _
& vbNewLine & "3 = Small Planets")
Dim LargePlanet As Long
Dim MediumPLanet As Long
Dim smallPlanet As Long
LargePlanet = 5475797
MediumPlanet = 9620956
smallPlanet = 12893591
If Color = 1 Then
selectPlanetColor = LargePlanet
Else
If Color = 2 Then
selectPlanetColor = MediumPlanet
Else
If Color = 3 Then
selectPlanetColor = smallPlanet
End If
End If
End If
End Function
Any help would be amazing. I've been able to do the pieces individually but now drawing them all together into one sub that calls on them is not working out well for me. Thank you VBA community :)
It's much simpler. Just
Set StartingCell = Cells(R, C)
after getting the inputs, then End Function.
The magic of the Cells method is it accepts, for its second parameter, both a number or a character. That is:
Cells(3, 4) <=> Cells(3, "D")
and
Cells(1, 28) <=> Cells(3, "AB")
One more thing, you can prompt the user directly to enter a range, with just one input box, like this:
Dim myRange as Range
Set myRange = Application.InputBox(Prompt:="Enter a range: ", Type:=8)
The Type:=8 specifies the input prompted for is a Range.
Last thing, since you are in the learning process of VBA, avoid as much as possible:
using the Select and Activate stuff
using unqualified ranges. This refers to anywhere the methods Cells(..) or Range(..) appear without a dot . before them. That usually leads to some random issues, because they refer to the ActiveSheet, which means the behavior of the routine will depend on what is the active worksheet at the moment they run. Avoid this and always refer explicitly from which sheet you define the range.
Continuing your line of thought of selecting the Range bu Selecting the Column and Row using the InputBox, use the Application.InputBox and add the Type at the end to restrict the options of the user to the type you want (Type:= 1 >> String, Type:= 2 >> Number).
Function StartingCell Code
Function StartingCell() As Range
Dim cNum As Integer
Dim R As Integer
Dim C As Variant
C = Application.InputBox(prompt:="Starting Column:", Type:=2) '<-- type 2 inidcates a String
R = Application.InputBox(prompt:="Starting Row:", Type:=1) '<-- type 1 inidcates a Number
Set StartingCell = Range(Cells(R, C), Cells(R, C))
End Function
Sub TestFunc Code (to test the function)
Sub TestFunc()
Dim StartCell As Range
Dim StartCellAddress As String
Set StartCell = StartingCell '<-- set the Range address to a variable (using the function)
StartCellAddress = StartCell.Address '<-- read the Range address to a String
End Sub
This code feels like Schrodinger is executing it. If I open the project and run the code, I won't get any errors at all. If I view the code to edit or add anything, the first time I run the code, I get 'Run-time error 91'. If I try to run it a second time, making no changes, I get 'Run-time error 429' (ActiveX component can't create object).
What I'm trying to achieve is to find the row (BuildSel) in a range on worksheet (Ref) that has the same value as what's selected in a list on a userform (BuildList). Then once the row is found, to take data from that row and columns A and B, and put them in textbox's on my userform. Is my code right and ActiveX making the error? I apologize for terrible coding too.
EDIT: The listbox is on a multipage on my userform. I first noticed the issue today when I tried adding another listbox on a different page.
Private Sub BuildList_Click()
Dim Ref As Worksheet, BuildSel As Long
Set Ref = ThisWorkbook.Sheets("Ref")
BuildSel = Ref.Range("B2", Ref.Range("B" & Rows.Count).End(xlUp)).Find(BuildList.Value, lookat:=xlPart).Row
BuilderText.Value = Ref.Range("A" & BuildSel).Value
CompNameText.Value = Ref.Range("B" & BuildSel).Value
End Sub
Not sure why altering 'BuildSel' to variant makes it work, but the code as it stands has no error checking for when there is no matching list item to be found
The following code should be better suited for usage:
Private Sub BuildList_Click()
Dim Ref As Worksheet: Set Ref = ThisWorkbook.Sheets("Ref")
Dim BuildSel As Range
With Ref
Set BuildSel = .Range("B2", .Range("B" & Rows.Count).End(xlUp)).Find _
(BuildList.Value, lookat:=xlPart)
If Not BuildSel Is Nothing Then
BuilderText.Value = .Range("A" & BuildSel.Row).Value
CompNameText.Value = .Range("B" & BuildSel.Row).Value
Else
BuilderText.Value = ""
CompNameText.Value = ""
End If
End With
End Sub
I run the code below in two different PCs while get two different result: one works fine, another reminds "run time error 91 object variable or with block not set". Can anybody help me out?
Function FindArchiveFile() As String
FindArchiveFile = Application.Intersect(Worksheets("Filelist").UsedRange, _
Worksheets("Filelist").Range("B:B")).Find( _
CDate(WorksheetFunction.Large(Worksheets("Filelist").Range("B:B"), 2))).Offset(0, -1).Value
Worksheets("Setting").Range("LastDate").Value = _
Application.Intersect(Worksheets("Filelist").UsedRange, _
Worksheets("Filelist").Range("B:B")).Find( _
CDate(WorksheetFunction.Large(Worksheets("Filelist").Range("B:B"), 2))).Value
End Function
I've revised your function to make it more human-readable. This should work with some caveats.
If you are calling this function from a worksheet, it is unlikely to work.
This is because a workhseet function can generally only return a value to the calling cell. It cannot otherwise manipulate the worksheet objects. This function is trying to set a value in a named range "LastDate" and this is likely to fail. It may fail silently on this line and still return the value to the calling cell.
This function when called from a worksheet will likely raise a Circular Reference Error
When called from a subroutine, this function appears to work, or at least it does not return an error for me.
Sub Test()
'Use this to test the function
MsgBox FindArchiveFile
Worksheets("Setting").Range("LastDate").Value = FindArchiveFile
End Sub
Function FindArchiveFile() As String
Dim ws As Worksheet
Dim rngB As Range
Dim rngInt As Range
Dim foundVal As String
Set ws = Worksheets("Filelist")
Set rngB = ws.Range("B:B")
Set rngInt = Application.Intersect(ws.UsedRange, rngB)
foundVal = rngInt.Find( _
CDate(WorksheetFunction.Large(rngB, 2))).Offset(0, -1).Value
'## I comment the next line out and put it in the calling subroutine, since
' the function returns a value, use the function properly.
'Worksheets("Setting").Range("LastDate").Value = foundVal
FindArchiveFile = IIf(foundVal = vbNullString, "Not Found", foundVal)
End Function