Problems with adding variants together VBA - excel

I am fairly new to VBA programming and how the different data types work, so I am running into a problem. I am adding two different numbers (rng and rng1) in an inputbox. If the user presses cancel the program closes down and the sheet locks. If I use e.g. an integer instead of a variant I can't run the close down code. However, when I try do rng + rng1 it does not add them, but instead glues them together, i.e. if grp = 2 and grp1 = 3 then grp + grp1 = 23. This messes with my plot function. So I am hoping that someone can help me figure out the correct data types to use? Or a different solution to the problem. The code can be seen below.
dim grp As variant
dim grp1 As variant
Application.DisplayAlerts = False
On Error Resume Next
grp = InputBox("Enter No in Group 1")
On Error GoTo 0
Application.DisplayAlerts = True
If grp = "" Then
MsgBox ("User canceled!")
ActiveSheet.Protect Password:="..."
Exit Sub
End If
Application.DisplayAlerts = False
On Error Resume Next
grp1 = InputBox("Enter No in Group 2")
On Error GoTo 0
Application.DisplayAlerts = True
If grp1 = "" Then
MsgBox ("User canceled!")
ActiveSheet.Protect Password:="..."
Exit Sub
End If
ActiveSheet.ChartObjects("Chart1").Activate
With ActiveChart
I = 3
Do Until I = grp + 3
ActiveChart.FullSeriesCollection(I).Select
ActiveChart.SeriesCollection(I).Select
With Selection
.Border.LineStyle = xlContinuous
.Border.Color = RGB(0, 255, 0)
.MarkerBackgroundColor = RGB(0, 255, 0)
.MarkerForegroundColor = RGB(0, 255, 0)
End With
I = I + 1
Loop
j = grp + 3
Do Until j = grp + grp1 + 3
ActiveChart.SeriesCollection(j).Select
With Selection
.Border.LineStyle = xlContinuous
.Border.Color = RGB(0, 0, 255)
.MarkerBackgroundColor = RGB(0, 0, 255)
.MarkerForegroundColor = RGB(0, 0, 255)
End With
j = j + 1
Loop

it does not add them, but instead glues them together, i.e. if grp = 2 and grp1 = 3 then grp + grp1 = 23
InputBox returns a String type. What a lot of people don't realize is that you can use either the & or the + operator to combine strings, and that's what you're doing:
"2" + "3" = "23" '// equivalent to "2" & "3"
Whereas:
2 + 3 = 5
So because your parameters are of type String, the + operator assumes that you're trying to combine them, there's no implicit type conversion to Int or Long or Double, because the operator is perfectly valid for combining strings, which is what you gave it :)
NOTE: It's usually recommended to only use the & operator, that way it's less ambiguous that you're combining strings, versus adding long/integer values.
To handle the resulting input as a numeric type (i.e., to perform addition or other arithmetic operation), then you need to be working with numeric data (Integer/Long/Double type) instead of String type. You could do an explicit typecast like:
Dim grp as Long
grp = CLng(InputBox("Enter No in Group 1"))
Or, more preferably, use the Type argument of the InputBox function:
Dim grp as Long
grp = InputBox("Enter No in Group 1", Type:=1)
Same for grp2.

Because neither input can be 0, this will work for you just fine:
Dim dInput1 As Double
Dim dInput2 As Double
'Use Application.InputBox with Type 1 to force a numeric entry
dInput1 = Application.InputBox("Enter No in Group 1", Type:=1)
If dInput1 = 0 Then Exit Sub 'Pressed cancel
'Use Application.InputBox with Type 1 to force a numeric entry
dInput2 = Application.InputBox("Enter No in Group 2", Type:=1)
If dInput2 = 0 Then Exit Sub 'Pressed cancel
'A simple example showing the values and their sum
MsgBox dInput1 & " + " & dInput2 & " = " & dInput1 + dInput2
Here's a link for more information regarding Application.InputBox

Related

VBA how to create this If IsNumber formula as a function

So I am lookingto convert this formula =IF(A3,IF(ISNUMBER(C3/B3),C3/B3,""),"")
into a private function so I can use that instead. I have tried and failed to get it to work in VBA, if it is numeric and it is, how can I perform the division from the two cells?
Code:
Function GetSpendPerHead(Ra As Range, rb As Range, rc As Range)
GetSpendPerHead = ""
If Ra.Value = True Then
If IsNumeric(rb.Value) And IsNumeric(rc.Value) Then
If rb.Value <> 0 Then
GetSpendPerHead = rc / rb
End If
End If
End If
End Function
Your function seems to work fine for me, here are some test cases.
Sub UnitTestGetSpendPerHead()
Dim ra As Range: Set ra = Range("A1")
Dim rb As Range: Set rb = Range("B1")
Dim rc As Range: Set rc = Range("C1")
Dim test As Variant
rb.NumberFormat = "General"
' Test 1
ra.Value2 = ""
rb.Value2 = 1
rc.Value2 = 2
test = GetSpendPerHead(ra, rb, rc)
Debug.Print "Test 1 Result: " & test
' Test 2
ra.Value2 = 2014
rb.Value2 = 2
rc.Value2 = "a"
test = GetSpendPerHead(ra, rb, rc)
Debug.Print "Test 2 Result: " & test
' Test 3
ra.Value2 = 2015
rb.NumberFormat = "#"
rb.Value2 = "4"
rc.Value2 = 2
test = GetSpendPerHead(ra, rb, rc)
Debug.Print "Test 3 Result: " & test
End Sub
Function GetSpendPerHead(ra As Range, rb As Range, rc As Range)
GetSpendPerHead = ""
If Not IsEmpty(ra.Value) Then
If IsNumeric(rb.Value) And IsNumeric(rc.Value) Then
If rb.Value <> 0 Then
GetSpendPerHead = rc / rb
End If
End If
End If
End Function
Function CheckNumber()
'Define the variables
Dim FirstNumber As Double
Dim SecondNumber As Double
Dim Result As Double
'If we have an error when are reading the variables go to Oops
On Error GoTo Oops
'Read the first number from row 3 and column 3 (C)
FirstNumber = Sheets("Sheet1").Cells(3, 3).Value
'Read the first number from row 3 and column 2 (B)
SecondNumber = Sheets("Sheet1").Cells(3, 2).Value
'Calculate C3/B3
Result = FirstNumber / SecondNumber
'Get the result as message box
MsgBox Result
'If we have error we get this message: Not Number
Oops:
MsgBox "Not Number"
End Function
Thanks all, after debugging, problem was instead or Ra.Value = true , I then set Ra.Value = 1 and it worked

Padding Zero's while printing and changing values

I'm new here but hope you all can help with a solution I'm working towards. I'm working on an excel document and setting up a macro. It works until I try to add some logic to pad a number with zero's.
I'm trying to pad zero's in a select cell where the labels are less than 10, then add my integer. If the labels are greater than 9, I want to pad one less zero, likewise when they are greater than 99, one less from those with 10 or more.
My program asks the user how many labels they wish to print (1-999).
I've tried to add an IF statement within my For I = 1 To LabelCount:
For I = 1 To LabelCount
If I < 10 Then
ActiveSheet.Range("C20").Value = "C906BGM0880000" & I
ActiveSheet.PrintPreview
Else
ActiveSheet.Range("C20").Value = "C906BGM088000T" & I
ActiveSheet.PrintPreview
End If
Next
The above did not work.
Sub IncrementPrint()
'updateby Tyler Garretson
Dim LabelCount As Variant
Dim xScreen As Boolean
Dim I As Long
On Error Resume Next
LInput:
LabelCount = Application.InputBox("Please enter the number of copies you want to print:")
If TypeName(LabelCount) = "Boolean" Then Exit Sub
If (ActiveSheet.Range("F11").Value = "") Or (ActiveSheet.Range("F14").Value = "") Or (ActiveSheet.Range("C18").Value = "") Then
MsgBox "Error Occurred. Please enter values for Route, Stop, and Destination Name", vbExclamation
ElseIf (LabelCount = "") Or (Not IsNumeric(LabelCount)) Or (LabelCount < 1) Or (LabelCount > 999) Then
MsgBox "Error Occurred. Please enter 1 - 999", vbExclamation
ElseIf LabelCount < 10 Then
xScreen = Application.ScreenUpdating
Application.ScreenUpdating = False
For I = 1 To LabelCount
ActiveSheet.Range("C20").Value = "C906BGM0880000" & I
ActiveSheet.PrintPreview
Next
ActiveSheet.Range("C20").ClearContents
Application.ScreenUpdating = xScreen
End If
End Sub
User enters 11 labels that he or she wishes to print, the program prints out the following:
Label1: ABC00001
Label2: ABC00002
Label3: ABC00003
Label4: ABC00004
Label5: ABC00005
Label6: ABC00006
Label7: ABC00007
Label8: ABC00008
Label9: ABC00009
Label10: ABC00010
Label11: ABC00011
You want the Format command - Format(1, "00000") = 00001
Format(123,"00000") = 00123
' This might be the basis of what you need
for a = 1 to 1000
b = right("0000000000" & a,8) ' B will always be 8 long and paaded left with 0's
next a
This works well with a text prefix too
for a = 1 to 1000
c = "XYZ" & right("0000000000" & a,8)
next a

Sum every odd row in a single Column VBA

I have a code that searches an excel fill for the word distance, takes the value of the cell next to it, paste it into a new cell, then sums all the cells up. Which works great, but i now need to find a way to only sum ever even row number. Does that make sense?
Sub Distance_Check()
Dim DistanceCheck As String
Dim DistanceNumber As String
Dim DistanceSum As String
Dim DistanceTotal As String
DistanceCheck = MsgBox("Would you like to check the distance?", vbYesNo)
If DistanceCheck = vbYes Then
If IsArray(fname) Then Workbooks.OpenText fname(1)
i = 1
findStr = "Distance"
Set foundCel = Range("A:A").Find(what:=findStr)
If Not foundCel Is Nothing Then
firstAddress = foundCel.Address
Do
Range("J" & i).Value = foundCel.Offset(0, 1).Value
Set foundCel = Range("A:A").FindNext(foundCel)
i = i + 1
Loop While Not foundCel Is Nothing And foundCel.Address <> firstAddress
End If
Set wkbSourceBook = ActiveWorkbook
DistanceNumber = i - 2
DistanceSum = WorksheetFunction.Sum(Range(Cells(2, 15), (Cells(DistanceNumber + 1, 15))))
DistanceTotal = DistanceSum / DistanceNumber
If DistanceNumber = Cells(2, 12) Then
MsgBox ("No error found wihin distance")
Else
MsgBox ("Error found with distance")
End If
Else
End If
Call Save_Data
End Sub
Would the way youd go about this be using a for loop on the
cells(DistanceNumber(j,+1)
Where j = 0,
j = j +2 ,
Until j > DistanceNumber,
Would that work? If so how would you go about it?
Thanks
A quick way to step through a loop in the desired increments is to use the Mod operator which divides two numbers and returns any remainder (e.g. 7 mod 2 = 1, as two sixes fit into seven, leaving one).
You can use the row property of the range you identify with the Find method, and since you want to jump by two the modulo should be zero:
If foundcel.Row Mod 2 = 0 Then Range("J" & i).value = foundcel.Offset(0, 1).Value
That said, there is a 'built in' way to step through a loop if using a For loop like this
For x = 2 to 10 Step 2
' Do stuff
Next x
You can also use this method to step backwards, e.g.
For x = 100 to 0 Step -10
' Do stuff backwards!
Next x

excel vba formula determining highest value based on text

Hi currently i'm having a problem regarding the displaying of the most significant text among 4 rows in one column . What I have here is remarks of clients which is excellent,good,fair and bad ..and i would like to display the word excellent on a cell if it is present in that column , otherwise if good is the highest value present then it should display it ,if fair then fair or and lastly if bad then display bad
enter image description here
Hope this is not too late to answer your question. Try the following formula:
=INDEX({"Bad","Fair","Good","Excellent"},MATCH(1,(MATCH({"Bad","Fair","Good","Excellent"},B2:E2,0)),0))
See the image for reference:
It's not a formula, but the main trouble, as I see, is not to grade four known values you listed above, but to exclude empty and unknown values. Moreover, when such happened, user must be informed about it and make the right decision...
'''''''
Private Sub sb_Test_fp_Grade3()
Debug.Print fp_Grade3(Selection, 1, True)
End Sub
Public Function fp_Grade3(pRng As Range, _
Optional pUnkMod& = 0, _
Optional pEmpDen As Boolean = False) As String
' pUnkMod - Mode of UnKnown grades handling
' 0-Ignore; 1-Info only; 2-Deny
' pEmpDen - Deny or not empty values. If Deny, then empty treated as Unknown
' according pUnkMod setting
Const S_BAD As String = "BAD"
Const S_FAI As String = "FAIR"
Const S_GOO As String = "GOOD"
Const S_EXC As String = "EXCELLENT"
Const S_UNK As String = "UNK" ' UNKNOWN
Dim rCell As Range
Dim lVal&, lMax&, lUnk&
Dim sGrades$(0 To 4), sRet$, sVal$
sGrades(0) = S_UNK
sGrades(1) = S_BAD
sGrades(2) = S_FAI
sGrades(3) = S_GOO
sGrades(4) = S_EXC
lMax = 0
lUnk = 0
sRet = vbNullString
For Each rCell In pRng
sVal = rCell.Value
If (LenB(sVal) > 0 Or pEmpDen) Then
Select Case UCase(rCell.Value)
Case S_BAD: lVal = 1
Case S_FAI: lVal = 2
Case S_GOO: lVal = 3
Case S_EXC: lVal = 4
Case Else: lVal = 0
End Select
Select Case (lVal > 0)
Case True ' Known values
If (lVal > lMax) Then
lMax = lVal
If (lMax = 4) Then
If (pUnkMod = 0) Then Exit For
End If
End If
Case False ' UnKnown values
Select Case pUnkMod
Case 0 ' ignore them
' do nothing
Case 1 ' info about them
lUnk = lUnk + 1
Case Else ' 2 & any others - stop
lMax = 0
Exit For
End Select
End Select
End If
Next
If (lUnk > 0) Then sRet = " & " & lUnk & "x" & S_UNK
sRet = sGrades(lMax) & sRet
fp_Grade3 = sRet
End Function
'''

Excel VBA verify specific formatting in textbox

How can I verify a specific format in a textbox? I am scanning a label into a textbox that contains a date which is formatted like:
mm.dd.yyyy.hh.mm.ss
I have tried things that do not work...
If Not Format(TextBox1, "mm.dd.yyyy.hh.mm.ss") Then
MsgBox "Wrong Format"
End If
You can use this simple function to check if is a date
Function checkFormatDate(str As String) As Boolean
Dim Y
Dim M
Dim D
Dim H
Dim I
Dim S
Dim theDate As Date
M = Mid(str, 1, 2)
D = Mid(str, 4, 2)
Y = Mid(str, 7, 4)
H = Mid(str, 12, 2)
I = Mid(str, 15, 2)
S = Mid(str, 18, 2)
theDate = DateSerial(Y, M, D) + TimeSerial(H, I, S)
If IsDate(theDate) Then
checkFormatDate = True
Else
checkFormatDate = False
End If
End Function
This returns TRUE (if is a date) or FALSE (if not)
Just care about the dates like: 02.05.2016.01.10.05 (where mm.dd.yyyy.hh.mm.ss), because, you need to trust that 02 is the month and not the day, as 05 is the day and not the month, always appears somebody with a great idea, and change the values just because.
Edit #2
Heres a better version of the code:
Tester:
Sub testDate()
Dim Check As Boolean
Check = checkFormatDate2(UserForm1.TextBox1.Text)
'the textbox is inside UserForm1
If Check Then
MsgBox "Is a Date"
Else
MsgBox "Not a Date"
End If
End Sub
Function:
Function checkFormatDate2(str As String) As Boolean
Dim ArrayD
Dim i
Dim m
ArrayD = Split(str, ".")
If UBound(ArrayD) <> 5 Then
checkFormatDate2 = False
Exit Function
End If
For i = 0 To 5
Select Case i
Case 0
If CInt(ArrayD(i)) < 1 Or CInt(ArrayD(i)) > 12 Then
checkFormatDate2 = False
Exit Function
End If
Case 1
If CInt(ArrayD(i + 1)) < 1900 Or CInt(ArrayD(i + 1)) > 2050 Then
'set the botton and limit year as you need
checkFormatDate2 = False
Exit Function
End If
Case 2
m = Day(DateSerial(CInt(ArrayD(2)), CInt(ArrayD(0)) + 1, 1) - 1)
'm = the last (num) day of the month
If CInt(ArrayD(i - 1)) < 1 Or CInt(ArrayD(i - 1)) > m Then
checkFormatDate2 = False
Exit Function
End If
Case 3
If CInt(ArrayD(i)) < 1 Or CInt(ArrayD(i)) > 23 Then
checkFormatDate2 = False
Exit Function
End If
Case 4
If CInt(ArrayD(i)) < 1 Or CInt(ArrayD(i)) > 59 Then
checkFormatDate2 = False
Exit Function
End If
Case 5
If CInt(ArrayD(i)) < 1 Or CInt(ArrayD(i)) > 59 Then
checkFormatDate2 = False
Exit Function
End If
Case Else
End Select
Next i
checkFormatDate2 = True
End Function
Use this function to validate if the text inside the TextBox is a date sending the TextBox.Value or TextBox.Text. Thanks MikeD for your advice. This way is better.
Edit #3
As you tell me in the comments you could use the AfterUpdate on the Textbox, like this:
Private Sub TextBox1_AfterUpdate()
Dim a As Boolean
a = checkFormatDate2(Me.TextBox1.Value)
If a Then
MsgBox "is date"
Else
MsgBox "no date"
End If
End Sub
Where the UserForm1 has a TextBox named TextBox1 inside, and the function checkFormatDate2 is in a regular module. In my case I just send a MsgBox saying that is not a date.
Edit #4
The same way you can validate the text inside the TextBox when Exit the UserForm this way:
1) Add a button and set the Cancel Property to TRUE:
2)And inside the code you put this:
Private Sub CommandButton1_Click()
Dim a As Boolean
a = checkFormatDate2(Me.TextBox1.Value)
If a Then
MsgBox "is date"
Else
MsgBox "no date"
End If
End Sub
This way, when you press ESC in the keyboard or press the button CommandButton1 you try to close the UserForm and fire the code inside, and test the text inside the TextBox and you could do whatever you want, as, don't let the user go away, go back to the TextBox after a message saying what is the right format, well whatever you want.
Try :
If textbox1 = Format(TextBox1, "mm.dd.yyyy.hh.mm.ss") Then
TextBox1 does not contain a date format, so you can't apply a Format() function using date/time format codes.
One solution would be to split your string into array elements and examine them one by one, e.g.
Sub Test()
Dim A() As String, Rslt As Boolean
A = Split(Me.TextBox1.Value, ".") ' load dot seperated elements into array
If UBound(A) <> 5 Then
MsgBox "not 6 numbers seperated by (5) dots"
Rslt = False
ElseIf Val(A(0)) < 1 Or Val(A(0)) > 12 Then
MsgBox "1st part not a valid month (01-12)"
Rslt = False
ElseIf Val(A(1)) < 1 Or Val(A(1)) > 31 Then
MsgBox "2nd part not a valid day (01-31)"
Rslt = False
ElseIf Val(A(2)) < 0 Or Val(A(1)) > 99 Then
MsgBox "3rd part not a valid year (00-99)"
Rslt = False
ElseIf Val(A(3)) < 0 Or Val(A(3)) > 23 Then
MsgBox "4th part not a valid hour (00-23)"
Rslt = False
ElseIf Val(A(4)) < 0 Or Val(A(4)) > 59 Then
MsgBox "5th part not a valid minute (00-59)"
Rslt = False
ElseIf Val(A(5)) < 0 Or Val(A(5)) > 59 Then
MsgBox "6th part not a valid second (00-59)"
Rslt = False
End If
If Not Rslt Then
'beat the user
End If
End Sub
This doesn't consider months with 28/29/30/31 days, but that's only 1 or 2 if's further away.

Resources