Im trying to use the environ function to only allow certain users to use a document
Here is my issue: it works with one username, not with several usernames....
I know nested loops could be a solution here but I think there is probably an easier way. Reference tables also didn’t work successfully
The current code looks like
If ((IDnumber=“12345”) or (IDnumber=“1234”) or IDnumber=“123”)) then
Msgbox “approved”
Else: msgbox “denied”
Select Case is the appropriate statement to use, here:
Dim strMsg as String
Select Case IDnumber
Case 12345, 1234, 123
strMsg = "Approved"
Case Else
strMsg = "Denied"
End Select
Msgbox strMsg
If your variable IDnumber is a string (in which case, bad choice of variable name!) then use quotes around each item in the list:
Dim strMsg as String
Select Case IDnumber
Case "alpha", "bravo", "charlie"
strMsg = "Approved"
Case Else
strMsg = "Denied"
End Select
Msgbox strMsg
One could argue if using Select Case (as Olly suggests) or If is the better solution.
However, the syntax for the If would be
If IDnumber = "12345" Or IDnumber = "1234" Or IDnumber = "123" Then
MsgBox "approved"
Else
MsgBox "denied"
End if
You have several mistakes in your code snippet:
unbalanced parentheses. In your case, you don't need any parentheses at all. In VBA, (unlike in many other languages), it is not needed to wrap the whole logical term in parentheses. Also, no need to wrap the single or-conditions into parentheses.
As Marcucciboy2 said in a comment, wrong quote characters, use "
The syntax else: MsgBox is valid but not a good style. The : separates two statements, it's better to write this in 2 lines. Anyhow, you need an ending End If statement
The allowed usernames could be passed as an Array, and then check whether the current username is in that array. The valueInArray() does exactly this:
Sub CheckUser()
Dim userNames As Variant
userNames = Array("User1", "User2", "User3")
If valueInArray(Environ("UserName"), userNames) Then
Debug.Print "User Present"
Else
Debug.Print "User Not Present"
End If
End Sub
Public Function valueInArray(myValue As Variant, myArray As Variant) As Boolean
Dim cnt As Long
For cnt = LBound(myArray) To UBound(myArray)
If LCase(CStr(myValue)) = CStr(myArray(cnt)) Then
valueInArray = True
Exit Function
End If
Next cnt
End Function
Related
I want to find if a string contains a ","(comma) in it. Do we have any other option other than reading char-by-char?
Use the Instr function (old version of MSDN doc found here)
Dim pos As Integer
pos = InStr("find the comma, in the string", ",")
will return 15 in pos
If not found it will return 0
If you need to find the comma with an excel formula you can use the =FIND(",";A1) function.
Notice that if you want to use Instr to find the position of a string case-insensitive use the third parameter of Instr and give it the const vbTextCompare (or just 1 for die-hards).
Dim posOf_A As Integer
posOf_A = InStr(1, "find the comma, in the string", "A", vbTextCompare)
will give you a value of 14.
Note that you have to specify the start position in this case as stated in the specification I linked: The start argument is required if compare is specified.
You can also use the special word like:
Public Sub Search()
If "My Big String with, in the middle" Like "*,*" Then
Debug.Print ("Found ','")
End If
End Sub
There is also the InStrRev function which does the same type of thing, but starts searching from the end of the text to the beginning.
Per #rene's answer...
Dim pos As Integer
pos = InStrRev("find the comma, in the string", ",")
...would still return 15 to pos, but if the string has more than one of the search string, like the word "the", then:
Dim pos As Integer
pos = InStrRev("find the comma, in the string", "the")
...would return 20 to pos, instead of 6.
Building on Rene's answer, you could also write a function that returned either TRUE if the substring was present, or FALSE if it wasn't:
Public Function Contains(strBaseString As String, strSearchTerm As String) As Boolean
'Purpose: Returns TRUE if one string exists within another
On Error GoTo ErrorMessage
Contains = InStr(strBaseString, strSearchTerm)
Exit Function
ErrorMessage:
MsgBox "The database has generated an error. Please contact the database administrator, quoting the following error message: '" & Err.Description & "'", vbCritical, "Database Error"
End
End Function
You wouldn't really want to do this given the existing Instr/InstrRev functions but there are times when it is handy to use EVALUATE to return the result of Excel worksheet functions within VBA
Option Explicit
Public Sub test()
Debug.Print ContainsSubString("bc", "abc,d")
End Sub
Public Function ContainsSubString(ByVal substring As String, ByVal testString As String) As Boolean
'substring = string to test for; testString = string to search
ContainsSubString = Evaluate("=ISNUMBER(FIND(" & Chr$(34) & substring & Chr$(34) & ", " & Chr$(34) & testString & Chr$(34) & "))")
End Function
I am trying to develop a tool that will help standardize a description catalog of products. I want to have an input box prompt a user to enter a size. I want to encourage size entries like "5-1/2" and prevent users from entering "5.5". Ideally, if the size was entered with a decimal and not a dash with a fraction, I want a message box to pop up saying they can not do that. It would then need to re-show the input box.
Here is what I have -
Private Sub CS_Other_Click()
Unload Me
Sheets("Fill In").Activate
Worksheets("Fill In").Range("C2").NumberFormat = "#"
Dim other_casing_size As Variant
other_casing_size = InputBox("Fill in the casing size. Syntax MUST be in the form of X-X/X", "New Casing Size")
Range("C2") = other_casing_size
I just dont know the code to prevent an entry with decimals. Even better, if i knew how to code an exact syntax to include or exclude anything I wanted that would be perfect.
Thanks
A while loop, which checks the input string for a dot or comma would work quite ok, I guess:
Sub TestMe()
Dim inputString As String
Dim inputNumeric As Boolean
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Do While InStr(1, inputString, ".") Or _
InStr(1, inputString, ",") Or _
Not inputNumeric
If Not CBool(inputNumeric) Then
MsgBox "You tried to cancel or entered empty value!"
Exit Do
End If
MsgBox "Please, do not write dot or comma!"
inputString = InputBox("Please, enter a number!")
inputNumeric = isNumeric(Evaluate(inputString))
Loop
End Sub
The isNumeric() checks the input for being able to be converted to numeric. Thus 5-1/2 should be ok.
Concerning cancellation or entering empty value from the InputBox() - it really depends on the business logic of the "app", but in the case above - there is a msgbox and it exits the loop.
Write a separate function responsible for that prompt, and use it e.g. like this:
Dim casingSize As String
If GetCasingSize(casingSize) Then
ActiveSheet.Range("C2").Value = casingSize
End If
The function needs to return a Boolean for this to work - it returns True if the input is valid, False if there's no valid input to work with (e.g. prompt was cancelled). What makes this work, is passing the result as a ByRef argument, like this:
Public Function GetCasingSize(ByRef outResult As String) As Boolean
Do
Dim raw As Variant
raw = InputBox("Casing size?")
If VarType(raw) = vbBoolean Then
'handle cancelled prompt:
Exit Do
End If
If ValidateFractional(raw) Then
'handle valid input:
outResult = CStr(raw)
GetCasingSize = True
Exit Do
End If
'handle invalid input:
If MsgBox("The value '" & raw & "' is not valid. Try again?", vbYesNo) = vbNo Then
Exit Do
End If
Loop
End Function
Note the ValidateFractional function is its own concern - a separate, Private function would work, but I'd recommend making it Public, and unit-testing it to make sure it works as intended given a wide variety of edge-case inputs - and having it in a separate function means the logic in GetCasingSize doesn't need to change if the validation needs to be fine-tuned; for example this naive implementation uses the Like operator and would work for 5-1/4, but not for e.g. 15-5/8:
Public Function ValidateFractional(ByVal value As String) As Boolean
ValidateFractional = value Like "#[-]#/#"
End Function
Using Regular Expressions for this would probably be a good idea.
I am looking to see if a string has a comma or not.
Lets say I have two usernames "David , Boon" and "David Blind".
I need to write an if loop based on the conditions whether ',' exists in the User name or not. Is there a way to check that ? Something like Contains in .Net
Kindly share your thoughts.
You can use the InStr function to check the presence/location of one string within another:
Dim myVar As String
myVar = "foo,bar"
If InStr(1, myVar, ",") > 0 Then
'There was a comma
End If
Here's two,
if cbool(instr(1, mystring, chr(44))) then
...
if cbool(ubound(split(mystring, chr(44)))) then
...
You may try the following approach in your code...
If InStr(Range("A1").Value, ",") Then
MsgBox "Comma Exists", vbInformation
Else
MsgBox "No comma exists", vbExclamation
End If
Or just have a Function like below...
Function CommaExists(ByVal Rng As Range) As Boolean
If InStr(Rng.Value, ",") Then
CommaExists = True
End If
End Function
And call the function in your sub routine like this...
Sub Test()
If CommaExists(Range("A1")) Then
MsgBox "Comma found in the string.", vbInformation
Else
MsgBox "No Comma found in the string.", vbExclamation
End If
End Sub
I think that most readable option (subjective opinion, but worth to mention) here is a Like operator:
Dim myVar As String
myVar = "foo,bar"
If myVar Like "*,*" Then
'There was a comma
End If
What is wrong with this line of code? Throws up Run-time error '13', Type mis-match.
Dim mail As Outlook.MailItem
If mail.Subject Like "VEH" & "*" Or "MAT" & "*" Then
Trying to say if the subject of the email starts with "VEH" or "MAT", then do something.
You can simply use VBA LEFT function here:
If UCASE(LEFT(TRIM(mail.Subject), 3)) = "VEH" OR UCASE(LEFT(TRIM(mail.Subject), 3)) = "MAT" Then
'' DO something here
End If
If the emails are from different users then I wouldn't recommend using Like in such a scenario. If it is machine generated with fixed Subject then it makes sense.
Reason: Like/Left is case sensitive
Example:
Sub Sample()
Dim s As String
s = "vehicle"
If s Like "VEH*" Then
MsgBox "Found a match"
Else
MsgBox "didn't find a match"
End If
End Sub
or
Sub Sample()
Dim s As String
s = "vehicle"
If Left(s, 3) = "VEH" Then
MsgBox "Found a match"
Else
MsgBox "didn't find a match"
End If
End Sub
Alternative
For a search which is not case sensitive for example Vehicle, vehicle, VeHiCle, modify what #PareshJ posted.
Sub Sample()
Dim subj As String
subj = " Vehicle Number XYZ"
If UCase(Left(Trim(subj), 3)) = "VEH" Then
MsgBox "Found a match"
Else
MsgBox "didn't find a match"
End If
End Sub
Trim trims the leading and trailing spaces and Ucase converts a string into uppercase.
Edit
If you still want to use Like then you may want to use this
Option Compare Text '<~~ For case insensitive
Sub Sample()
Dim subj As String
subj = " Vehicle Number XYZ"
If Trim(subj) Like "VEH*" Then
MsgBox "Found a match"
Else
MsgBox "didn't find a match"
End If
End Sub
Caution: Option Compare Text will also affect any other comparisons in the code.
The correct syntax for your If statement is:
If (mail.Subject Like "VEH*") Or (mail.Subject Like "MAT*") Then
The brackets are optional (Like operator has higher precedence than Or), but it's easier to read.
I have a list of names that I need to filter a pivot table on. I use a Case Select to make the pivot items that are equal to the names visible and all other not visible. Can I make a one time array or list of these 15 names and call the list/array name within the code instead of using the 15 names in multiple locations? I have to fun a pivot table and sort on these names many times.
This is what I am trying to avoid. It works as is, but I'm trying to save myself headaches in the future with modifications.
Set table = Worksheets("Sheet2").PivotTables("PivotTable2")
With table.PivotFields("Assigned to")
For Each PvI In .PivotItems
Select Case PvI.Name
Case "Antone", "Brad", "Cavan", "Chris", "Daneisha", "Edward", "James", "Jonathan", "Joesph", "Karen", "Shaun", "Steve", "Timothy", "Tracey"
PvI.Visible = True
Case Else
PvI.Visible = False
End Select
Next
End With
Try this REVERSE Select Case
Sub Sample()
Dim sNames As String
sNames = "/Antone/Brad/Cavan/Chris/Daneisha/Edward/James/Jonathan/Joesph/Karen/Shaun/Steve/Timothy/Tracey/"
Set Table = Worksheets("Sheet2").PivotTables("PivotTable2")
With Table.PivotFields("Assigned to")
For Each PvI In .PivotItems
Select Case 0 '<~~ Reverse Select case
Case InStr(1, sNames, "/" & PvI.Name & "/", vbTextCompare)
PvI.Visible = False '<~~ This also reverses
Case Else
PvI.Visible = True
End Select
Next
End With
End Sub
Here is a simple way to see how Reverse Select Case works :)
Sub Sample()
Dim sNames As String, NameToCheck As String
sNames = "/Antone/Brad/Cavan/Chris/Daneisha/Edward/James/Jonathan/Joesph/Karen/Shaun/Steve/Timothy/Tracey/"
NameToCheck = "Antoneeeee"
'NameToCheck = "Daneisha"
Select Case 0
Case InStr(1, sNames, "/" & NameToCheck & "/", vbTextCompare)
MsgBox "Not found"
Case Else
MsgBox " Found"
End Select
End Sub
Sure and it eliminates the Select Case. I tested the code below minus the pivot table aspect. This stores your names in an array. You can keep the array as a global variable or as a range in the workbook (for the latter change the nameArr = ... line). The code (UBound(Filter(nameArr, PvI.Name)) > -1) will check if PvI.Name exists in nameArr by filtering the array for your desired name and checking if the resulting array has anything in it. Then the true/false value returned by the "array contains check" is assigned to PvI.Visible.
Dim nameArr As Variant
nameArr = Array("Antone", "Brad", "Cavan", "Chris", "Daneisha", "Edward", "James", "Jonathan", "Joesph", "Karen", "Shaun", "Steve", "Timothy", "Tracey")
Set table = Worksheets("Sheet2").PivotTables("PivotTable2")
With table.PivotFields("Assigned to")
For Each PvI In .PivotItems
PvI.Visible = (UBound(Filter(nameArr, PvI.Name)) > -1)
Next
End With
Elaborating on my comment, I recommend a table-driven approach.
The first thing to do is put the names of interest in a worksheet, and give that range a name. I called the range "MyNames" in this example.
The second thing to do is add this function to your project:
Function NameIsInList(LookFor As String) As Boolean
On Error GoTo Err_NameIsInList
NameIsInList = WorksheetFunction.Match(LookFor, Range("MyNames"), 0) > 0
On Error GoTo 0
Exit Function
Err_NameIsInList:
' the name was not found, or a bad parameter was supplied
NameIsInList = False
On Error GoTo 0
End Function
Finally, modify your procedure to use the function:
Set table = Worksheets("Sheet2").PivotTables("PivotTable2")
With table.PivotFields("Assigned to")
For Each PvI In .PivotItems
PvI.Visible = NameIsInList(PvI.Name)
Next
End With
The advantages I was shooting for here are:
Table-driven: The list of values can be stored in a worksheet where it's easily accessible and modified. (If you don't want users to see the LOV you can always hide the worksheet.)
Reusable: Let the function do the work of checking the name list. You suggest this is something that needs to be done many times; the function can be called from multiple places.