VBA Code to Find within a Sheet

itsgrady

Board Regular
Joined
Sep 11, 2022
Messages
115
Office Version
  1. 2021
Platform
  1. Windows
  2. MacOS
A little new to VBA code but I am enjoying the process of learning the VBA.

I need some help to type a VBA Code to do the follow: I need to type the search criteria in a cell (O2) and then find xls.values in Column B. I also need to go to the cell (if possible, I would like to go 3 rows down and 3 columns over) where the criteria is found.

I also need to Find Next if more than one matched criteria.

criteria will be a Name. My sheet has 2,000 rows.
 

Excel Facts

Format cells as date
Select range and press Ctrl+Shift+3 to format cells as date. (Shift 3 is the # sign which sort of looks like a small calendar).
I will let someone else provide the code, but please elaborate on how do you want to trigger the VBA code to navigates 3 rows down and 3 columns over from the cell where the match is found? Because how that needs to be done will determine how the above is handled. For example, there are different triggers for a VBA function. In the sheet code, you can have right click or double click. You can also combine key presses with them, should you need to be free to right click and double click as normal.

Or you can have fixed cells to function as buttons (when right clicked or double clicked) or actual buttons.
 
Upvote 0
IMO, your code cannot "go to" row x of 2000 rows and keep running, or else it will simply move to the last row that fits the bill. You can go to the cell where it is first found, then shift (Offset(3,3)) and select or highlight, but I think you'll have to stop there. If you use button click, you can run again from the current position. For the first time you'd want the selected cell to be at or near the top of the sheet, or set a variable value upon each click and if the value is zero start from the top.
 
Upvote 0
I am looking to find a name in a long list of names (only in column B) there are other names in other columns I don't want to search because some of the names match the names in column B and I don't want to confuse the user.
 
Upvote 0
IMO, your code cannot "go to" row x of 2000 rows and keep running, or else it will simply move to the last row that fits the bill. You can go to the cell where it is first found, then shift (Offset(3,3)) and select or highlight, but I think you'll have to stop there. If you use button click, you can run again from the current position. For the first time you'd want the selected cell to be at or near the top of the sheet, or set a variable value upon each click and if the value is zero start from the top.
If the offset will not work well I can just do the find, but I like the thought of hitting the button again where it left off. I have the button in a Freeze Frame area that is always visible to the user.
 
Upvote 0
IMO, your code cannot "go to" row x of 2000 rows and keep running, or else it will simply move to the last row that fits the bill. You can go to the cell where it is first found, then shift (Offset(3,3)) and select or highlight, but I think you'll have to stop there. If you use button click, you can run again from the current position. For the first time you'd want the selected cell to be at or near the top of the sheet, or set a variable value upon each click and if the value is zero start from the top.
Hello, are you able to help with my issue?
 
Upvote 0
In my spare time, perhaps, although I am not that adept at Excel vba. Patience, Grasshopper. Someone who can do this in his sleep will likely beat me to it. I on the other hand, have to use Google for a lot of Excel vba syntax and concepts.
 
Upvote 0
OK, how about this, where the find value is in J2, the column values are in L and the active cell is in column L and your button is an ActiveX control on the same sheet:
VBA Code:
Private Sub CommandButton1_Click()
Dim rng As Range, rngFind As Range
Dim strToFind As String

strToFind = ActiveSheet.Range("J2")

With ActiveSheet
   Set rng = .Range(ActiveCell.Address & ":L" & Lrow)
   Set rngFind = rng.Find(What:=strToFind, After:=rng(rng.Cells.count), LookIn:=xlValues, SearchDirection:=xlNext, MatchCase:=False)
End With

If Not rngFind Is Nothing Then
   rngFind.Offset(3, 3).Select
Else
   'do something else
End If

End Sub
 
Upvote 0
Okay, I decided to code this.

Although I'm guilty of it, it's best not to use Range.Find for VBA because it changes the search settings. (So if you want to search partial match but use Range.Find to search to match entire cell's contents, for example, when you go to search after you have run a program with it, it will be set to what VBA used it for.) And it turns out that you can do this with the VBA form of Match (and I did). In addition, this code has the full functionality that I believe you are seeking. (Including, but not limited to zooming into the part of the sheet that you offset to.)

The following sheet corresponds to the code that follows.
Blank.xlsb
ABCDEO
1B5,B9,B14,B20Namesprevsearchnext
21coop
3Jem Floyd
4Anne-Marie Franklin
5Clyde Cooper
6Tye Mccray
7Jordan Harwood
8Emer Hutchinson
9Clyde Cooper
10Jem Floyd
11Jeanne Mcgrath
12Renesmae Lyons
13Serenity Sutherland
14Clyde Cooper
15Meg Mcgregor
Sheet1

Put all of the code (from all code blocks that follow) in one standard VBA code module (with the exception of the sheet code, of course . . . which I will specify).

I didn't make actual buttons. I just made it so that when you right click on 3 specific cells, then things happen. (But you can take the few lines of code associated with them in the sheet code and put them in a button code block to achieve the same result.) Those specific cells represent:
  • Go to previous occurrence [Right click on C2]
  • Search (and go to first occurrence) [Right click on D2]
  • Go to next occurrence [Right click on E2]
But you can change these cells by changing the address in these 3 short functions:
VBA Code:
Function Previous_Cell_Button_Address()
Previous_Cell_Button_Address = Range("C2").Address
End Function
Function Search_Cell_Button_Address()
Search_Cell_Button_Address = Range("D2").Address
End Function
Function Next_Cell_Button_Address()
Next_Cell_Button_Address = Range("E2").Address
End Function

Cell A1 is used to store a list of the cell addresses of ALL occurrences, and Cell A2 is used to store/update the current index in the list (that's in A1) that you have just "visited". But you can change which cells those are with these two functions:
VBA Code:
Function Cell_Address_To_Keep_OccurrenceList()
Cell_Address_To_Keep_OccurrenceList = "A1"
End Function
Function Cell_Address_To_Keep_Track_Of_Last_Visited_Index()
Cell_Address_To_Keep_Track_Of_Last_Visited_Index = "A2"
End Function

I have a function where you can change which cell the search critera are entered in, as well as the desired row and column offset amounts. But you don't have to change them (but if you do, it will modify the functionality as expected!):
VBA Code:
Function Criteria_Cell()
Criteria_Cell = "O2"
End Function
Function Row_Offset()
Row_Offset = 3
End Function
Function Column_Offset()
Column_Offset = 3
End Function

I also allow you to specify if the search is to be an exact match or partial match. (But both are case in-senstive.) I set it to partial by default.
VBA Code:
Function Exact_Match()
'Neither option is case-senstive, but it must be the full name if set = True.
Exact_Match = False
End Function

I also allow you to change the column letter with the names and the first data row in your table. (It will assume that the last row with data is the last row.)
VBA Code:
Function Column_Of_Names()
Column_Of_Names = "B"
End Function

Function First_Data_Row()
First_Data_Row = 3
End Function

This is the code that you put in the sheet (the sheet code):
VBA Code:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
If Target.Cells.Count > 1 Then Exit Sub
Select Case Target.Address
    Case Previous_Cell_Button_Address
        Cancel = True
        Call Navigate_To_Offset_Of_Search("previous")
    Case Search_Cell_Button_Address
        Cancel = True
        Range(Cell_Address_To_Keep_Track_Of_Last_Visited_Index).Value = 0
        Call Record_Matched_Cells
        Call Navigate_To_Offset_Of_Search("next")
    Case Next_Cell_Button_Address
        Cancel = True
        Call Navigate_To_Offset_Of_Search("next")
End Select
End Sub

And this is the rest of the code that you put in the standard VBA module that you have put the other (short) functions in:
VBA Code:
Sub Test__Navigate_To_Offset_Of_Search()
Call Navigate_To_Offset_Of_Search("next")
End Sub
Sub Navigate_To_Offset_Of_Search(previousOrNext As String)
Application.EnableEvents = False
Dim destinationAddress$

If UCase(previousOrNext) = "NEXT" Then
    destinationAddress = Next_Result_Address
Else
    destinationAddress = Previous_Result_Address
End If

Range( _
    Range(destinationAddress), _
    Range(destinationAddress).Offset(Row_Offset, Column_Offset) _
).Select
With ActiveWindow
    .zoom = True
    .zoom = Round(.zoom * 0.5, 0)
End With
Range(destinationAddress).Offset(Row_Offset, Column_Offset).Select
Call Record_Last_Visited_Index(destinationAddress)
Application.EnableEvents = True
End Sub

Sub Test__Record_Last_Visited_Index()
Call Record_Last_Visited_Index("B7")
End Sub
Sub Record_Last_Visited_Index(visitedCellAddress As String)
Range(Cell_Address_To_Keep_Track_Of_Last_Visited_Index).Value = Index_Of_Occurrence(Range(Cell_Address_To_Keep_OccurrenceList).Value, visitedCellAddress)
End Sub

Sub Test__Number_Of_Occurrences()
MsgBox Number_Of_Occurrences
End Sub
Function Number_Of_Occurrences()
Number_Of_Occurrences = Number_Of_Occurrences_In_Str(Range(Cell_Address_To_Keep_OccurrenceList).Value, ",") + 1
End Function

Sub Test__Previous_Result()
MsgBox Previous_Result
End Sub
Function Previous_Result_Address()
Dim previousIndex%
With Range(Cell_Address_To_Keep_Track_Of_Last_Visited_Index)
    If .Value < 2 Then
        previousIndex = Number_Of_Occurrences
    Else
        previousIndex = .Value - 1
    End If
End With
Previous_Result_Address = Split(Range(Cell_Address_To_Keep_OccurrenceList).Value, ",")(previousIndex - 1)
End Function

Sub Test__Next_Result_Address()
MsgBox Next_Result_Address
End Sub
Function Next_Result_Address()
Dim nextIndex%
With Range(Cell_Address_To_Keep_Track_Of_Last_Visited_Index)
    If .Value = Number_Of_Occurrences Then
        nextIndex = 1
    Else
        nextIndex = .Value + 1
    End If
End With
Next_Result_Address = Split(Range(Cell_Address_To_Keep_OccurrenceList).Value, ",")(nextIndex - 1)
End Function

Sub Test__Index_Of_Occurrence()
Debug.Print Index_Of_Occurrence("B1,B11,B35", "B11")
End Sub
Function Index_Of_Occurrence(list As String, cellAddress As String)
If InStr(list & ",", cellAddress & ",") = 0 Then
    Index_Of_Occurrence = -1
    Exit Function
End If
Index_Of_Occurrence = Number_Of_Occurrences_In_Str(SubString(list & ",", 1, InStr(list & ",", cellAddress & ",")), ",") + 1
End Function

Sub Record_Matched_Cells()
Dim matchedCells$
matchedCells = Matched_Cells
If matchedCells <> "" Then Range(Cell_Address_To_Keep_OccurrenceList).Value = matchedCells
End Sub

Function Matched_Cells()
'Match is NOT case-sensitive.
ThisWorkbook.Activate
Dim sheetName$, searchColumnLetter$, firstDataRow&, lastRow&, search$
Dim lookupRangeAddress$, matchedRow$, allMatches$, numberOfMatches%
sheetName = ActiveSheet.Name
firstDataRow = First_Data_Row
searchColumnLetter = Column_Of_Names
search = Trim(Range(Criteria_Cell).Value)
lastRow = Last_Non_Blank_Row_In_This_Column(sheetName, searchColumnLetter)
lookupRangeAddress = searchColumnLetter & firstDataRow & ":" & searchColumnLetter & lastRow

matchedRow = Excel_Match(sheetName, search, lookupRangeAddress, firstDataRow)
If matchedRow <> "" Then
    allMatches = searchColumnLetter & matchedRow
Else
    Matched_Cells = ""
    Exit Function
End If

numberOfMatches = 1
Do While matchedRow <> ""
    DoEvents 'Here just in case of an infinit loop, it won't crash Excel when you press Esc to quit.
    matchedRow = Excel_Match(sheetName, search, searchColumnLetter & matchedRow + 1 & ":" & searchColumnLetter & lastRow, matchedRow + 1)
    If matchedRow <> "" Then
        numberOfMatches = numberOfMatches + 1
        allMatches = allMatches & "," & searchColumnLetter & matchedRow
    Else
        GoTo Done
    End If
Loop
Done:
Matched_Cells = allMatches

End Function
Function Excel_Match(sheetName As String, search As String, lookupRangeAddress As String, firstDataRow As Long)
On Error GoTo Exit_Function
If Exact_Match = True Then
    Excel_Match = Application.WorksheetFunction.Match(search, Sheets(sheetName).Range(lookupRangeAddress), 0) + firstDataRow - 1
Else
    Excel_Match = Application.WorksheetFunction.Match("*" & search & "*", Sheets(sheetName).Range(lookupRangeAddress), 0) + firstDataRow - 1
End If
Exit_Function:
End Function

Sub Test__Last_Non_Blank_Row_In_This_Column()
MsgBox Last_Non_Blank_Row_In_This_Column(ActiveSheet.Name, 2)
End Sub
Function Last_Non_Blank_Row_In_This_Column(sheetName As String, columnLetterOrNumber As Variant)
Last_Non_Blank_Row_In_This_Column = Sheets(sheetName).Cells(Sheets(sheetName).Rows.Count, columnLetterOrNumber).End(xlUp).Row
End Function

Sub Test__Number_Of_Occurrences_In_Str()
MsgBox Number_Of_Occurrences_In_Str("xx^2+6x+9", "x")
End Sub
Function Number_Of_Occurrences_In_Str(expression As String, character As String)
Number_Of_Occurrences_In_Str = (Len(expression) - Len(Replace(expression, character, ""))) / Len(character)
End Function

Sub Test__SubString()
MsgBox "|" & SubString("ABCDEF", 3, 5) & "|"
End Sub
Function SubString(inputString As String, start As Integer, finish As Integer)
'Only when b in Mid(inputString, a, b) is = Len(inputString) are SubString() and Mid() equivalent!
On Error Resume Next
SubString = Mid(inputString, start, finish - start + 1)
End Function

Lastly, if you want to use this, I would suggest that, you change the "cell buttons" to be on a top row (above the table) somewhere. Then you can:
  1. If for example the first data row is Row 3, left click on Cell A3.
  2. Goto View -> Freeze Panes
  3. Select the Freeze Panes option (not freeze top row . . . in this example, where the first data row is > 2).
 
Upvote 0
I also need to go to the cell (if possible, I would like to go 3 rows down and 3 columns over) where the criteria is found.
What are you going to do when you get to the offset cell?
Doing something manually?
or
Doing something that can be done by macro?
 
Upvote 0

Forum statistics

Threads
1,215,020
Messages
6,122,712
Members
449,093
Latest member
Mnur

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top