Why am I getting an error with Find in VBA? - excel

I'm trying to search for a string in a range using VBA. I've cobbled together some code but I keep getting a 1004 error at the "If Not" line:
Sub test1()
Dim wb As Workbook
Dim ws As Worksheet
Dim found_range As Range
Dim search_range As Range
Set wb = Workbooks("D1")
Set ws = wb.Sheets("Master data")
Set search_range = ws.Cells(147, 1).EntireRow
If Not Range(search_range).Find("Test") Is Nothing Then
'match found
Set found_range = Range(search_range).Find("Test")
Debug.Print found_range.Column
Else
MsgBox "No match found"
'no match found
End If
End Sub
Any ideas as to why I'm getting the error?

I'm kind of confused with the double .Find
If your Range.Find method already returns a Range object once, there's no need to set it twice.
Also search_range is already a Range object, so need to try to encapsulate it in another Range() object.
In fact it's the reason, you are getting the error, because it
expects a string inside the type-casted Range(<string>)
As #MathieuGuindon correctly pointed out:
It is the cause of the error, but the reason is because the
unqualified Range call is a child object of whatever the ActiveSheet
is, and you can't do Sheet1.Range(Sheet2.Cells(1, 1), Sheet2.Cells(1,> 10)) - error 1004 is thrown in OP's code because ws isn't the
ActiveSheet; qualifying the Range call with ws would have fixed the
error too... but yeah Range(someRange) is definitely redundant.
Sub test1()
Dim wb As Workbook
Dim ws As Worksheet
Dim found_range As Range
Dim search_range As Range
Set wb = Workbooks("D1")
Set ws = wb.Sheets("Master data")
Set search_range = ws.Cells(147, 1).EntireRow
Set found_range = search_range.Find("Test")
If Not found_range Is Nothing Then
Debug.Print found_range.Address
Else
MsgBox "No match found"
End if
End Sub

You could use:
Option Explicit
Sub test1()
Dim wb As Workbook
Dim ws As Worksheet
Dim found_range As Range, search_range As Range
Set wb = Workbooks("D1")
Set ws = wb.Sheets("Master data")
Set search_range = ws.Rows(147).EntireRow
Set found_range = search_range.Find(What:="Test", LookIn:=xlValues, LookAt:=xlWhole)
If Not found_range Is Nothing Then
Debug.Print found_range.Column
Else
MsgBox "No match found"
'no match found
End If
End Sub
Note:
i you want exact match you should use LookAt:=xlWhole

Related

Iterating through multiple ranges to create a new sheet and update data

I'm trying to create a new sheet labeled with a different identifier from a range and also have two cells from other ranges included on each update. I can get the new sheets to create with a different label from a range, and have the first cell in the second range (xRg2) added to each subsequent sheet, but haven't been successful at iterating through the second range. I know I need another loop somewhere but my last nest created way too many sheet. See example below
Sub Add()
Dim xRg As Excel.Range
Dim xRg2 As Excel.Range
Dim wSh As Excel.Worksheet
Dim wBk As Excel.Workbook
Dim wSh2 As Excel.Worksheet
Dim wSh3 As Excel.Worksheet
Set wSh = ActiveSheet
Set wBk = ActiveWorkbook
Set wSh2 = ThisWorkbook.Sheets("List")
Set wSh3 = ThisWorkbook.Sheets("Template")
Set xRg2 = wSh2.Range("G66:G88")
Application.ScreenUpdating = False
For Each xRg In wSh2.Range("B66:B88")
With wBk
wSh3.Copy after:=.Sheets(.Sheets.Count)
On Error Resume Next
wSh.Name = xRg.Value
wSh.Cells(33,7) = xRg2.Value
If Err.Number = 1004 Then
Debug.Print xRg.Value & "already used as a sheet name"
End If
On Error GoTo 0
End With
Next xRg
Application.ScreenUpdating = True End Sub
So, to summarize, the goal here is to input the ranges into the code each time and have each new sheet include the first values from each range, then the second sheet the second values from each range, and so on until the xRg is at the end of it's list. I know there's only two ranges down here but the total will be 3. Also apologies on the poor variable discipline...
Thanks!
Try something like this (sorry do not like all those x... variable names)
Sub Add()
Dim c As Range
Dim wb As Workbook, ws As Worksheet, wsList As Worksheet
Set wb = ActiveWorkbook
Set wsList = wb.Worksheets("List")
Application.ScreenUpdating = False
For Each c In wsList.Range("B66:B88").Cells
ThisWorkbook.Sheets("Template").Copy after:=wb.Sheets(wb.Sheets.Count)
Set ws = wb.Sheets(wb.Sheets.Count) '<< get the just-created sheet copy
On Error Resume Next
ws.Name = c.Value
ws.Cells(33, 7) = c.EntireRow.Columns("G").Value
If Err.Number = 1004 Then
Debug.Print "'" & c.Value & "' already used as a sheet name"
End If
On Error GoTo 0
Next c
Application.ScreenUpdating = True
End Sub

ReferesToRange gives Run-time Error '91':

Hi I keep getting "variable or with block variable not set" for the line in the for loop of the following code. Could anyone tell me where I am going wrong? Thanks
Public Sub TestFind()
Set wsNew = Worksheets.Add(after:=Worksheets(Worksheets.Count))
wsNew.Range(wsNew.Cells(1, 1), wsNew.Cells(1, 17)).Value _
= Array("ReturnId", "GridName", "Item", "TabName", "AltFldName", "FieldPos", "Reference", "Type", _
"SortPos", "FieldSize", "CalcField", "CellDesc", "DoNotExport", "SortStrategy", "Threshold", "IsInnerGridCell", "ReportLine")
Dim Nm As Name
Dim rng As Range
Dim wb As Workbook
Set wb = ThisWorkbook
For Each Nm In ThisWorkbook.Names
rng = Nm.RefersToRange.Value
Next
End Sub
You have two problems in the code:
1- Not all names refer necessarily to a named range. They may refer to constants, for example. So you need to add some checking before assuming that the name refers really to a range.
2- to assign a range object to a named range, you need to use Set
Try this:
Dim Nm As Name, rng As Range
For Each Nm In ThisWorkbook.Names
Debug.Print Nm.Name
On Error Resume Next
Set rng = Nm.RefersToRange ' <-- Use Set to assign object references
If Err.Number <> 0 Then GoTo NextNm ' <-- This name does not refer to a named range
On Error GoTo 0
Debug.Print rng.Address
' ... Do whatever you want with the named range
NextNm:
On Error GoTo 0
Next Nm
Adjustments made and commented.
Option Explicit
Sub wqewtr()
Dim Nm As Name
Dim rng As Range
Dim var As Variant '<~~ for numbers, dates and/or text
Dim wb As Workbook
Set wb = ThisWorkbook
For Each Nm In ThisWorkbook.Names
Debug.Print Nm.Name
'nm could a 'special internal name' that starts with an underscore
'skip over these
If Left(Nm.Name, 1) <> "_" Then
'show the address of the defined name range - could be more than one cell
Debug.Print Nm.RefersToRange.Address
'do not try to assign value to a range object unless that range already has been asinged a cell or cells
'rng = Nm.RefersToRange.Value
'Debug.Print rng
'this fails if Nm is more than a single cell
'var = Nm.RefersToRange.Value
'Debug.Print var
'this guarantees one cell
var = Nm.RefersToRange.Cells(1, 1).Value
Debug.Print var
End If
Next
End Sub
try the below and report back. You have not included some of your coded i am assuming...
Dim Nm As Name
Dim rng As Range
Dim wb As Workbook
Set wb = ThisWorkbook
For Each Nm In ThisWorkbook.Names
rng = Nm.RefersToRange.Value
Next

Error 91 setting range of worksheet variable

I'm at a loss as to why it seems that I can store a range value in the range property of a worksheet variable but not in a range variable. I got error 91 every time I tried to run this code:
Dim ws As Worksheet, rng As Range
Set ws = Worksheets.Add
ws.name = "Potato"
rng = ws.Range("A1:K1")
rng.PasteSpecial
I was able to run the program successfully by replacing the last 2 lines with:
ws.Range("A1:K1").PasteSpecial
This works, even though it clutters other parts of my code. But I can't understand for the life of me what the problem was with using the range variable was.
I would appreciate any clarification anyone can provide.
You have to use Set with object variables:
Set rng = ws.Range("A1:K1")
Here is added code that will make sure there won't be an error if you already have a sheet named "Potato"
Code to make sure Sheet("Potato") doesn't already exist.
Sub Button1_Click()
Dim ws As Worksheet, rng As Range, cRng As Range
Dim worksh As Integer
Dim worksheetexists As Boolean
Dim s As String
s = "Potato"
Set cRng = ActiveSheet.Range("A1:K1")
worksh = Application.Sheets.Count
worksheetexists = False
For x = 1 To worksh
If Worksheets(x).Name = s Then
worksheetexists = True
MsgBox s & ", already Exists"
Exit For
End If
Next x
If worksheetexists = False Then
Set ws = Worksheets.Add()
ws.Name = s
Set rng = ws.Range("A1:K1")
cRng.Copy
rng.PasteSpecial Paste:=xlPasteAll
Application.CutCopyMode = 0
End If
End Sub

Generate sheets corresponding to row values (duplicate values exist)

I have a main worksheet (Install_Input) where sheet number, test section, and material are manually entered by user.
(Below: illustration of Install_Input ws: Range A1:C8)
Sheet# | TestSection | Material
.....1.....|..........A..........|.STEEL.|
.....2.....|..........B..........|.PLASTIC.|
.....3.....|..........C..........|.STEEL.|
.....5.....|..........G..........|.STEEL.|
.....2.....|..........F..........|.PLASTIC.|
.....2.....|..........A..........|.STEEL.|
.....5.....|..........D..........|.PLASTIC.|
I want to generate sheets within the current workbook that correspond to sheet numbers entered in Install_Input. The code I made will generate a new sheet for each value in MyRange, however, I would like for my code to skip over generating sheets that already exist. I tried using the "On Error Resume Next" and "On Error GoTo 0" commands to solve this problem, but they just generated unnamed sheets to compensate for those that already exist.
Sub Consolidate_Sheets()
Dim MyCell As Range
Dim MyRange As Range
Dim ws As Worksheet
Set MyRange = Sheets("Install_Input").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
If Sheets(Sheets.Count).Name <> MyCell.Value Then
'On Error Resume Next
Sheets.Add After:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = MyCell.Value
'On Error GoTo 0
End If
Next MyCell
End Sub
You can use the following two functions:
Function getSheetWithDefault(name As String, Optional wb As Excel.Workbook) As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
If Not sheetExists(name, wb) Then
wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count)).name = name
End If
Set getSheetWithDefault = wb.Sheets(name)
End Function
Function sheetExists(name As String, Optional wb As Excel.Workbook) As Boolean
Dim sheet As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
sheetExists = False
For Each sheet In wb.Worksheets
If sheet.name = name Then
sheetExists = True
Exit Function
End If
Next sheet
End Function
To use it in your code:
Sub Consolidate_Sheets()
Dim MyCell As Range
Dim MyRange As Range
Dim ws As Worksheet
Set MyRange = Sheets("Install_Input").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
If Sheets(Sheets.Count).Name <> MyCell.Value Then
'On Error Resume Next
set ws = getSheetWithDefault(MyCell.Value)
'On Error GoTo 0
End If
Next MyCell
End Sub
You could implement a CheckSheet function like the one described in this SO answer that loops through all existing sheets and compares the name of each sheet with the passed-in value.

Define Cell Location Variable in Excel

I'm looking for a way to, instead of typing "ActiveCell.OffSet(1,1) over and over again in my vba code, define that as a variable, "x" and use that instead.
I have to use the dim command to do this but I"m not sure what the data type would be.
Suggestions?
When I test it using the code below I get Runtime Error 1004.
Private Sub CommandButton1_Click()
Dim i As Range
Set i = ActiveCell
ActiveSheet.Range(ActiveSheet.Range(i), ActiveSheet.Range(i).End(xlUp)).Select
End Sub
In response to your edit
Avoid the use of .Select/Activate and fully qualify your objects. INTERESTING READ
Your code can be written as
Private Sub CommandButton1_Click()
Dim ws As Worksheet
Dim rng1 As Range, rng2 As Range
'~~> Change as applicable
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
Set rng1 = ws.Range("A10")
Set rng2 = .Range(rng1, rng1.End(xlUp))
With rng2
Debug.Print .Address
'
'~~> Do something with the range
'
End With
End With
End Sub
If you still want to know what was wrong with your code then see this.
You have already defined your range. You do not need to add ActiveSheet.Range() again. Your code can be written as
Private Sub CommandButton1_Click()
Dim i As Range
Set i = ActiveCell
ActiveSheet.Range(i, i.End(xlUp)).Select
End Sub
EDIT
Followup from comments
Was ActiveSheet.Range() actually problematic or just redundant? – user3033634 14 mins ago
It is problematic. The default property of a range object is .Value
Consider this example which will explain what went wrong with your code
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
Set rng = .Range("A1")
rng.Value = "Blah"
MsgBox rng '<~~ This will give you "Blah"
MsgBox rng.Value '<~~ This will give you "Blah"
MsgBox rng.Address '<~~ This will give you "$A$1"
MsgBox ws.Range(rng) '<~~ This will give you an error
'~~> Why? Becuase the above is evaluated to
'MsgBox ws.Range("Blah")
MsgBox ws.Range(rng.Address) '<~~ This will give you "Blah"
End With
End Sub
Dim x As Range
Set x = ActiveCell.OffSet(1,1)
EDIT: in response to your comment:
Private Sub CommandButton1_Click()
Dim i As Range
Set i = ActiveCell
ActiveSheet.Range(i, i.End(xlUp)).Select
End Sub

Resources