.FindNext keeps returning to the first match, instead of the next - excel

I have a SourceFile.xlsm that contains an X number of field definitions and their contents:
I want to put the contents of these fields into a TargetFile.xlsx, that may contain 0 or more of those field definitions:
The expected end result would be this:
But the actual end result is always this:
And that is because this line in the code below:
Set source_range = sourceSheet.Cells.FindNext(source_range)
always keeps coming back to the first occurrence (cell B5, containing "[Field 1]"), instead of the next (cell B6, containing "[Field 2]"):
Function CopyFromSourceToTarget()
Dim sourceWB As Workbook
Dim targetWB As Workbook
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim source_range As Range
Dim target_range As Range
Dim FirstFound_source As String
Dim FirstFound_target As String
Set sourceWB = ActiveWorkbook
Set targetWB = Workbooks.Open("C:\TEMP\TargetFile.xlsx")
For Each sourceSheet In sourceWB.Worksheets
Set source_range = sourceSheet.Cells.Find("[", LookIn:=xlValues)
If Not source_range Is Nothing Then
FirstFound_source = source_range.Address
Debug.Print source_range.Value
Do
sourceWB.Activate
source_range.Select
For Each targetSheet In targetWB.Worksheets
Set target_range = targetSheet.Cells.Find(source_range.Value, LookIn:=xlValues)
If Not target_range Is Nothing Then
FirstFound_target = target_range.Address
Do
target_range.FormulaR1C1 = CStr(source_range.Offset(0, 1).Value)
Set target_range = targetSheet.Cells.FindNext(target_range)
If target_range Is Nothing Then Exit Do
Loop Until target_range.Address = FirstFound_target
End If
Next
Set source_range = sourceSheet.Cells.FindNext(source_range)
Debug.Print source_range.Value
Loop Until source_range.Address = FirstFound_source
End If
Next
End Function
I've tried several options, but all to no avail. Hopefully, someone here can help me along, because this seemingly very simple issue is driving me nuts.

Instead of this line:
Set source_range = sourceSheet.Cells.FindNext(source_range)
try this line:
Set source_range = sourceSheet.Cells.Find(What:="[", After:=source_range, LookIn:=xlValues)
I'd also add some more options to the Find like LookAt:= xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False, but it might not be necessary. Up to you.

Related

How do I get the Cell Address from a Variable VBA

I created a variable oldPassword which is populated using a VLookup.
I am trying to get now the cell address from that result but nothing seem to work.
Dim oldPassword As String
oldPassword = Application.WorksheetFunction.VLookup(Me.ComboBox1.Value, Worksheets("Employees").Range("A:B"), 2, False)
You should break the task into steps
Get a reference to the cell containing the search value
Use that reference to get the required value and address
Sub Demo
Din rSearch As Range
Dim rUser as Range
Dim rPassword As Range
Dim idx As Variant
Set rSearch = Worksheets("Employees").Range("A:B")
idx = Application.Match(Me.ComboBox1.Value, rSearch.Columns(1), 0)
If Not IsError(idx) Then
Set rUser = rSearch.Cells(idx, 1)
Set rPassword = rUser.Cells(1, 2)
' get the result
oldPassword = rPassword.Value2
' get the address
Debug.Print rPassword.Address
End If
End Sub
I would prefer using .Find as #Andreas suggested but then that is my personal preference.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Employees")
Dim aCell As Range
Set aCell = ws.Columns(1).Find(What:=ComboBox1.Value, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
Dim oldPassword As String
If Not aCell Is Nothing Then
With aCell.Offset(, 1)
'~~> Do what you want with that cell
oldPassword = .Value2
MsgBox .Address
End With
Else '<~~ Optional
MsgBox ComboBox1.Value & " not found!"
End If
End Sub

How to use cell address as a parameter of Range()?

I have a template file that I will use to populate more files and I need to hide some rows according to what its selected, but at the same time I can't hide other rows. I can do it well if the data stay the same size all the time, but the file will be increasing and decreasing depending on the information.
I have a range of values in Column C. What I tried to do is to look for the cell value that contains "Pack" (It will be same for all files). From that cell that contains "Pack" (let's assume that is at C8 now, but can be in C30 in other file) I need to start looking for values that are not equal to the one that I have from a droplist (rowing) and hide the rows.
Maybe better explained, also I tried to do was to assign a variable that will hold the value of the droplist and just look for values that was not equal and simply hide it. Then do a .Find() to find the "Pack" word. Once it was found, get the cell address. Finally take that address and use it as a parameter in Range() as yo can see in the code that I wrote: For Each cell In Range("packR:C5") and I know that is very wrong because I can't pass that.
Dim cell As Range
Dim pack As Range
rowing = Range("A2").Value
Set pack = Range("C1:C12").Find("Pack")
Set packA = Range(pack.Address)
Set packR = packA
For Each cell In Range("packR:-end point here")
cell.EntireRow.Hidden = False
If Not IsEmpty(cell) Then
If cell.Value <> rowing Then
cell.EntireRow.Hidden = True
End If
End If
Next
I have very little vba background but with research I can understand a few. Basically the goal is to ignore all the rows in top of "Pack" and start looking from "Pack" (That need to have a cell address) to the end of the excel file. The biggest issue is to take that cell address and use it as parameter to the Range ("":"").
I think you're looking for something like this. Note the comment about specifying the other parameters of Range.Find.
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rowing As Variant
rowing = ws.Range("A2").Value
Dim pack As Range
Set pack = ws.Range("C1:C12").Find("Pack") '<--- you should specify the other parameters of Find
Dim lastCell As Range
Set lastCell = ws.Cells(ws.Rows.Count, "C").End(xlUp)
If Not pack Is Nothing Then '<--- tests to see if pack was found
Dim cell As Range
For Each cell In ws.Range(pack, lastCell)
If Not IsEmpty(cell) Then
cell.EntireRow.Hidden = (cell.Value <> rowing)
End If
Next
End If
End Sub
EDIT:
End(xlUp) will not find the true last row if rows are already hidden. To get around this, here are two options:
Unhide all rows after finding "Pack".
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rowing As Variant
rowing = ws.Range("A2").Value
Dim pack As Range
Set pack = ws.Range("C1:C12").Find("Pack") '<--- you should specify the other parameters of Find
If Not pack Is Nothing Then '<--- tests to see if pack was found
ws.UsedRange.EntireRow.Hidden = False '<--- unhide all rows so as to find the last cell properly
Dim lastCell As Range
Set lastCell = ws.Cells(ws.Rows.Count, "C").End(xlUp)
Dim cell As Range
For Each cell In ws.Range(pack, lastCell)
If Not IsEmpty(cell) Then
cell.EntireRow.Hidden = (cell.Value <> rowing)
End If
Next
End If
End Sub
Use an alternate way of finding the last cell:
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rowing As Variant
rowing = ws.Range("A2").Value
Dim pack As Range
Set pack = ws.Range("C1:C12").Find("Pack") '<--- you should specify the other parameters of Find
Dim lastCell As Range
Set lastCell = GetLastCell(ws, 3)
If Not pack Is Nothing Then '<--- tests to see if pack was found
Dim cell As Range
For Each cell In ws.Range(pack, lastCell)
If Not IsEmpty(cell) Then
cell.EntireRow.Hidden = (cell.Value <> rowing)
End If
Next
End If
End Sub
Private Function GetLastCell(ByVal ws As Worksheet, Optional ByVal colNum As Long = 1) As Range
With ws
Dim lastCell As Range
Set lastCell = .Columns(colNum).Find(What:="*", _
After:=.Cells(1, colNum), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If lastCell Is Nothing Then
Set lastCell = .Cells(1, colNum)
End If
End With
Set GetLastCell = lastCell
End Function

How to loop through several regions of a worksheet?

I'm looking for some VBA that will allow me to loop through several different REGIONS on a worksheet. Not individual cells, necessarily, but to jump from "currentregion" to the next "currentregion". And once the region is located, it should be selected and copied.
I've tried setting a StartCell (via Cells.Find(What:="*") and then using that cell to select the corresponding 'currentregion'. The issue is how to move to the next 'currentregion', until all 'currentregions' on the worksheet have been copied/pasted.
My results are inconsistent so far, where sometimes all the necessary regions are copied/pasted, but other times some of the regions are ignored (same exact worksheet, same exact data).
Set StartCell = Cells.Find(What:="*", _
After:=Cells(Rows.Count, Columns.Count), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)Do
'Select Range and copy it
If StartCell <> "" Then
StartCell.currentregion.CopyPicture
'Select a cell to paste the picture in
Range("A16").PasteSpecial
'Move to next range to be copied
Set StartCell = StartCell.End(xlToRight).End(xlToRight)
StartCell.Select
End If
Loop Until StartCell = ""
Something like that should work
Option Explicit
Public Sub ProcessEachRegion()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'define your sheet
Dim StartCell As Range
Set StartCell = ws.Range("A1") 'define start cell
Do Until StartCell.Column = ws.Columns.Count 'loop until end of columns
With StartCell.CurrentRegion
'do all your copy stuff here!
'.Copy
'Destination.Paste
Set StartCell = .Resize(1, 1).Offset(ColumnOffset:=.Columns.Count - 1).End(xlToRight)
End With
Loop
End Sub
It looks for the next region right to the previous one (regions 1 to 5 in the example below).
The main sub (I named it tgr) will call a function named GetAllPopulatedCells which defines a range for all populated cells in a worksheet. The .Areas property will let your loop through each region. It will then copy each Area/Region as a picture (still not sure why you want this) and put it in the destination cell, and then adjust the destination cell as needed so that all of the pasted images are stacked on top of each other.
Sub tgr()
Dim ws As Worksheet
Dim rAllRegions As Range
Dim rRegion As Range
Dim rDest As Range
Set ws = ActiveWorkbook.ActiveSheet
Set rAllRegions = GetAllPopulatedCells(ws)
Set rDest = ws.Range("A16")
If rAllRegions Is Nothing Then
MsgBox "No populated cells found in '" & ws.Name & "'. Exiting Macro.", , "Error"
Exit Sub
End If
For Each rRegion In rAllRegions.Areas
rRegion.CopyPicture
rDest.PasteSpecial
Set rDest = rDest.Offset(rRegion.Rows.Count)
Next rRegion
End Sub
Public Function GetAllPopulatedCells(Optional ByRef arg_ws As Worksheet) As Range
Dim ws As Worksheet
Dim rConstants As Range
Dim rFormulas As Range
If arg_ws Is Nothing Then Set ws = ActiveWorkbook.ActiveSheet Else Set ws = arg_ws
On Error Resume Next
Set rConstants = ws.Cells.SpecialCells(xlCellTypeConstants)
Set rFormulas = ws.Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
Select Case Abs(rConstants Is Nothing) + 2 * Abs(rFormulas Is Nothing)
Case 0: Set GetAllPopulatedCells = Union(rConstants, rFormulas)
Case 1: Set GetAllPopulatedCells = rFormulas
Case 2: Set GetAllPopulatedCells = rConstants
Case 3: Set GetAllPopulatedCells = Nothing
End Select
Set ws = Nothing
Set rConstants = Nothing
Set rFormulas = Nothing
End Function

VBA - update find function to loop through rows and move on if value isn't there

Trying to put together a macro that searches each row to see if it contains 7 search terms (see "Warranty:" example below). If the cell starts with one of the phrases (like "Warranty:"), then that cell is pasted in a specific cell (same row but different column) in another worksheet.
Issues:
Had trouble with the macro until I added the select function - I know this slows them down, but I couldn't figure out a way to do this without it
Can't figure out how to get it to loop through all rows
Errors if the row doesn't have the word - need it to just keep going through
Sub FindTest()
Worksheets("Macro").Range("1:1").Find(What:="Warranty: ", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True).Copy
'Cell begins with "Warranty:" but text following varies
Sheets("CSV Upload").Select
Sheets("CSV Upload").Range("J1").Select
ActiveSheet.Paste
End Sub
UPDATE:
Sub FindTest()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
'On Error Resume Next
For R = 1 To Macro.UsedRange.Rows.Count
Set rng = Macro.Rows(R)
Dim FindRange As Range: Set FindRange = rng.Find(What:="Warranty:", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
'FindRange.Copy CSV.Range("J1")
'CSV.Cells(1, J) = Macro.Cells(FindRange)
Next
'On Error GoTo 0
End Sub
To loop through each row in the worksheet:
Dim ws As Worksheet: Set ws = Sheets("Macro")
Dim csv_upload As workseet: Set csv_upload = Sheets("CSV Upload")
For r = 1 To ws.UsedRange.Rows.Count
Set rng = ws.Rows(r)
rng.Find(What:="Warranty: ", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
...
Next
Then to copy the values, depending on which cells you need to copy
csv_upload.cells(dest_row, dest_col) = ws.cells(orig_row, orig_col)
For it to continue when you have an error, you can tell it to resume
On Error Resume Next
' potential for error to be raised
' Don't use this unless you know you are going to get a specific
' error and know there are no unintended consequences of ignoring it.
On Error GoTo 0
Using the code in your update, the following code should work for you.
Sub FindWarranty()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
Dim rng As Range, FindRange As Range
Dim Phrase As String
Phrase = "Warranty:"
For r = 1 To Macro.UsedRange.Rows.Count
Set rng = Macro.Rows(r)
Set FindRange = rng.Find(What:=Phrase, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
If Not FindRange Is Nothing Then
' Set destination cell to what you need it to be
c = 1
CSV.Cells(r, c) = FindRange
End If
Next
End Sub
A slightly more elegant way that Quicksilver alluded to is:
Sub FindWarrantys()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
Dim FoundCell As Range, FirstAddr As String
Dim Phrase As String, c As Integer
Phrase = "Warranty:"
' Find the first occurrence. The after variable is set to the
' last cell so that it will start searching from the beginning.
Set FoundCell = Macro.UsedRange.Find(what:=Phrase, _
after:=Macro.UsedRange.Cells(Macro.UsedRange.Cells.Count))
' Save the address of the first occurrence to prevent an infinite loop
If Not FoundCell Is Nothing Then
FirstAddr = FoundCell.Address
End If
' Loop through all finds
Do Until FoundCell Is Nothing
c = 1 ' Adjust for logic to determine which column
CSV.Cells(FoundCell.Row, c) = FoundCell
' Find the next occurrence
Set FoundCell = Macro.UsedRange.FindNext(after:=FoundCell)
' Break if we're back at the first address
If FoundCell.Address = FirstAddr Then
Exit Do
End If
Loop
End Sub

how to delete row when multiple criteria is selected

Hi i have below code which delete the rows when it finds particular single criteria from multiple sheets i want to modify the code with multiple criteria
Sub DeleteRow_IMPLEMENTATION()
Dim Header As Range
Dim FoundCell As Range
Dim ws As Worksheet
Dim HeaderToFind As String
Dim ValueToFind As String
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
HeaderToFind = "BankName"
ValueToFind = "abcd"
For Each ws In Worksheets
Set Header = ws.Rows(1).Find(what:=HeaderToFind, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=True)
If Not Header Is Nothing Then
Set FoundCell = ws.Columns(Header.Column).Find(what:=ValueToFind, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=True)
Do While Not FoundCell Is Nothing
ws.Rows(FoundCell.Row).delete
Set FoundCell = Nothing
Set FoundCell = ws.Columns(Header.Column).Find(what:=ValueToFind, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=True)
Loop
End If
Next ws
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
End Sub
i tried something like below
HeaderToFind = "BankName"
ValueToFind = "abcd,xyz "
when i tried its not working any help is appreciated.
Try setting the value you are trying to find in an array instead of a comma seperated string. then foreach value in the array: set ValueToFind equal to the value, and call this code
HeaderToFind = "BankName"
Dim targetValues = New String { "abcd", "xyz" }
For Each targetVal In targetValues
ValueToFind = targetVal
For Each ws In Worksheets
....
Next ws
Next targetVal
Alternatively, you can check out this:
Efficient way to delete entire row if cell doesn't contain '#'
and set the criteria to your array of values

Resources