Way to not use goto loops in vba msgbox - excel

I am trying to find a way to avoid using GoTo loops in VBA as i understand they can lead to serious confusion and issues. I have a user InputBox, where the user defines a variables, currently on the if statement there are 3 options, if = Y ElseIf = N and else GoTo Start.
However this works great for the case where the user mistypes the variable i.e Y# etc, but i currently run into issues when the user wants to close the input box i.e clicks cancel or the cross.
So i was wondering if there is a more elligent solution to this or am I stuck with this hicup?
My code is below, this is only a test set used to test this new feature i am adding to my main code.
Sub MsgBox_Test ()
Dim TestVariable As String
VariableEntrey: TestVariable = InputBox(" Y or N")
If TestVariable = "Y" Or TestVariable = "y" Then
MsgBox ("Yeyy")
ElseIf TestVariable = "N" Or TestVariable = "n" Then
MsgBox ("Awww")
Else: GoTo VariableEntrey
End If
End Sub
Thanks for any help you are able to provide

You can try a Do... Loop Until construct that just repeats until you get an acceptable answer. For example:
Sub GetAnswer()
Dim strAnswer As String
Do
strAnswer = InputBox("Y or N")
Loop Until strAnswer Like "[YyNn]"
MsgBox "Thanks for your answer of: " & strAnswer
End Sub
See the docs on the Like operator to prevent having to check individually for y, Y, n and N.

Option Explicit
Sub MsgBox_Test()
Dim TestVariable As String
Dim done As Boolean
Do
TestVariable = InputBox(" Y or N")
done = True ' preload exit value
If LCase(TestVariable) = "y" Then
MsgBox ("Yeyy")
ElseIf LCase(TestVariable) = "n" Then
MsgBox ("Awww")
ElseIf Len(TestVariable) > 0 Then
done = False ' abort exit
End If
Loop Until done
End Sub

I would use a MsgBox instead of an InputBox.
Sub GetYesNo()
Dim answer As VbMsgBoxResult
answer = MsgBox("Yes or No?", vbYesNo)
If answer = vbYes Then
'do something
Else
'do something else
End If
End Sub

You could use Do >> Loop Until loop, to remove the GoTo.
Also, you can use UCase(TestVariable) to remove the Or from your Ifs.
Sub MsgBox_Test()
Dim TestVariable As String
Do
TestVariable = InputBox(" Y or N")
If UCase(TestVariable) = "Y" Then
MsgBox ("Yeyy")
ElseIf UCase(TestVariable) = "N" Then
MsgBox ("Awww")
End If
Loop Until UCase(TestVariable) Like "[YN]"
End Sub

You could try with Do .... Loop here:
Edit: plus limitation for single character or empty input
Dim TestVariable As String
TestVariable = InputBox(" Y or N")
Do While (TestVariable = "N" Or TestVariable = "n" or Len(TestVariable) > 1)
MsgBox ("Awww")
TestVariable = InputBox(" Y or N")
Loop
If TestVariable = "Y" Or TestVariable = "y" Then
MsgBox ("Yeyy")
End if

Related

BeforeUpdate event validation control

Dears,
I want to make a simple userform to record some serial numbers into excel, it contains a textbox_serialNo., a command button “enter” and another command button “cancel”.
I made a validation control in that serialNo textbox so that only number can be entered. However, when I run the program and input some numbers into the textbox, both command buttons (the "enter" button named as label_enter,the "cancel" button named as label_cancel) have no reactions (e.g. the "cancel" button doesn't unload the form when press) , how should I correct the program? Below are the relevant codes, Thanks.
Private Sub TextBox_SerialNo_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
If Not IsNumeric(TextBox_SerialNo.Value) Then
TextBox_SerialNo.BackColor = rgbYellow
End If
Cancel = True
End Sub
Private Sub TextBox_SerialNo_AfterUpdate()
If TextBox_SerialNo.Value <> "" Then
TextBox_SerialNo.BackColor = rgbWhite
End If
End Sub
Private sub label_enter_click()
sheet1.Select
Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Select
ActiveCell.Value = ActiveCell.Offset(-1, 0).Value + 1
ActiveCell.Offset(0, 1) = TextBox_SerialNo.Value
TextBox_SerialNo.Value = ""
End Sub
Private Sub Label_Cancel_Click()
Unload Me
End Sub
Sorry to be posting as an answer, not enough rep.
Shouldn't Cancel=True be inside the if statement? You are locking it up regardless of entry being numeric or not as is.
Edit:
Actually upon further testing still not working proper. However, change event works better and you can get instant feedback for any non numerics.
Updated code would look like this, control names differ. I am used to working with .Text, same thing as .Value. Also, since I am not sure what you would do with an empty string, assumed it to be yellow background as well.
One concern would be, can you allow comma or period in there? Depending on locale settings, a decimal would also be considered a numeric.
Private Sub cmdCancel_Click()
Unload Me
End Sub
Private Sub cmdEnter_Click()
If TextBox1.BackColor = rgbYellow Then Exit Sub
test4.Range("A1").Value = TextBox1.Text
End Sub
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1.Text) Or TextBox1.Text = "" Then
TextBox1.BackColor = rgbYellow
Else
If TextBox1.Text <> "" Then
TextBox1.BackColor = rgbWhite
End If
End If
End Sub
Edit 2: I use this piece of code to check for only numbers (assuming number Ascii codes are standard). Maybe it can help.
Public Function isnumber(ByVal strValue As Variant) As Boolean
On Error Resume Next
Dim i As Long
isnumber = True
If Not strValue = "" Then
For i = 1 To Len(CStr(strValue))
If Asc(Mid(strValue, i, 1)) > 57 Or Asc(Mid(strValue, i, 1)) < 48 Then
isnumber = False
Exit For
End If
Next i
Else
isnumber = False
End If
On Error GoTo 0
Err.Clear
End Function
Edit 3: I have revised the TextBox1_Change event code so all invalid characters are stripped right away. However, in this state if you copy paste a serial no with a non-allowed char, it will strip them leaving only the numbers. Not sure if it is acceptable.
Private Sub TextBox1_Change()
If Not isnumber(TextBox1.Text) Or TextBox1.Text = "" Then
TextBox1.BackColor = rgbYellow
Dim i As Long
Dim strValue As String
strValue = ""
If Not TextBox1.Text = "" Then
For i = 1 To Len(CStr(TextBox1.Text))
If Not (Asc(Mid(TextBox1.Text, i, 1)) > 57 Or Asc(Mid(TextBox1.Text, i, 1)) < 48) Then
strValue = strValue & Mid(TextBox1.Text, i, 1)
End If
Next i
End If
TextBox1.Text = strValue
Else
If TextBox1.Text <> "" Then
TextBox1.BackColor = rgbWhite
End If
End If
End Sub

Inputbox prevent user from pressing enter when value is blank

I have the following code :
Range("P" & Rows.Count).End(xlUp).offset(1).Value = InputBox("INTRODUCE ACT JUSTIFICATIV")
User must enter letters in inputbox
I want to prevent the user from accidentally pressing enter if value in inputbox is blank with a personalized error message.
And pressing cancel button to exit sub.
Set your Users input to a variable and then test to see whether it is valid or not.
Dim usrInput as String
Do
usrInput = InputBox("INTRODUCE ACT JUSTIFICATIV")
If usrInput = vbNullString Then
MsgBox "Your Error Message"
End If
Loop While usrInput = vbNullString
I don't think that you can force the InputBox to close only if something was entered. Instead, read the result into variable and loop until something valid was entered
Dim answer as string, okay As Boolean
answer = ""
okay = False
Do While Not okay
answer = InputBox("INTRODUCE ACT JUSTIFICATIV")
On Error Resume Next
okay = Range(answer & "1").Address <> ""
On Error GoTo 0
Loop
Now, the InputBox will pop up until a valid column was entered.
Try the next approach, please:
Sub testInputBoxValidate()
Dim strInpB As String
strInpB = InputBox("INTRODUCE ACT JUSTIFICATIV")
If strInpB = "" Then MsgBox "You did not introduce anything!": Exit Sub
Range("P" & Rows.count).End(xlUp).Offset(1).Value = strInpB
End Sub
And a variant to oblige the user inputting something:
Sub testInputBoxValidate()
Dim strInpB As String
Start:
strInpB = InputBox("INTRODUCE ACT JUSTIFICATIV")
If strInpB = "" Then MsgBox "You did not introduce anything!": GoTo Start
Range("P" & Rows.count).End(xlUp).Offset(1).Value = strInpB
End Sub
I would also suggest you to try some more other checking. For instance, that 'ACT JUSTIFICATIV' must not have a specific length (number of digits)? A specific pattern like `xxxx###', where '###' is a three (x) digits number?
You can use Application.InputBox which gives you a little more control. The user will be prompted until they either enter a non-blank string or press the cancel or x buttons
Dim vInput As Variant: vInput = ""
Do
vInput = Application.InputBox("INTRODUCE ACT JUSTIFICATIV", Type:=2) '2 -> String type
If vInput = False Then 'User pressed Cancel or x button
Exit Do
ElseIf Len(vInput) > 0 Then 'User has entered non-blank value
Range("P" & Rows.Count).End(xlUp).Offset(1).Value = vInput
Exit Do
End If
Loop

Issues with Index Match Match and Userform in VBA

I am trying to run code to use sap_a and sap_b (both text boxes in a userform) to look up the variable 'Run'. I wrote the code so that the person entering the sap_a and sap_b values could enter the values in either box and the 'Run' variable would be found, which is why ErrorCheck1 exists. ErrorCheck2 exists simply to provide a message if the values in sap_a/b are unable to be matched with a 'Run' variable. The issue I'm having is that this code generates a 'Run' value even when one of the sap_a or sap_b values is not correct (i.e. sap_a is a value that does exist in the spreadsheet, sap_b is a fake value not existing in the spreadsheet, and a 'Run' variable is still produced). Do you have any input specifically regarding issues with my code or any issues you see? Code is attached.
Thank you!
Private Sub SearchButtonTEST_Click()
Dim sap_a As Variant
Dim sap_b As Variant
Dim Run_ As Variant
Sheets("R_Database Sheet").Activate
sap_a = textbox5.Value
sap_b = textbox8.Value
If sap_a = "" And sap_b = "" Then
Run_ = ""
Let textbox1.Text = Run_
Msgbox "Must enter SAP Codes in SAP # A and SAP # B to search."
Exit Sub
Else
Check1:
On Error GoTo ErrorCheck1
Run_ = Application.WorksheetFunction.Index(Sheets("R_Database Sheet").Range("A:A"), Application.WorksheetFunction.Match(CLng((sap_a)), Sheets("R_Database Sheet").Range("E:E"), Application.WorksheetFunction.Match(CLng((sap_b)), Sheets("R_database sheet").Range("H:H"), 0)))
Let textbox1.Text = Run_
Check2:
On Error GoTo ErrorCheck2
Run_ = Application.WorksheetFunction.Index(Sheets("R_Database Sheet").Range("A:A"), Application.WorksheetFunction.Match(CLng((sap_b)), Sheets("R_Database Sheet").Range("E:E"), Application.WorksheetFunction.Match(CLng((sap_a)), Sheets("R_database sheet").Range("H:H"), 0)))
Let textbox1.Text = Run_
Exit Sub
Check3:
Msgbox "No data found for specified SAP #'s."
End If
Exit Sub
ErrorCheck1:
Resume Check2
ErrorCheck2:
Resume Check3
End Sub
Your code will be easier to manage if you drop the Worksheetfunction and just use Application.Match
If you include the worksheetfunction then a run-time error is raised if there's no match (requiring tricky error handling). If you drop it, then a no-match just returns an error value which you can test using IsError(). Personally I find this much easier to manage.
Private Sub SearchButtonTEST_Click()
Dim sap_a As Variant, sap_b As Variant
Dim ws As Worksheet, mA, mB
Set ws = Sheets("R_Database Sheet")
sap_a = Trim(textbox5.Value)
sap_b = Trim(textbox8.Value)
If sap_a = "" And sap_b = "" Then
textbox1.Text = ""
MsgBox "Must enter SAP Codes in SAP # A and SAP # B to search."
Exit Sub
Else
mA = Application.Match(CLng(sap_a), ws.Range("E:E"), 0)
mB = Application.Match(CLng(sap_b), ws.Range("H:H"), 0)
If Not IsError(mA) Then
textbox1.Text = ws.Cells(mA, "A")
ElseIf Not IsError(mB) Then
textbox1.Text = ws.Cells(mB, "A")
Else
textbox1.Text = "Not found!"
End If
End If
End Sub

Compress multiple OR-conditions in VBA code

I use the following code to allow users to write a value into Cell A1.
Sub TestUsername()
If Environ("Username") = "firstname1.lastname1" Or Environ("Username") = "firstname2.lastname2" _
Or Environ("Username") = "firstname3.lastname3" Or Environ("Username") = "firstname4.lastname4" Then
Sheet1.Range("A1").Value = 1
Else
Sheet1.Range("A2").Value = 2
End If
End Sub
As you can see I list each user who is allowed to enter a value into Cell A1 with an OR-condition in my VBA code. All this works fine.
Now, I was wondering if there is an easier way to do this. Something like this:
Sub TestUsername()
If List of or-conditions: {"firstname1.lastname1", "firstname2.lastname2", _
"firstname3.lastname3", "firstname4.lastname4"} = True Then
Sheet1.Range("A1").Value = 1
Else
Sheet1.Range("A2").Value = 2
End If
End Sub
I just know in PHP you can compress multiple conditions like here. Therefore, I thought this might also be possible for VBA programming.
Maybe something like this
Sub TestUsername()
Select Case Environ("Username")
Case "firstname1.lastname1", "firstname2.lastname2", "firstname3.lastname3"
Sheet1.Range("A1").Value = 1
Case Else
Sheet1.Range("A2").Value = 2
End Select
End Sub
I suppose, if you had an atrocious amount of conditions, you could stick them in an array and then simply replace your conditional statement
If Environ("Username") = "firstname1.lastname1" Or Environ("Username") = "firstname2.lastname2" _
Or Environ("Username") = "firstname3.lastname3" Or Environ("Username") = "firstname4.lastname4" Then
with this
If IsInArray(Environ("Username"), arr) Then
This does require that you dimension an array with the conditions first and use this function, however:
Public Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
Dim i
For i = LBound(arr) To UBound(arr)
If arr(i) = stringToBeFound Then
IsInArray = True
Exit Function
End If
Next i
IsInArray = False
End Function
This way, your code becomes much more readable and easy to maintain.
Since you're working in a cell, you might want to define the allowed usernames within the spreadsheet.
Here's how the spreadsheet table might look:
And here's the code you might use:
Sub TestUsername()
Dim username As String
Dim userInTable As Integer
Dim allowedUserRange As Excel.Range
username = Environ("username")
Set allowedUserRange = Excel.Range("tUsers")
userInTable = Excel.WorksheetFunction.CountIf(allowedUserRange, username)
If userInTable Then
Sheet1.Range("A1").Value = 1
Else
Sheet1.Range("A1").Value = 2
End If
End Sub
The Select Case provides a great solution to testing multiple conditions at the same time. I am using this to alert the user when they have not furnished all the required inputs. I am monitoring inputs from a number of Drop Down Boxes as well as some direct cell inputs.
Select Case True
Case Range("Customer_DD_Control_Cell") > 0 _
And Range("Dealer_DD_Control_Cell") > 0 _
And Range("Rep_DD_Control_Cell") > 0 _
And Range("Product_DD_Control_Cell") > 0 _
And Len(Range("Customer_State_Input")) > 0 _
And Len(Range("Contract_Date_Input")) > 0
Case Else
MsgBox "You have not completed the required inputs"
End Select

Error on VB Function returning Array

The following is my VB code. I want to count all the distinct records about "Peter" in a spreadsheet without duplication.
When I run the code, "Run-time error '13':Type Mismatch" always appear. I fail to debug. What's wrong with my code?
Private Sub CheckBox5_Click()
Dim myarray As Variant
myarray = WorksheetFunction.If(Range("C7:C266") = "Peter", 1 / (WorksheetFunction.CountIfs(Range("C7:C266"), "Peter", Range("F7:F266"), Range("F7:F266"))), 0)
If CheckBox5.Value = True Then
TextBox6.Value = WorksheetFunction.Sum(myarray) + 1
End If
If CheckBox5.Value = False Then
TextBox6.Value = ""
End If
End Sub
The error you are getting is a result of the way the IF function is called. The first term must be a logical result, but you cannot call the value of a multi-cell Range (ie Range("C7:C266")). To solve this problem, I think you will need to loop through each of the cells and act on them accordingly, although there may be a more clever solution using something other than IF that I am not aware of
You can do it like this:
Sub findPeter()
Dim ws As Worksheet
Dim peterCount As Long
Set ws = Worksheets("nameofyoursheet")
With ws
For i = 7 To 266
If .Cells(i, 3) = "Peter" Then
peterCount = peterCount + 1
End If
Next
End With
If CheckBox5.Value = True Then
TextBox6.Value = peterCount + 1
End If
If CheckBox5.Value = False Then
TextBox6.Value = ""
End If
End Sub
peterCount is the sum of all occurences of the value Peter.

Resources