VBA Else without if error - vba

I keep getting the " Else without if" error in VBA when I clearly do not have that issue. Does anyone know how to fix it? It takes me to the elseif statement that begins with elseif memb= "platinum"
Below is my code:
ElseIf memb = "Platinum" Then d = 0.01
ElseIf memb = "Black" Then d = 0.03
End If
If st >= min Then cb = st * d Else cb = 0
End If
If cb >= thresh Then MsgBox ("cb is greater than thresh")
End If
tac = st + cb
Range("B5").Value = st
Range("B7").Value = cb
Range("B9").Value = tac

I'm going to assume your first If statement goes something like this:
If memb = "Gold" Then d = 0.005
ElseIf memb = "Platinum" Then d = 0.01
ElseIf memb = "Black" Then d = 0.03
End If
If some processing is performed on the same line as the Then keyword, VBA treats it as a single, non-nested If statement. This means that anything after that will be treated as a new statement and not related to prior If statement.
What you can do is put the processing statement(s) on the next line after each If-Then and ElseIf-Then statements.
Example,
If memb = "Gold" Then
d = 0.005
ElseIf memb = "Platinum" Then
d = 0.01
ElseIf memb = "Black" Then
d = 0.03
End If
With this in mind, you may want to fix the succeeding If-Then-Else statements in your code. The End If part becomes meaningless if your If-Then-Else is in a single line.

Your code seems to have syntax error and error message tells you that.
Or you did not post all code?
Have a look on MS documentation: https://msdn.microsoft.com/de-de/library/752y8abs.aspx
Do you really stick to the syntax?
Without even having MS OFfice this should (be better readable and) work:
If memb = "Platinum" Then
d = 0.01
ElseIf memb = "Black" Then
d = 0.03
End If
If st >= min Then
cb = st * d
Else
cb = 0
End If
If cb >= thresh Then
MsgBox ("cb is greater than thresh")
End If
tac = st + cb
Range("B5").Value = st
Range("B7").Value = cb
Range("B9").Value = tac

Related

Is there a way to optimize nested if clauses?

for a while now I've tried to solve the decreased speed issue in my Access application when opening a print preview of certain reports. I've noticed that the slow reports have one thing in common - long, nested if clauses. I tried to search the internet for an answer for this issue, but some of the solutions do not apply to Access VBA or they just aren't possible to implement in the case of my application.
I was wondering if there are some commonly known ways that are used in order to avoid if clause monsters?
EDIT: A snip of code - it mostly handles the structure of the report based on certain conditions.
If (strCcDocNumber <> vbNullString) Then
Dim strUpperPart As String, strLowerPart As String
IDModule.placeIDStringsToPrivateVariables strCcDocNumber, ", "
strUpperPart = IDModule.returnUpper()
strLowerPart = IDModule.returnLower()
txtIDs = strUpperPart & vbCrLf & strLowerPart
Else
txtIDs = " " & vbCrLf & " "
End If
If (strOrderNumber = IO_OrderNumber.OrderNumberCode & "12345") Then
txtIDs = txtIDs
txtIDSpec1 = ModuleIDSpec1.getIDSpec1
txtIDSpec2 = ModuleIDSpec2.getIDSpec2
txtIDSpec1.Height = 330
txtIDSpec2.Height = 330
txtUpperLower = "- Ex" & vbCrLf & "- Ex2" & vbCrLf & vbCrLf & "- Ex3"
On Error Resume Next
For Each c In Me.Controls
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") Then c.Height = 0
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") Then c.Visible = False
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra") Then c.Height = 0
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra") Then c.Visible = False
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1Texts") Then c.Visible = True
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1Texts") Then c.Height = 330
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C") Then c.Visible = True
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C") Then c.Height = 330
'+ many more tags
Next
On Error GoTo 0
txtIDSpec1.Visible = True
txtIDSpec2.Visible = True
If (txtIDSpec1 = vbNullString And txtIDSpec2 = vbNullString) Then
txtIDSpec1.Height = 0
txtIDSpec2.Height = 0
txtIDSpec1.Visible = False
txtIDSpec2.Visible = False
End If
Else
'+a lot more similar conditions
EDIT: I remembered which if statements were the most troublesome ones. I think you can't change these ones into select cases or ElseIf statements, because all of the conditions need to be checked...
It goes like this:
If (condition) Then
Do this
If (differentCondition) Then
Do this also
If (completelyDifferentCondition) Then
Do this as well
Else
Do this instead
End If
End If
Else
If (yetAnotherCondition) Then
Do this
Else
Do this instead
End If
End If
I was wondering if there are some commonly known ways that are used in
order to avoid if clause monsters?
First step is to work out what you want to achieve, not how you want to do it. In this context, you want to set height and visibility. From here, you can work out what conditions are required to set this.
When you first do this, you will have some monster clauses - but this is OK because you have not clarified your thinking. Work from an assumption of one state unless proven otherwise. Here is an example:
c.visible = True
If ((c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") OR (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra")) then c.visible = True
Of course, the second line can now be simplified a little bit.
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table" Or c.Tag = "IDSpec1TableExtra") then c.visible = True
I also I set marker Booleans - for example:
IsSpecTable = (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table")
IsMySpecialFriend = (c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C")
[...]
c.Visible = IsSpecTable Or IsMySpecialFriend
These are a couple of techniques I use to simplify complex business logic. I am also looking at the use of flags, but this means converting the text Tag to an enumerated value (I am doing this in VB.Net). This technique, though, simplifies the expression down to a simple mask with a And or Or operator as appropriate.
Consider using Select Case Statements when you have multiple If Statement based off the same value.
MSDN - Select Case Statement
Executes one of several groups of statements, depending on the value of an expression.
For Each c In Me.Controls
Select Case c.Tag
Case "IDSpec2Table", "IDSpec1Table", "IDSpec1TableExtra"
c.Height = 0
c.Visible = False
Case "IDSpec2Texts", "IDSpec1Texts"
c.Visible = True
c.Height = 330
Case "IDSpec1TextsExtra"
If ModuleTarget.TargetGroup <> "23C" Then
c.Visible = True
c.Height = 330
End If
End Select
Next
Performance: Select Case vs If vs If ElseIf
I mentioned in a comment that using a Select Case is more for readability than performance. Which is correct if we are comparing Select Case and If ElseIf statements (read Which way is faster? If elseif or select case).
Select Case and If ElseIf can be considerably faster than multiple If statements. This is because the VBA evaluates every condition in an If statement and will stop evaluating when one condition is meet in the Select Case statement. Note: not all languages do.
Consider this simple test.
Sub Test()
Debug.Print "Test:If Statement Test:"
If ret(1) = 1 Or ret(2) = 2 Or ret(3) = 3 Or ret(4) = 4 Or ret(5) = 5 Then
End If
Debug.Print vbNewLine; "Test:If ElseIf Statement"
If ret(1) = 1 Or ret(2) = 2 Then
ElseIf ret(3) = 3 Then
ElseIf ret(4) = 4 Then
ElseIf ret(5) = 5 Then
End If
Debug.Print vbNewLine; "Test:Select Case Statement"
Select Case 1
Case ret(1), ret(2)
Case ret(3)
Case ret(4)
Case ret(5)
End Select
End Sub
Function ret(n As Long) As Long
ret = n
Debug.Print n,
End Function
Notice that the If statement had to perform 5 operations even though they were all true. The ElseIf had to perform 2 operations because the first 2 operations were grouped in a single If clause. The Select Case only performed a single operation, even though, two operations were grouped together. This is because the Select Case will always stop evaluating conditions when a single condition is true.

Non-functional IF Else VBA

Witless newbie attempting an If Else based on entries made on a user form:
If OptionButtonMed.Value = True Then
pp = pp + 2
ElseIf OptionButtonLarge = True Then
pp = pp + 4
ElseIf OptionButtonXL = True Then
pp = pp + 8
Else: pp
End If
It simply does not work. Suggestions valued.
You get a run-time error, because of your Else statement, you need to give pp a valid value.
If you try pp = pp +1 or whatever value, it will work.
Also, it's good practice to keep .Value after the Control you are trying to test the Critera. (the = True is actually not necessary)
Modify your code to:
If OptionButtonMed.Value Then
pp = pp + 2
ElseIf OptionButtonLarge.Value Then
pp = pp + 4
ElseIf OptionButtonXL.Value Then
pp = pp + 8
Else
pp = pp + 1 ' <-- just for example
End If

Comparing two cells using Select Case or If Then VBA

I am trying to get different results in cell "C8" depending on the relationship between the values of the cells "B3" and "C3"
I first tried with a basic Select Case :
Sub Salmonpool_depth1()
Dim score As Variant, result As String
With Sheets("Vertical")
score = Range("C3").Value
Select Case score
Case Is = ""
result = ""
Case Is >= 0.3 * Range("B3").Value
result = "0.3"
Case Is >= 0.6 * Range("B3").Value
result = "0.6"
Case Is >= Range("B3").Value
result = "1"
Case Else
result = "0"
End Select
Range("C8").Value = result
End With
End Sub
But it always gave me the result 0.3 as a result if any conditions were filled other than the last one, it did give me 0 when the last condition was filled.
I then tried by defining B3 and C3 as variables
Sub Salmonpool_depth2()
Dim pool As Variant, result As String, hydraulic As Variant
With Sheets("Vertical")
pool = Range("C3").Value
hydraulic = Range("B3").Value
Select Case pool
Case Is = ""
result = ""
Case Is >= 0.3 * hydraulic
result = "0.3"
Case Is >= 0.6 * hydraulic
result = "0.6"
Case Is >= hydraulic
result = "1"
Case Else
result = "0"
End Select
Range("C8").Value = result
End With
End Sub
But that also gave me 0.3 or 0 as a result as above
I then tried with If Then statements instead :
Sub Salmonpool_depth3()
Dim hydraulic As Variant, pool As Variant
hydraulic = Range("B3").Value
pool = Range("C3").Value
If pool >= hydraulic Then
Range("C8").Value = 1
End If
If pool >= 0.6 * hydraulic Then
Range("C8").Value = 0.6
End If
If pool >= 0.3 * hydraulic Then
Range("C8").Value = 0.3
End If
If pool < 0.3 * hydraulic Then
Range("C8").Value = 0
End If
If pool = "" Then
Range("C8").Value = ""
End If
End Sub
But that also gives me 0.3 or 0 as above.
Does anyone have any idea how to change this? It must be in the way that I ask the question as the program does not understand.
Lilou
the problem is that if its greater than .03 then it will always stop there. SELECT CASE Statements exit when one of the conditions evaluates to try. You would need to order it in such a fashion:
Case Is >= hydraulic
result = "1"
Case Is >= 0.6 * hydraulic
result = "0.6"
Case Is >= 0.3 * Range("B3").Value
result = "0.3"
Your problem in the Select Case statements is that once a condition is met, it quits comparing. So when your condition is...say 0.8, the following code says "Is it greater than or equal to 0.3 * Hydraulic? Yes" and then never compares the 0.6 * Hydraulic.
Case Is >= 0.3 * hydraulic
result = "0.3"
Case Is >= 0.6 * hydraulic
You need to put a limit on the first comparison like this:
Case (0.3 * hydraulic) To (0.6 * hydraulic)
result = "0.3"
Case (0.6 * hydraulic) To 1
Using the first bit of code you posted, I think your >= is backwards. It should be <=.
Sub Salmonpool_depth1()
Dim score As Variant, result As String
With Sheets("Vertical")
score = Range("C3").Value
Select Case score
Case Is = ""
result = ""
Case Is <= (0.3 * Range("B3").Value)
result = "0.3"
Case Is >= (0.6 * Range("B3").Value)
result = "0.6"
Case Is >= Range("B3").Value
result = "1"
Case Else
result = "0"
End Select
Range("C8").Value = result
End With
End Sub
I know that Tim, Doug and JC have all answered your question for cases, but as you said your if statements were also giving improper results, and I'm not sure anyone addressed that. As written, changing the order would fix the result, but the more elegant solution is using the Else If statement. That way, you get a situation similar to using Case, where it checks each statement until one is right, does the section of code in that section, and skips ahead to End If when its done. It would look something like this:
If pool >= hydraulic Then
Range("C8").Value = 1
Else If pool >= 0.6 * hydraulic Then
Range("C8").Value = 0.6
Else If pool >= 0.3 * hydraulic Then
Range("C8").Value = 0.3
Else If pool < 0.3 * hydraulic Then
Range("C8").Value = 0
Else If pool = "" Then
Range("C8").Value = ""
End If
This way, lets say pool was 0.7*hydraulic, it would throw False for the first If statement, then True for the next Else If line, set C8 to 0.6, and move to the End If line. Your way (putting End If after each If statement) checks each If statement sequentially, whether something passes the previous statement or not. You can also make this more robust by requiring multiple conditionals to pass each if:
If pool >= hydraulic Then
Range("C8").Value = 1
Else If pool >= 0.6 * hydraulic And pool < hydraulic Then
Range("C8").Value = 0.6
Else If pool >= 0.3 * hydraulic And pool < 0.6 * hydraulic Then
Range("C8").Value = 0.3
Else If pool < 0.3 * hydraulic Then
Range("C8").Value = 0
Else If pool = "" Then
Range("C8").Value = ""
End If
With this snipet, you can reorder the conditionals however you like, and it will only pass if it falls within that specific range. And, if you like, you can place an Else line by itself at the end of the section that will be run if none of the If or Else If statements pass:
If pool >= hydraulic Then
Range("C8").Value = 1
Else If pool >= 0.6 * hydraulic And pool < hydraulic Then
Range("C8").Value = 0.6
Else If pool >= 0.3 * hydraulic And pool < 0.6 * hydraulic Then
Range("C8").Value = 0.3
Else If pool < 0.3 * hydraulic Then
Range("C8").Value = 0
Else If pool = "" Then
Range("C8").Value = ""
Else
Range("C8").Value = "Invalid Entry"
End If

Upper Case in VB 6 text box

How to make first letter in upper case while pressing tab or space in vb 6.0 ?
My code is as follows
txtFirstName.Text = UCase$(txtFirstName.Text)
but it doesn't change after tab or space
It's just simple just do this in the text box keypress events...
Private sub textbox_keypress(KeyAscii As Integer)
KeyAscii = Asc(UCase(Chr(KeyAscii)))
End Sub
Use the LostFocus event
Private Sub yourTextBox_LostFocus()
With yourTextBox
'first letter in upper case, the rest, untouched.
.Text = UCase(Mid(.Text, 1, 1)) & Mid(.Text, 2, Len(.Text))
End With
End Sub
Apply the same logic to the KeyDown event and check if the pressed key is the space key.
Private Sub yourTextBox_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = 32 Then
With yourTextBox
'first letter in upper case, the rest, untouched.
.Text = UCase(Mid(.Text, 1, 1)) & Mid(.Text, 2, Len(.Text))
.SelStart = Len(.Text) 'put the cursor at the end of the textbox...
End With
End If
End Sub
StrConv Function
Returns a Variant (String) converted as specified.
Syntax
StrConv(string, conversion, LCID)
The StrConv function syntax has these named arguments:
Part Description
string Required. String expression to be converted.
conversion Required. Integer. The sum of values specifying the type of conversion to perform.
LCID Optional. The LocaleID, if different than the system LocaleID. (The system LocaleID is the default.)
Settings
The conversion argument settings are:
Constant Value Description
vbUpperCase 1 Converts the string to uppercase characters.
vbLowerCase 2 Converts the string to lowercase characters.
vbProperCase 3 Converts the first letter of every word in string to uppercase.
AND THERE IS MORE ...
TO GSERGE
$ means nothing when applied to a function name as opposed to a variable name. VBA uses $ AND B as a suffix to denote similar functionality.
VB6 IS VBA the person who said maybe in VB6 but not in VBA. VB6 program host VBA as their programming language. VB6 on it's own are some app objects and the forms package only - no programming language. It's best to think of VB6 as a VBA host like Office.
If you want to proper case see this WORDBASIC Ver 6 code, (which word 2003 helpfully converted to vba).
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Public Sub MAIN()
Select Case WordBasic.Int(GetModifer)
Case 0
WordBasic.ChangeCase
Case 1
WordBasic.ChangeCase 4
Case 2
WordBasic.ChangeCase 2
Case 3
ProperCase
Case Else
WordBasic.ChangeCase
End Select
End Sub
Private Sub ProperCase()
Dim F
Dim z
Dim a$
Dim P
F = 1
WordBasic.ChangeCase 2
WordBasic.EditBookmark Name:="SerenityChangeCase", SortBy:=0, Add:=1
z = WordBasic.GetSelEndPos()
WordBasic.CharLeft 1
While WordBasic.GetSelEndPos() < z And Not WordBasic.AtEndOfDocument()
WordBasic.SelectCurWord
a$ = WordBasic.[Selection$]()
P = 0
If LCase(a$) = "a" Then
P = 1
ElseIf LCase(a$) = "an" Then
P = 1
ElseIf LCase(a$) = "as" Then
P = 1
ElseIf LCase(a$) = "at" Then
P = 1
ElseIf LCase(a$) = "be" Then
P = 1
ElseIf LCase(a$) = "by" Then
P = 1
ElseIf LCase(a$) = "in" Then
P = 1
ElseIf LCase(a$) = "is" Then
P = 1
ElseIf LCase(a$) = "of" Then
P = 1
ElseIf LCase(a$) = "on" Then
P = 1
ElseIf LCase(a$) = "or" Then
P = 1
ElseIf LCase(a$) = "to" Then
P = 1
ElseIf LCase(a$) = "and" Then
P = 1
ElseIf LCase(a$) = "are" Then
P = 1
ElseIf LCase(a$) = "for" Then
P = 1
ElseIf LCase(a$) = "the" Then
P = 1
ElseIf LCase(a$) = "from" Then
P = 1
ElseIf LCase(a$) = "what" Then
P = 1
ElseIf LCase(a$) = "with" Then
P = 1
End If
If P = 1 And F = 0 Then WordBasic.Insert LCase(a$)
WordBasic.WordRight 1
F = 0
Wend
WordBasic.WW7_EditGoTo Destination:="SerenityChangeCase"
WordBasic.EditBookmark Name:="SerenityChangeCase", SortBy:=0, Delete:=1
End Sub
Private Function GetModifer()
Dim a
Dim B
Dim c
Dim X
a = GetAsyncKeyState(16)
B = GetAsyncKeyState(17)
c = GetAsyncKeyState(18)
X = 0
If a < 0 Then X = X + 1
If B < 0 Then X = X + 2
If c < 0 Then X = X + 4
GetModifer = X
End Function
OK. Yeah txtFirstName is a good indicator of usage here.. So I'd use (sort of) Title Caps And I'd do it on the Validate event.. So
Private Sub txtFirstName_Validate(Cancel As Boolean)
Dim p As Integer ' i doubt we'll use more than 32K for a name....
Dim mName As String
p = 1
' first off lets trim any leading blanks.. assume NOTHING and make sure its all lower case..
mName = LCase(LTrim(txtFirstName))
Do While p > 0 And p <= Len(txtFirstName) ' start with the first non-blank
Mid(mName, p, 1) = UCase(Mid(mName, p, 1))
p = InStr(p, mName, " ")
If p > 0 And p < Len(mName) Then p = p + 1
Loop
Cancel = False
txtFirstName = mName
End Sub
Works every time, and capitalizes each word.. Didn't add any code to to do TRUE title caps but this is close, and short & easy...

Converting a Number to Alphanumeric in VBA

I am using the below code as a portion of a much larger code to convert a number to its alphanumeric equal i.e 1=A, 2=B, etc. While this does work it is crazy long code and I am sure there is a better way to do this and was hoping you guys could assist.
Sub Convert()
Time = Range("A1")
If Time = 1 Then
E = "A"
Else
If Time = 2 Then
E = "B"
Else
If Time = 3 Then
E = "C"
Else
If Time = 4 Then
E = "D"
Else
If Time = 5 Then
E = "E"
Else
If Time = 6 Then
E = "F"
Else
If Time = 7 Then
E = "G"
Else
If Time = 8 Then
E = "H"
Else
If Time = 9 Then
E = "I"
Else
If Time = 10 Then
E = "J"
Else
If Time = 11 Then
E = "K"
Else
If Time = 12 Then
E = "L"
Else
If Time = 13 Then
E = "M"
Else
If Time = 14 Then
E = "N"
Else
If Time = 15 Then
E = "O"
Else
If Time = 16 Then
E = "P"
Else
If Time = 17 Then
E = "Q"
Else
If Time = 18 Then
E = "R"
Else
If Time = 19 Then
E = "S"
Else
If Time = 20 Then
E = "T"
Else
If Time = 21 Then
E = "U"
Else
If Time = 22 Then
E = "V"
Else
If Time = 23 Then
E = "W"
Else
If Time = 24 Then
E = "X"
Else
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
MsgBox E
End Sub
Easier and more solid way is to use what Excel already offers, which means "every letter is associated to a number in the ranges":
Public Function numberToLetter(ByVal myNumber As Integer) As String
numberToLetter = Split(Cells(1, myNumber).Address, "$")(1)
End Function
This is one way.
Sub numberToLetter()
Dim Time As Integer
Dim E As String
Time = Range("A1")
If Time > 26 Then
E = Chr(Int((Time - 1) / 26) + 64) & Chr(((Time - 1) Mod 26) + 65)
Else
E = Chr(Time + 64)
End If
End Sub
Notes
Chr returns a character based on the ASCII value