Avoiding Nested IFs in VBA with "And _" [duplicate] - vba

This question already has answers here:
Does the VBA "And" operator evaluate the second argument when the first is false?
(7 answers)
Boolean Expression - Order of Operations
(1 answer)
VBA Short-Circuit `And` Alternatives [duplicate]
(3 answers)
Closed 2 years ago.
I am trying to avoid nesting IF statements but the code below gives me an error. The error only appears when I have the first line present. Without it, the other "And _" work perfectly. I cannot understand why!
For background, the objective of this code is to check that the date is in the correct format (dd/mm/yyyy) and occurs after 2020.
Any help would be really appreciated!
This code doesn't work:
Public ValidPaymentDate As Boolean
Public ArrInput_PaymentDate As Variant
ArrInput_PaymentDate = Split(PaymentDate.Value, "/")
If (UBound(ArrInput_PaymentDate) = 2) And _ ' ***Here is the issue!!!***
ArrInput_PaymentDate(0) > 0 And ArrInput_PaymentDate(0) <= 31 And _
ArrInput_PaymentDate(1) > 0 And ArrInput_PaymentDate(1) <= 12 And _
ArrInput_PaymentDate(2) >= 20 And ArrInput_PaymentDate(2) < 100 Then
ValidPaymentDate = True
End If
This code works (but I'd like to avoid nesting If statements):
Public ValidPaymentDate As Boolean
Public ArrInput_PaymentDate As Variant
ArrInput_PaymentDate = Split(PaymentDate.Value, "/")
If (UBound(ArrInput_PaymentDate) = 2) Then
If ArrInput_PaymentDate(0) > 0 And ArrInput_PaymentDate(0) <= 31 And _
ArrInput_PaymentDate(1) > 0 And ArrInput_PaymentDate(1) <= 12 And _
ArrInput_PaymentDate(2) >= 20 And ArrInput_PaymentDate(2) < 100 Then
ValidPaymentDate = True
End If
End If

You have an extra parenthesis that does not belong there
If (UBound(ArrInput_PaymentDate) = 2 And _ ' ***Here is the issue!!!***
ArrInput_PaymentDate(0) > 0 And ArrInput_PaymentDate(0) <= 31 And _
ArrInput_PaymentDate(1) > 0 And ArrInput_PaymentDate(1) <= 12 And _
ArrInput_PaymentDate(2) >= 20 And ArrInput_PaymentDate(2) < 100) Then
ValidPaymentDate = True
End If

Related

own function VBA else will not execute [duplicate]

This question already has answers here:
I use OR to form a multiple condition IF ELSE statement on VBA, it's not working
(2 answers)
How to use OR in if statement in VBA [duplicate]
(1 answer)
Closed 1 year ago.
Hi I have absolutly no clue why the else with "exit for" will not execute.
Value of my respective Excel testing cell as follows:
"VERS.GR.III, KENNNR.1905, V ERS.NR. 1023461"
Function DebitorVorhanden(Debitor As String) As Boolean
Dim Ausgabe As Boolean
Ausgabe = False
Dim TextLänge As Integer
TextLänge = Len(Debitor)
Dim Prüfer As Integer
For Prüfer = 1 To TextLänge
If Mid(Debitor, Prüfer, 1) = "1" Then
Dim PrüferLänge As Integer
PrüferLänge = 0
Dim Prüfer2 As Integer
For Prüfer2 = Prüfer To Prüfer + 6
If Int(Mid(Debitor, Prüfer2, 1)) = 1 Or 2 Or 3 Or 4 Or 5 Or 6 Or 7 Or 8 Or 9 Then
PrüferLänge = PrüferLänge + 1
Else
Exit For
End If
Next Prüfer2
If PrüferLänge = 7 Then
Ausgabe = True
GoTo DebitorGefunden
End If
End If
Next Prüfer
DebitorGefunden:
DebitorVorhanden = Ausgabe
End Function
If Int(Mid(Debitor, Prüfer2, 1)) = 1 Or 2 Or 3 Or 4 Or 5 Or 6 Or 7 Or 8 Or 9 Then
is always true and therefore the if case is running. The Or 2 statements return the value 2 and do not compare the two to the int(...) value.
A working solution would be:
dim tmp
tmp=Int(Mid(Debitor, Prüfer2, 1))
If tmp=1 or tmp=2 or tmp=3 Then
A shorter ways is:
If InStr("123456789", Mid(Debitor, Prüfer2, 1) ) Then
The Or operator "performs a logical disjunction on two expressions."
So expressions on either side of the operator are treated entirely separately.
Meaning that for it to work, it would have to be something like this:
If Int(Mid(Debitor, Prüfer2, 1)) = 1 Or Int(Mid(Debitor, Prüfer2, 1)) = 2 Or Int(Mid(Debitor, Prüfer2, 1)) = 3....
There are several ways to solve the problem; a common approach to handle numbers would be to use the Select Case statement, as you can do things like Case 1 To 9
But I like the InStr approach by Samuel

How to set items check in checklistbox?

I use SetItemChecked method to automatically check the checklistbox but it does not work. Anyone have a look to my code and please give a solution?
Private Sub Check(value As Double)
If 0 <= value < 20 Then
Me.CheckedListBox1.SetItemChecked(0, True)
ElseIf 20 <= value < 40 Then
Me.CheckedListBox1.SetItemChecked(1, True)
ElseIf 40 <= value < 60 Then
Me.CheckedListBox1.SetItemChecked(2, True)
ElseIf 60 <= value < 80 Then
Me.CheckedListBox1.SetItemChecked(3, True)
Else
Me.CheckedListBox1.SetItemChecked(4, True)
End If
End Sub
Then I call check(60) and it returns the 1st item check. It is wrong?
You need to correct your conditions as shown below also Clear
Private Sub Check(value As Double)
Me.CheckedListBox1.ClearSelected()
If 0 <= value And value < 20 Then
Me.CheckedListBox1.SetItemChecked(0, True)
ElseIf 20 <= value And value < 40 Then
Me.CheckedListBox1.SetItemChecked(1, True)
ElseIf 40 <= value And value < 60 Then
Me.CheckedListBox1.SetItemChecked(2, True)
ElseIf 60 <= value And value< 80 Then
Me.CheckedListBox1.SetItemChecked(3, True)
Else
Me.CheckedListBox1.SetItemChecked(4, True)
End If
End Sub
The first If-statement will always evaluate to True.
Because True is converted to 1 and False is converted to 0.
It's getting clearer if you look at this:
If (0 <= value) < 20 Then
So no matter which number is contained in value, the condition will be either...
True < 20
..or...
False < 20
For more information see here: Convert Boolean to Integer in VB.NET
So you need to change the conditions like following:
If 0 <= value And value < 20 Then ...

I have 8 rows but when (i = 7) it says (i + 1) is out of range?

I have a DataGridView with 8 Rows. In the following Sub i have an If statement to only do something when i is less than the RowCount, this is purposely so when i use (i + 1) on the last row it will still be in range, yet it is not? I can't figure out why. Would appreciate any help.
This is the sub
Public Sub Durations(dgv As DataGridView)
For i As Integer = 0 To dgv.RowCount
Dim intTotalMinutesOfRows As Integer
Dim IntHoursForRows As Integer
Dim intMinutesForRows As Integer
If i < dgv.RowCount Then
If dgv.Rows(i).Cells("EmployeeID").Value = dgv.Rows(i + 1).Cells("EmployeeID").Value _
And dgv.Rows(i).Cells("Date").Value = dgv.Rows(i + 1).Cells("Date").Value Then
intTotalMinutesOfRows = intTotalMinutesOfRows + dgv.Rows(i).Cells("TotalDurationOfRow").Value
Else
intTotalMinutesOfRows = intTotalMinutesOfRows + dgv.Rows(i).Cells("TotalDurationOfRow").Value
IntHoursForRows = Math.Floor(intTotalMinutesOfRows / 60)
intMinutesForRows = intTotalMinutesOfRows Mod 60
dgv.Rows(i).Cells("TotalDurationForDay").Value = "" & IntHoursForRows & " Hrs " & intMinutesForRows & " Mins"
intTotalMinutesOfRows = 0
End If
End If
Next
Iterate to RowCount - 1 only:
For i As Integer = 0 To dgv.RowCount - 1
^^^
Note that despite you've got If i < dgv.RowCount Then, later in this conditional operator you're trying to access Rows(i + 1), which causes the exception for i = dgv.RowCount - 1. So you have to change your condition to If i < dgv.RowCount - 1 Then as well.
Indexes are zero-based (they start at 0), so index 7 is the 8th row.
Row : 1 2 3 4 5 6 7 8
Index : 0 1 2 3 4 5 6 7
Infact even your loop's end is wrong, because i will go to whatever RowCount is. Thus if RowCount is 8 then i will be 8 as well in the end (which doesn't work, as seen in the indexes above).
In order to loop to the last index you must loop to RowCount - 1:
For i As Integer = 0 To dgv.RowCount - 1

A function returns a MsgBox 10 times?

Found a function on Excelguru which I changed a few things in and gonna edit some more. The idea is to use this to register worked hours and minutes.
There is one thing in this I don't understand: if I type the wrong time in the column reff I get a msg that its wrong, but it wont disappear unless I click it 10 times. I cant see what Im doing wrong. The entire code is posted and Im grateful for any help.
Use his function as part of the formula in the sheet like: TimeValue($E2;$F2;"16:00";"18:00";B2;9;C2)
Function TimeValue(FromTime As String, ToTime As String, StartTime As String, StopTime As String, Optional Weekday As String, Optional Daynr As Integer, Optional Holiday As String)
Dim x As Long
Dim F As Double
Dim T As Double
Dim Start As Double
Dim Stopp As Double
Dim Min As Long
Dim Day As Integer
Dim OverMid As Boolean
Select Case LCase(Weekday)
Case "mandag"
Day = 1
Case "tirsdag"
Day = 2
Case "onsdag"
Day = 3
Case "torsdag"
Day = 4
Case "fredag"
Day = 5
Case "lordag"
Day = 6
Case "sondag"
Day = 7
Case "x"
Day = 8
Case Else
Day = 0
End Select
OverMid = False
If LCase(Holiday) = "x" Then Day = 8
If Len(FromTime) = 0 Or Len(ToTime) = 0 Then
Exit Function
End If
If Len(FromTime) <> 5 Then
MsgBox ("Use format TT:MM - From time is wrong:" & FromTime)
Exit Function
End If
If Len(ToTime) <> 5 Then
MsgBox ("Use format TT:MM - To time is wrong:" & ToTime)
Exit Function
End If
F = Val(Left(FromTime, 2)) * 60 + Val(Right(FromTime, 2))
T = Val(Left(ToTime, 2)) * 60 + Val(Right(ToTime, 2))
Start = Val(Left(StartTime, 2)) * 60 + Val(Right(StartTime, 2))
Stopp = Val(Left(StopTime, 2)) * 60 + Val(Right(StopTime, 2))
If T = 0 Then T = 24 * 60
If T < F Then
T = T + 24 * 60
OverMid = True
End If
If Stopp = 0 Then Stopp = 24 * 60
For x = F + 1 To T
If x > Start And x <= Stopp Then
Min = Min + 1
End If
Next x
If OverMid = True Then
For x = 0 To Val(Left(ToTime, 2)) * 60 + Val(Right(ToTime, 2))
If x > Start And x <= Stopp Then
Min = Min + 1
End If
Next x
End If
'If weekday is set, equal to day
If Daynr <> 0 Then
If Daynr <> 9 Then
If Day <> Daynr Then Min = 0
End If
If Daynr = 9 And (Day > 5) Then
Min = 0
End If
End If
TimeValue = Min / 60
End Function
And the sub in the sheets
Private Sub Worksheet_Change(ByVal Target As Range)
Dim streng As String
Dim k As Long
Dim r As Long
k = Target.Column
r = Target.Row
If Cells(1, k) = "P" Then
If Cells(r, k) = "x" Then
Cells(r, 4) = "x"
Else
Cells(r, 4) = ""
End If
End If
End Sub
Message boxes really don't belong in UDFs (VBA functions meant to be used as spreadsheet functions).
Instead of the message box you could use code like:
If Len(FromTime) <> 5 Then
TimeValue = "Error! Use format TT:MM - From time is wrong:" & FromTime
Exit Function
Or perhaps:
If Len(FromTime) <> 5 Then
TimeValue = CVErr(xlErrValue)
Exit Function
This later will cause #VALUE! to display in the cell. Include enough documentation in your spreadsheet so that users can interpret such error values.

Output/Print variable, in code, in if statement

I am trying to achieve the following:
The user is shown an excel spread sheet with a list of assumption which they can change.
Title | Value |
Input01 | 10 | =
Input02 | 2 | >=
Input03 | 800 | >=
Input04 | 4 | >=
Input05 | 2 | <=
There is an If .. Then Statement that pulls in data if the assumption are met. However if an assumption is blanc, it should not be included in the If .. Then Statement.
If x = Input01Value And y >= Input02Value _
And z >= Input03Value And a >= Input04Value _
And b <= Input05Value Then
User ommits Input03
If x = Input01Value And y >= Input02Value _
And a >= Input04Value And b <= Input05Value Then
Now I could check to see if each value exist, and then follow it by another If statement with the appropriate variables. But this seems a bit redundant.
I was wondering if something like the following is possible:
Input 01 = ""
If Input01Value != "" Then Input01 = "x = " & Input01Value
'Then use join or something similar to join all of them ..
And Then use this Input01 directly in the If .. Then statement. This way when a variable is empty the And .. are not included and the If statement will not fail.
Eg. (I know this doesn't work, just illustrating the scenario)
VBA: If Input01 Then
Result while compiling: If x = Input01Value Then
Please Note, I know I could do something like the following:
If Boolean And Variable2 > 4 Then and then have Boolean and Variable2 populate with a value in the cell, however the issue with this is that if the user, for example, decides to omit the Variable2 (which is reasonable) it will fail. eg. If (Boolean = True) And > 4 Then.
Hope my question is clear, thanks for the help.
What about using a function which operates on a select case depending on a string operator and two values?
Function conditionalString(condition As String, x As Variant, y As Variant) As Boolean
Select Case condition
Case "="
If (x = y) Then
conditionalString = True
Else
conditionalString = False
End If
Exit Function
Case ">="
conditionalString = (x >= y)
Exit Function
Case "<="
conditionalString = (x <= y)
Exit Function
Case ">"
conditionalString = (x > y)
Exit Function
Case "<"
conditionalString = (x < y)
Exit Function
Case Else
conditionalString = False
End Select
End Function
You could then just have another function, say "check if value isn't blank" before calling all assumptions.
Expanding on my comment, you can use something like this to test each row of input.
Function TestIt(testValue,inputOperator,inputValue) As Boolean
If Len(inputValue)=0 Then
TestIt=True 'ignore this test: no value supplied
Else
TestIt=Application.Evaluate(testValue & inputOperator & inputValue)
End If
End function