How to make VBA Command button search for provided input and change status if exists? - excel

Ok so here is my VBA:
Private Sub In_Click()
Range("E2").Find.Text = TextBox1.Text And TextBox2.Text
Range("A65536").End(xlUp).Select
RowNumber = ActiveCell.Row
Range("E(RowNumber)") = ("IN")
End Sub
What I am trying to make happen is, the contents of form TextBox1 and 2 are searched for and, if found, a cell in column E of whatever row the text was found (Will be the same row for both) in will be overwritten to read "IN" and the form cleared for the next entry.
If down voting or flagging in some way please tell me why at least. I cant do better if I dont know the problem.
New:
Private Sub CheckIn_Click()
Dim FoundRange As Range
Dim Status As Range
Set FoundRange = Columns("D").Find(What:=TextBox2.Text, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not FoundRange Is Nothing Then
Set Status = Columns("E")
Status.Value = "IN"
Else
MsgBox "Not Found"
End If
End Sub

You would use either the Range.Find method to find the first occurrence …
Sub In_Click()
Dim FoundRange As Range
Set FoundRange = Columns("E").Find(What:=TextBox1.Text & TextBox2.Text, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not FoundRange Is Nothing Then
FoundRange.Value = "IN"
Else
MsgBox "not found"
End If
End Sub
Or the Range.Replace method to replace all occurences …
Sub In_Replace()
Columns("E").Replace What:=TextBox1.Text & TextBox2.Text, Replacement:="IN", LookAt:=xlWhole, MatchCase:=False
End Sub
Note that if you concatenate 2 strings you must use the & and you cannot use the word And: ConcatString = TextBox1.Text & TextBox2.Text.

Related

Is there a VBA code that looks for a cell in a column that contains a specific number? [duplicate]

I have a column of numbers of over 500 rows. I need to use VBA to check if variable X matches any of the values in the column.
Can someone please help me?
The find method of a range is faster than using a for loop to loop through all the cells manually.
here is an example of using the find method in vba
Sub Find_First()
Dim FindString As String
Dim Rng As Range
FindString = InputBox("Enter a Search value")
If Trim(FindString) <> "" Then
With Sheets("Sheet1").Range("A:A") 'searches all of column A
Set Rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
Application.Goto Rng, True 'value found
Else
MsgBox "Nothing found" 'value not found
End If
End With
End If
End Sub
Simplest is to use Match
If Not IsError(Application.Match(ValueToSearchFor, RangeToSearchIn, 0)) Then
' String is in range
If you want to do this without VBA, you can use a combination of IF, ISERROR, and MATCH.
So if all values are in column A, enter this formula in column B:
=IF(ISERROR(MATCH(12345,A:A,0)),"Not Found","Value found on row " & MATCH(12345,A:A,0))
This will look for the value "12345" (which can also be a cell reference). If the value isn't found, MATCH returns "#N/A" and ISERROR tries to catch that.
If you want to use VBA, the quickest way is to use a FOR loop:
Sub FindMatchingValue()
Dim i as Integer, intValueToFind as integer
intValueToFind = 12345
For i = 1 to 500 ' Revise the 500 to include all of your values
If Cells(i,1).Value = intValueToFind then
MsgBox("Found value on row " & i)
Exit Sub
End If
Next i
' This MsgBox will only show if the loop completes with no success
MsgBox("Value not found in the range!")
End Sub
You can use Worksheet Functions in VBA, but they're picky and sometimes throw nonsensical errors. The FOR loop is pretty foolproof.
try this:
If Application.WorksheetFunction.CountIf(RangeToSearchIn, ValueToSearchFor) = 0 Then
Debug.Print "none"
End If
Just to modify scott's answer to make it a function:
Function FindFirstInRange(FindString As String, RngIn As Range, Optional UseCase As Boolean = True, Optional UseWhole As Boolean = True) As Variant
Dim LookAtWhat As Integer
If UseWhole Then LookAtWhat = xlWhole Else LookAtWhat = xlPart
With RngIn
Set FindFirstInRange = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=LookAtWhat, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=UseCase)
If FindFirstInRange Is Nothing Then FindFirstInRange = False
End With
End Function
This returns FALSE if the value isn't found, and if it's found, it returns the range.
You can optionally tell it to be case-sensitive, and/or to allow partial-word matches.
I took out the TRIM because you can add that beforehand if you want to.
An example:
MsgBox FindFirstInRange(StringToFind, Range("2:2"), TRUE, FALSE).Address
That does a case-sensitive, partial-word search on the 2nd row and displays a box with the address. The following is the same search, but a whole-word search that is not case-sensitive:
MsgBox FindFirstInRange(StringToFind, Range("2:2")).Address
You can easily tweak this function to your liking or change it from a Variant to to a boolean, or whatever, to speed it up a little.
Do note that VBA's Find is sometimes slower than other methods like brute-force looping or Match, so don't assume that it's the fastest just because it's native to VBA. It's more complicated and flexible, which also can make it not always as efficient. And it has some funny quirks to look out for, like the "Object variable or with block variable not set" error.
Fixed Problem mentioned by #JeffC in the function from #sdanse:
Function FindFirstInRange(FindString As String, RngIn As Range, Optional UseCase As Boolean = True, Optional UseWhole As Boolean = True) As Variant
Dim LookAtWhat As Integer
If UseWhole Then LookAtWhat = xlWhole Else LookAtWhat = xlPart
With RngIn
Set FindFirstInRange = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=LookAtWhat, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=UseCase)
If FindFirstInRange Is Nothing Then
FindFirstInRange = False
Exit Function
End If
If IsEmpty(FindFirstInRange) Then
FindFirstInRange = False
Else
FindFirstInRange = True
End If
End With
End Function
Try adding WorksheetFunction:
If Not IsError(Application.WorksheetFunction.Match(ValueToSearchFor, RangeToSearchIn, 0)) Then
' String is in range
=IF(COUNTIF($C$2:$C$500,A2)>0,"Exist","Not Exists")

Replace method and changing format?

I'm using vba method Replace, and I need to change every "/" to ",". This looks like a simple task so I use:
ActiveWorkbook.Worksheets(2).Cells.Replace What:="_/_", Replacement:=",", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=True, _
ReplaceFormat:=True
The problem is when one of cells has value like:
04_/_2018
And the result is:
4,2018
instead of:
04,2018
All of my cells in this workbook has text formating before and after aplying the code. My guess is that Excel in a process is changing the format to general for a moment and it cut offs not necessary for a number left zero.
I've tried to bypass this issue by changing parameters of the method (none of this worked) and changing the decimal separator from "," to ".". This helped when using find and replace by hand from Excel, but when I record it and try to use as Macro it doesn't work. What can I do to prevent Excel from cutting off zeros in this scenarios?
If you want 04,2018 then use .Find/.FindNext then replace+reconstruct the value before placing in the cell.
Is this what you are trying?
Sub Sample()
Dim oRange As Range, aCell As Range, bCell As Range
Dim ws As Worksheet
Dim ExitLoop As Boolean
Dim SearchString As String
On Error GoTo Whoa
Set ws = Worksheets("Sheet1")
Set oRange = ws.UsedRange
SearchString = "_/_"
Set aCell = oRange.Find(What:=SearchString, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
Set bCell = aCell
If Left(aCell.Value, 1) = 0 Then
aCell.Value = "'" & Replace(aCell.Value, SearchString, ",")
Else
aCell.Value = Replace(aCell.Value, SearchString, ",")
End If
Do While ExitLoop = False
Set aCell = oRange.FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
If Left(aCell.Value, 1) = 0 Then
aCell.Value = "'" & Replace(aCell.Value, SearchString, ",")
Else
aCell.Value = Replace(aCell.Value, SearchString, ",")
End If
Else
ExitLoop = True
End If
Loop
Else
MsgBox SearchString & " not Found"
End If
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
Screenshot
The leading zeroes are automatically removed in Excel by default. Thus 04,2014 is changed to 4,2014. A way to work around this is to format the cells as Text, adding this line before the Replace():
ActiveWorkbook.Worksheets(2).Cells.NumberFormat = "#"
Formatting to Text has a lot of unpleasant changes, e.g. the text goes to the left and Excel does not recognize the dates/numbers by default.
This is a simple sample of the code, changing 1 cell:
Sub TestMe()
ActiveWorkbook.Worksheets(1).Cells.NumberFormat = "General"
Range("B5") = "05_2018"
ActiveWorkbook.Worksheets(1).Cells.NumberFormat = "#"
Range("B5") = Replace(Range("B5"), "_", ".")
End Sub
Formatting to text can work like this, for bigger, unknown ranges:
Sub TestMe()
Worksheets(1).Cells.NumberFormat = "General"
Range("A1:B15") = "05_2018"
Dim findRange As Range
Set findRange = Worksheets(1).Cells.Find("_")
Dim myCell As Range
If Not findRange Is Nothing Then
For Each myCell In findRange
myCell.Replace "_", ","
Next myCell
End If
End Sub

How do I ignore no user input?

My code is supposed to check each column "C" cell for whatever input is in TextBox2 then, if found, using the row found in adjust the Column "D" intersected cell contents to "IN".
All this is happening as desired with one exception. If I click the command button "Check IN" with no input in textbox2, it sets the first next empty cell in column "D" as "IN".
Private Sub CheckIn_Click()
Dim FoundRange As Range
Dim Status As Range
Set FoundRange = Columns("C").Find(What:=TextBox2.Text, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not FoundRange Is Nothing Then
Set Status = FoundRange.Offset(ColumnOffset:=1)
Status.Value = "IN"
TextBox2 = ""
ThisWorkbook.Save
Else
Status.Value = ""
TextBox2 = ""
TextBox1.SetFocus
MsgBox "Not Found"
End If
End Sub
What I attempted to do is use the same script from line 9 at line 13 but with an empty value so that even if something is done the cell is empty. On the other hand, after running the code without TextBox2 input I received this error:"Object variable or With block variable not set" at line 13.
I do not understand what variable is not being set. All I am trying to do is mitigate what happens if the button is hit with no input.
Test First:
Private Sub CheckIn_Click()
Dim FoundRange As Range
Dim Status As Range
If TextBox2.Text = "" Then
MsgBox "Test Skipped"
Else
Set FoundRange = Columns("C").Find(What:=TextBox2.Text, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not FoundRange Is Nothing Then
Set Status = FoundRange.Offset(ColumnOffset:=1)
Status.Value = "IN"
TextBox2 = ""
ThisWorkbook.Save
Else
Status.Value = ""
TextBox2 = ""
TextBox1.SetFocus
MsgBox "Not Found"
End If
End If
End Sub
First check the value of TextBox2, then proceed if there is a value.
Private Sub CheckIn_Click()
If TextBox2.Value <> "" Then
Dim FoundRange As Range
Dim Status As Range
Set FoundRange = Columns("C").Find(What:=TextBox2.Text, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not FoundRange Is Nothing Then
Set Status = FoundRange.Offset(ColumnOffset:=1)
Status.Value = "IN"
TextBox2.Value = ""
Else
MsgBox "Not Found"
End If
End If
End Sub

Get the reference of a cell containing a certain text with wildcards

actually the answer for this question is already here.
Get the reference of a cell containing a certain text
which can be done without using macros,
But the problem I have is that I want to search for a certain text with a wild card.
example: DOM???text
You can replace the InStr() function for the Like Operator. Code would look like:
Public Function WhereIs(rIn As Range, sIn As String) As String
WhereIs = ""
Dim r As Range
For Each r In rIn
If r.Text Like sIn Then
WhereIs = r.Address(0, 0)
Exit Function
End If
Next r
End Function
You have to make sure that your sIn string has the correct wildcards.
Try this. Just run it and search for the word in the dialog box. Then it will give you the cell reference.
Option Explicit
Private Sub FindText()
Dim ws As Worksheet
Dim FindString As Variant
Dim rng As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
FindString = InputBox("Search for value")
If Trim(FindString) <> "" Then
Set rng = ws.Cells.Find( _
What:=FindString, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not rng Is Nothing Then
MsgBox rng.Address
Else
MsgBox "Nothing found"
End If
End If
End Sub

Using Range.Find in VBA to find only the previous value x?

I am trying to use the Range.Find method in VBA to locate the closest previous row number that has a "true" value.
For example, in Column X, there will be a "true" value (row 35), 10 rows with "false," and then "true" again (row 46).
When I get to row 46 in my loop, I need to do a range.find and return row 35.
The code I am using is this:
Worksheets("Data").Cells.Find(True, searchorder:=xlByColumns, searchdirection:=xlNext).Row
What is happening is that I am only finding either the very first "true" value (in this case, row 2), or the very last "true" value (row 24,xxx), as I vary search direction.
What can I do to find only the previous-most "true" value?
You can find the previous row with True by using the After argument in the Find method combined with xlPrevious as the SearchDirection. I have updated the code to add it into a loop, based on your comments.
Since you posted your code, I have edited my answer into your code.
Sub Main()
Dim iCurRow As Long
Dim iCounter As Long
Dim iLastRow As Long
Dim iTempRow As Long
Dim iPreviousRow As Long
Dim iChangeCol As Long
Dim ws As Worksheet
Set ws = Worksheets("Data")
With ws
iChangeCol = .Cells.Find(what:="Change Over?", searchorder:=xlByColumns, searchdirection:=xlNext).Column
iLastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
iPreviousRow = 2
For iCounter = 3 To iLastRow
If .Cells(iCounter, iChangeCol).Value = True Then
iTempRow = .Cells.Find(what:=True, After:=.Cells(iCounter, iChangeCol), searchorder:=xlByColumns, searchdirection:=xlPrevious).Row
iPreviousRow = iTempRow
End If
Next iCounter
End With
End Sub
This short snippet uses both the Range.Find method and Range.FindNext method to cycle through all matching cells in column X.
Sub rings_true()
Dim fnd As Range
With Worksheets("Sheet1") `<~~ set this worksheet reference properly
With .Columns(24)
Set fnd = .Find(What:="TRUE", after:=.Cells(.Rows.Count), _
LookIn:=xlValues, LookAt:=xlWhole, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
Do While Not fnd Is Nothing
If MsgBox("Currently at " & fnd.Address(0, 0) & Chr(10) & "exit now...?", vbYesNo + vbQuestion) = vbYes Then
Exit Do
Else
Set fnd = .FindNext(after:=fnd)
End If
Loop
End With
End With
End Sub
The current cell address is reported through a MsgBox function. The same MsgBox offers the user the opportunity to break the loop.
Additional error control might include confirming at least one matching value in column X before entering into the loop.
There are multiple arguments to put into the Find method, regarding what you told us, I suggest that you use :
After:=.Cells(.Rows.Count, 1) to start from the bottom of the column
LookIn:=xlValues
LookAt:=xlWhole
SearchOrder:=xlByRows to look row by row (instead of column by column)
SearchDirection:=xlPrevious to look "back", from bottom to top
MatchCase:=False
SearchFormat:=False
And furthermore, you can use the .Find method into a specific range, so rather than Worksheets("Data").Cells.Find(..., you should use Worksheets("Data").Range("X:X").Find(... to look only in the column X.
Here is your amended code :
Sub test_ilarson007()
Dim FirstAddress As String, PreviousMatch As Range, cF As Range
Worksheets("Data").Activate
With Worksheets("Data").Range("X:X")
'First, define properly the Find method
Set cF = .Find(What:=True, _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False, _
SearchFormat:=False)
'If there is a result,
If Not cF Is Nothing Then
FirstAddress = cF.Address
MsgBox "The row containing the previous 'True' in Column X is : " & cF.Row
'keep looking with FindNext method : Not usefull for your example
Do
Set PreviousMatch = cF
Set cF = .FindNext(cF)
'-------------------------------------------------------------
'----Place instructions to execute on the matched cell/row/...
'First match (i.e. Row 46 in your example)
MsgBox PreviousMatch.Row 'Should display 46 (then 35, then ??)
'Second match (i.e. Row 35 in your example)
MsgBox cf.Row 'Should display 35 (then ??, then ??)
'-------------------------------------------------------------
'Look until you find again the first result
Loop While Not cF Is Nothing And cF.Address <> FirstAddress
End If
End With
End Sub

Resources