FindNext from InputBox throughout Workbook - vba

Noob here. I've found plenty of code on this site and want to say thanks to all those who contribute.
My question is I have a UserForm. I click a button to bring up an InputBox, where they enter a value to search for bank name, banker name, business name, etc.
I have the code to do the search no problem, but I want to be able to be able to continue to search all of the instances of the InputBox value. For example, searching for the name "Smith" and if the first one isn't the one I need, to continue the search until I've landed on the one I'm looking for.
Dim ws As Worksheet
Dim rFound As Range
Dim strName As String
On Error Resume Next
strName = InputBox("Please Enter Search Value." & vbNewLine & "Entry Must Be Exact Cell Value!", "Search Value")
If strName = "" Then Exit Sub
For Each ws In Worksheets
With ws.UsedRange
Set rFound = .Find(What:=strName, After:=.Cells(1, 1), LookIn:=xlValues, LookAt:=xlPart)
If Not rFound Is Nothing Then
firstaddress = rFound.Address
Application.Goto rFound, True
Exit Sub
End If
End With
Next ws
On Error GoTo 0
MsgBox "Merchant not found. Please make sure you typed it correctly.", vbOKOnly + vbCritical, "Invalid Entry"

You need to modify your search so your code 'remembers' where it left off, like this:
Option Explicit
Dim ws As Worksheet
Dim rFound As Range
Dim strName As String
Static First as Range
'On Error Resume Next
if First is Nothing Then 'we haven't found anything yet
Set First = Worksheets(1).Cells(1,1) 'start searching at the beginning
End If
strName = InputBox("Please Enter Search Value." & vbNewLine & "Entry Must Be Exact Cell Value!", "Search Value")
If strName = "" Then Exit Sub
For Each ws In Worksheets
With ws.UsedRange
Set rFound = .Find(What:=strName, After:=First, LookIn:=xlValues, LookAt:=xlPart)
while Not rFound Is Nothing
if first is nothing then
First = rFound 'store off this address for use in our next search
end if
if first <> rFound Then 'we've found a NEW instance of the search item
firstaddress = rFound.Address
Application.Goto rFound, True
MsgBox "Found one!"
Set rFound = .Find(What:=strName, After:=rFound, LookIn:=xlValues, LookAt:=xlPart)
else 'we're back at the start, so jump out of the loop
set rFound = Nothing
End If
wEnd
End With
Next ws
On Error GoTo 0
MsgBox "Merchant not found. Please make sure you typed it correctly.", vbOKOnly + vbCritical, "Invalid Entry"
A couple of points:
I added Option Explicit which means your code won't run now because you never declared firstaddress. It's vital for your sanity to enable that option from the ribbon: Tools | Options | Editor then check Require Variable Declaration
By declaring First as a Static, it will remain set between calls to your search routine. That way, since we're feeding First into the .Find() function, it will pick up the search where it left off.
If you need to start the search from the beginning again, you could store off the 'last' search term - if the current term is different from the last term, reset set First = Worksheets(1).Cells(1,1)
Additional note - On Error Resume Next is useful in very limited situations. This isn't one of them. It allows you to ignore an error in your code so that you can immediately handle it, which isn't what you want in this situation. The follow up On Error Goto 0, which reenables the default error handling should really never be more than 1 line of code later - not an entire subroutine.

VBA already has a .FindNext() method for this very purpose:
Sub SO()
Dim inputString As String
Dim foundCell As Excel.Range
Dim wSheet As Excel.Worksheet
Dim foundAddress As String
inputString = InputBox("Please enter search term:", "Search")
For Each wSheet In ActiveWorkbook.Worksheets
Set foundCell = wSheet.Cells.Find(inputString, , -4163, 2)
If Not foundCell Is Nothing Then
Application.Goto foundCell, True
foundAddress = foundCell.Address
If MsgBox("Match in " & wSheet.Name & " (" & foundCell.Address & ")" & vbCrLf & "Continue?", 68, "Match Found") = vbYes Then
Do
Set foundCell = wSheet.Cells.FindNext(foundCell)
If Not foundCell Is Nothing And Not foundCell.Address = foundAddress Then
Application.Goto foundCell, True
Else
Exit Do
End If
Loop While MsgBox("Match in " & wSheet.Name & " (" & foundCell.Address & ")" & vbCrLf & "Continue?", 68, "Match Found") = vbYes And _
Not foundCell Is Nothing And Not foundCell.Address = foundAddress
Set foundCell = wSheet.Cells.FindNext(foundCell)
End If
End If
If MsgBox("All matches in this sheet found - move to next sheet?", 68, "Next Sheet?") = vbNo Then Exit Sub
Next wSheet
End Sub

Related

Opening Outlook through VBA crashes for unknown reason

I am using this code to open a list of email-adresses directly in outlook. The email list consists of roughly 100 email-adresses.
If I let the code run for only parts of the list it works just fine for all different parts, but as soon as I let it run for the whole list I get a runtime error '5. Does anyone have a suggestion what causes this problem? I would be very thankful.
If ActiveWorkbook.Worksheets("Output").Range("I10").Value = "Wahr" Then
Dim strAddress As String
Dim lastCell As Long
Dim i As Integer
Worksheets("Output").Activate
lastCell = Range("B" & Rows.Count).End(xlUp).Row
For i = 13 To lastCell
If strAddress = "" Then
strAddress = Cells(i, 2).Value
Else
strAddress = strAddress & ";" & Cells(i, 2).Value
End If
Next i
ActiveWorkbook.FollowHyperlink Address:="mailto:" & strAddress 'this line gives me the error
End If
EDIT: The weird thing is, that it doesnt really matter which "groups" I choose. It seems to be a question of how many adresses I pick.
Not sure what you mean by I am using this code to open a list of email-adresses directly in outlook.
The code appears to create a single blank email with each cell in B13 downwards providing the email addresses?
Maybe this code below will help.
It uses late binding (so no references needed) to get a reference to Outlook, it then creates an email and adds the email addresses to it as recipients before finally displaying it. You can change the .Display to .Send to send the email rather than just display it.
Public Sub Test()
Dim oOL As Object
Dim oMail As Object
Dim rLastCell As Range
Dim rAddRange As Range
Dim rCell As Range
Set oOL = CreateOL
With ThisWorkbook.Worksheets("Output")
Set rLastCell = .Cells(.Rows.Count, 2).End(xlUp)
Set rAddRange = .Range("B13", rLastCell)
End With
Set oMail = oOL.CreateItem(0)
With oMail
For Each rCell In rAddRange
.Recipients.Add rCell.Value
Next rCell
.Display
End With
End Sub
Public Function CreateOL() As Object
Dim oTmpOL As Object
On Error GoTo ERROR_HANDLER
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Creating an instance of Outlook is different from Excel. '
'There can only be a single instance of Outlook running, '
'so CreateObject will GetObject if it already exists. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set oTmpOL = CreateObject("Outlook.Application")
Set CreateOL = oTmpOL
On Error GoTo 0
Exit Function
ERROR_HANDLER:
Select Case Err.Number
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure CreateOL."
Err.Clear
End Select
End Function

How can make Sheet to activate in VBA from a variable? (A cell particularly)

I am actually quite new to VBA but I am doing some coding to streamline my office work. I understand this would be some amateur level questions to most of you but I tried to google for quite a while and I do not find satisfactory answer.
I have an excel write up that based on the inputted parameters, It should ultimately refer to the correct sheet -> copy the selected cells -> Generate an e-Mail with the body pasting the copied cells along with an attachment
I can do most of the parts, just that I cannot reference the "Correct Sheet" as a variable in my codes. Please shed some lights on for me. Thank you.
Here are most of the codes, the rest are irrelevant and too clumsy to paste all I guess.
Sub GenerateEmail()
Dim olApp As Object
Dim olMailItm As Object
Dim iCounter As Integer
Dim Dest As Variant
Dim SDest As String
Dim StrAtt1 As String
Dim rng As Range
Set rng = Nothing
On Error Resume Next
Set rng = Sheets("test").Range("A1:Q500").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set olApp = CreateObject("Outlook.Application")
Set olMailItm = olApp.CreateItem(0)
On Error Resume Next
With olMailItm
SDest = ""
StrAtt1 = ThisWorkbook.Path & "\PDF\" & Sheets("Email_Generator").Range("B16")
.To = Worksheets("Email_Generator").Range("B14")
.CC = "Myself"
.BCC = ""
.Subject = Worksheets("Email_Generator").Range("B18")
.HTMLBody = RangetoHTML(rng)
.attachments.Add StrAtt1
.Display
End With
Set olMailItm = Nothing
Set olApp = Nothing
End Sub
Specifically, I would like this code "Sheets("test") as a Cell in Sheet "Test" that is a variable based on the paramters I have inputted in my excel so that this code will reference to the correct worksheet
Set rng = Sheets("test").Range("A1:Q500").SpecialCells(xlCellTypeVisible)
But when I identify the sheet as a named sheet e.g. Sheets("Email1"), it perfectly works, just that it cannot become a variable.
I hope this post is not too long to read because I tried to be as specific as possible. Thank you to all who reads this and tries to help. I really appreciate it.
This function to return the worksheet name selected by the user from an InputBox. If the user cancels or enters an invalid number then the function returns a zero length string.
Sub TestFunction()
Dim SheetName As String
Dim rng As Range
SheetName = getSheetNameFromInputBox
If Len(SheetName) = 0 Then
MsgBox Prompt:="Try Again", Title:="Invalid Sheet"
Exit Sub
End If
Set rng = Sheets(SheetName).Range("A1:Q500").SpecialCells(xlCellTypeVisible)
MsgBox rng.Address(External:=True)
End Sub
Function getSheetNameFromInputBox() As String
Dim ws As Worksheet
Dim Prompt As String
Dim result
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name = "Sheet3" Then
Prompt = Prompt & ws.Index & ") " & ws.Name & vbCrLf
End If
Next
result = InputBox(Prompt:=Prompt, Title:="Enter the Worksheet number", Default:=1)
On Error Resume Next
If IsNumeric(result) Then getSheetNameFromInputBox = Worksheets(CInt(result)).Name
On Error GoTo 0
End Function

IF Null exit sub code

I'm trying to check that if cells are empty or null, that it would display a message and exit sub. Here's my code:
With Worksheets(1).[D3:D4, D6:D14]
If WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
End With
But the code only works if the entire [D3:D4, D6:D14] are empty. I want it to exit sub even just one of the cells are empty. All cells needs have content for it to continue sub. Please help.
You need to seach inside the Range, try the code below:
Sub CheckEmptyCellsinRange()
Dim Rng As Range
Dim cell As Range
Set Rng = Worksheets(1).[D3:D4, D6:D14]
For Each cell In Rng
If IsEmpty(cell) Or IsNull(cell) Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
Next cell
End Sub
You can also use SpecialCells:
Sub Check()
Dim rng1 As Range
On Error Resume Next
With Worksheets(1).[D3:D4, D6:D14]
Set rng1 = .SpecialCells(xlBlanks)
If Not rng1 Is Nothing Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
End With
End Sub
Follow-up question
Sub Check2()
Dim rng1 As Range
Dim rng2 As Range
With Worksheets(1)
Set rng1 = .Range(.[D3:D4], .[D6:D14])
End With
On Error Resume Next
Set rng2 = rng1.SpecialCells(xlBlanks)
On Error GoTo 0
If Not rng1 Is Nothing Then
MsgBox rng1.Cells.Count - rng2.Cells.Count & " were used"
Else
MsgBox "all cells used"
End If
End Sub

Copy visible columns only from Excel worksheet as CSV file in VB6 without using copy command

I have Excel worksheet object in which some columns are in invisible mode. I want to save those worksheets as CSV file with visible columns only. My primary requirement is not to use Copy method and csv file should contain all visible columns with value and format.
Private Sub SaveAsCSV_TSA(ByVal xl As Excel.Application, ByVal xlsheet As Excel.Worksheet, ByVal CSVSavePath As String)
On Error GoTo BottomLine
Set xlwbook1 = xl.Workbooks.Add
Dim xlsheet1 As Worksheet
Set xlsheet1 = xlwbook1.Sheets.Item(1)
xlsheet1.Activate
xlsheet.Cells.SpecialCells(xlCellTypeVisible).Copy
xlsheet1.Paste
xl.CutCopyMode = False
xlwbook1.SaveAs FileName:=CSVSavePath, FileFormat:=xlCSV
xlwbook1.Close SaveChanges:=False
Set xlwbook1 = Nothing
Set xlsheet1 = Nothing
BottomLine:
If Not xlsheet1 Is Nothing Then Set xlsheet1 = Nothing
If Not xlwbook1 Is Nothing Then Set xlwbook1 = Nothing
If Err.number > 0 And Err.number <> cdlCancel Then
MsgBox (Err.number & Chr(13) & Err.Description & " - Create_TS_Turn_file" & vbCrLf & "Line Number: " & Erl)
End If
End Sub
In the above case, xlsheet is a source, and xlsheet1 is a destination.
Note: Why I do not need to use copy command. Since, i have repeatedly calling the above method around (1000) times with different worksheet as parameter. (I have got the problem as cannot able to do other copy/paste work on the machine which this application runs. It causes that replace my original copied content with xlsheet.Cells.SpecialCells(xlCellTypeVisible).Copy content.
Please help me to resolve this.. I need to fix it soon. Thanks in advance!
edited as per OP's further specs
not so sure what's your issue but maybe this can help:
Option Explicit
Private Sub SaveAsCSV_TSA(ByVal xl As Excel.Application, ByVal xlsheet As Excel.Worksheet, ByVal CSVSavePath As String)
Dim xlwbook1 As Workbook
Dim xlsheet1 As Worksheet
Dim cell As Range
Dim colsAddr As String
On Error GoTo BottomLine
Set xlwbook1 = xl.Workbooks.Add
With xlwbook1
xlsheet.Copy After:=.Sheets.Item(1)
With .ActiveSheet '<~~ here starts the new "treatment"
With .UsedRange
For Each cell In .Rows(1).Cells '<~~ loop through first row cells
If cell.EntireColumn.Hidden Then colsAddr = colsAddr & cell.EntireColumn.Address & "," '<~~ store cell entire column address if hidden
Next cell
.Value = .Value '<~~ get rid of formulas and keep only their resulting values
End With
If colsAddr <> "" Then .Range(Left(colsAddr, Len(colsAddr) - 1)).Delete '<~~ delete hidden columns, if any
End With '<~~ here ends the new "treatment"
.SaveAs Filename:=CSVSavePath, FileFormat:=xlCSV
.Close SaveChanges:=False
End With
Set xlwbook1 = Nothing
Set xlsheet1 = Nothing
BottomLine:
If Not xlsheet1 Is Nothing Then Set xlsheet1 = Nothing
If Not xlwbook1 Is Nothing Then Set xlwbook1 = Nothing
If Err.Number > 0 And Err.Number <> xlCancel Then
MsgBox (Err.Number & Chr(13) & Err.Description & " - Create_TS_Turn_file" & vbCrLf & "Line Number: " & Erl)
End If
End Sub
which I suggest to call like follows
Sub main()
Application.ScreenUpdating = False '<~~ stop screen updating and speed things up
SaveAsCSV_TSA Application, ActiveSheet, "yourpath"
Application.ScreenUpdating = True '<~~ resume screen updating
End Sub

Excel vba username/ password lookup

Private Sub cmdLogin_Click()
On Error GoTo ErrorHandler
Dim RowNo As Long
Dim Id As String
Dim pw As String
Dim ws As Worksheets
Application.ScreenUpdating = False
Set ws = Worksheets("User&Pass")
Id = LCase(Me.txtLogin)
RowNo = Application.WorksheetFunction.Match(Id, ws.range("A2:A999"), 0)
CleanExit:
Set ws = Nothing ' free memory
Application.ScreenUpdating = True ' turn on the screen updating
Exit Sub
ErrorHandler:
MsgBox "Unable to match ID, enter valid ID.", vbOKOnly
GoTo CleanExit
End Sub
I've got an excel userform i've been working on and now I need it to look more professional by having a log-in screen. I've started with the code above but I have come to a dead end.
how its set up my aim is to say if id & password matches then load up workbook or unhide the workbook and continue. the username and password are on a sheet called "User&Pass"
Aim is it reads from there in columns a- user / b- pw respectively and if it's a success I will hide that sheet so they cant see other user's information
with what I started above I just need it to say if it matches usercolumn then corresponding pw next door to it continue else go to my errorhandler
i can do the formatting about hiding and unhiding sheets etc just need help with reading username and pw
thanks very much in advance
Z
Editted attempt one;
Private Sub cmdLogin_Click()
On Error GoTo ErrorHandler
Dim RowNo As Long
Dim Id As String
Dim pw As String
Dim ws As Worksheets
Application.ScreenUpdating = False
Set ws = Worksheets("User&Pass")
Id = LCase(Me.txtLogin)
RowNo = Application.WorksheetFunction.Match(Id, ws.range("A2:A999"), 0)
RowNo = RowNo + 1
pw = ws.range("B" & RowNo)
If pw = Me.txtLogin Then
'continue
txt1.Value = "yes"
Else
GoTo ErrorHandler
End If
CleanExit:
Set ws = Nothing ' free memory
Application.ScreenUpdating = True ' turn on the screen updating
Exit Sub
ErrorHandler:
MsgBox "Unable to match ID, enter valid ID.", vbOKOnly
GoTo CleanExit
End Sub
#siddarthRout
Private Sub cmdLogin_Click()
Dim RowNo As Long
Dim Id As String, pw As String
Dim ws As Worksheet
Dim aCell As range
On Error GoTo ErrorHandler
Application.ScreenUpdating = True
Set ws = Worksheets("Details")
Id = LCase(Me.txtLogin)
Set aCell = ws.Columns(1).Find(What:=Id, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'~~> If match found
If Not aCell Is Nothing Then
RowNo = aCell.Row
'~~> Rest of your code. For example if the password is
'~~> Stored in Col B then
Debug.Print aCell.Offset(, 1)
Unload Me
FrmMenu.Show
'~~> You can then use the above aCell.Offset(, 1) to
'~~> match the password which the user entered
Else '<~~ If not found
MsgBox "Unable to match ID, enter valid ID.", vbOKOnly
End If
CleanExit:
Set ws = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
MsgBox Err.Description
Resume CleanExit
End Sub
TESTED AND TRIED
Is this what you are trying?
CODE
Option Explicit
Private Sub cmdLogin_Click()
Dim RowNo As Long
Dim Id As String, pw As String
Dim ws As Worksheet
Dim aCell As Range
On Error GoTo ErrorHandler
If Len(Trim(txtLogin)) = 0 Then
txtLogin.SetFocus
MsgBox "Username cannot be empty"
Exit Sub
End If
If Len(Trim(txtPassword)) = 0 Then
txtPassword.SetFocus
MsgBox "Password cannot be empty"
Exit Sub
End If
Application.ScreenUpdating = False
Set ws = Worksheets("User&Pass")
Id = LCase(Me.txtLogin)
Set aCell = ws.Columns(1).Find(What:=Id, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'~~> If match found
If Not aCell Is Nothing Then
RowNo = aCell.Row
If Me.txtPassword = aCell.Offset(, 1) Then
FrmMenu.Show
Unload Me
Else
MsgBox "Unable to match UserID or PasswordID, Please try again", vbOKOnly
End If
Else '<~~ If not found
MsgBox "Unable to match UserID or PasswordID, Please try again", vbOKOnly
End If
CleanExit:
Set ws = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
MsgBox Err.Description
Resume CleanExit
End Sub
TIP:
Never let your user know (from security perspective) what was incorrect - The username or the password. Always show a generic message like "Unable to match UserID or PasswordID, Please try again" :)
HTH
Sid
Another way
On Error Resume Next
If Me.password <> Application.VLookup(Me.username, Sheet1.Cells(1, 1).CurrentRegion, 2, False) Then
MsgBox ("incorrect")
Exit Sub
Else
MsgBox ("Correct Password Entered")
End If
Also you will need to make sure that all your sheets are xlSheetVeryHidden from the outset to combat having macros disabled and un-hide them as part of your successful log in routine. You'll also want to set a password on your VBA project to prevent people unhiding the sheets. Bear in mind however, Excel is about as secure as a wet paper bag ;)