I am creating a userform with approximately 75 text boxes. I would like to create a common function to test the validity of the data for all text boxes by passing the allowable characters to the function along with the string I wish to validate.
For some boxes, I want to check only for numerics (like a phone number with (), -, and spaces stripped prior to calling the function).
For boxes such as last name, I want to test for upper and lower case alpha as well as allowing embedded spaces and a hyphen.
For other boxes that allow other free form text, I want to restrict input to selected character such as letters, numbers, hyphens, left and right parens "()", etc.
I cannot get the syntax right so that the "Is...Like" works correctly. I get "Run time error 5"..."Invalid procedure call or argument".
Here is my function:
Function IsValidString(strValue As String, strAllowed As String) As Boolean
Dim intPos As Integer
Dim strTemp As String
strTemp = """" & strAllowed & """"
MsgBox "StrValue = " & strValue & vbCrLf & vbCrLf & "StrAllowed = " & strAllowed & vbCrLf & vbCrLf & "strTemp = " & strTemp
For intPos = 1 To Len(strValue)
If Not (Mid(strValue, i, 1) Like strAllowed) Then IsValidString = False
Next intPos
IsValidString = True
End Function
To call the function, I use:
If Not IsValidString(strTemp, "[A-Za-z0-9]/#-") Then MsgBox "You Lose"
The error occurs within the Function code. It does not matter if I test for strAllowed or strTemp; I get the same error.
Thanks for looking at it.
Based upon the answer from #Comintern, I overlooked that I was checking the value of "i" instead of "intPos". Stupid mistake from a guy who is tired. Thanks for all the comments; it helped me solve the issue.
Related
How should I define large strings in VBA? Is there a better way than coding something like the below?
Dim largeString as String
largeString = "This is a long block of text that I want to fill " & _
"into a form field. I need to make sure I pay attention " & _
"to spacing and carriage return issues while doing so. " & _
"I also have to use quotes liberally, the concatenation " & _
"operator, and the continuance underscore to make sure " & _
"VBA can parse my code." & vbCr & vbCr & _
"It's kind of a pain in the ass and I wish I could use " & _
"a heredoc instead, letting me copy and paste the block" & _
"of text I need from another source and shove it into " & _
"a string."
Edit: Ugh, and there's a 25 line continuation limit too? So much for nice indenting and 80 characters of width, that only gives me enough room for a couple decent paragraphs.
I prefer doing it in this way:
Dim lStr As String
lStr = ""
lStr = lStr & "This is a long block of text that I want to fill "
lStr = lStr & "into a form field. I need to make sure I pay attention "
lStr = lStr & "to spacing and carriage return issues while doing so. "
lStr = lStr & "I also have to use quotes liberally, the concatenation "
lStr = lStr & "operator, and the continuance underscore to make sure "
lStr = lStr & "VBA can parse my code." & vbCr & vbCr
lStr = lStr & "It's kind of a pain in the ass and I wish I could use "
lStr = lStr & "a heredoc instead, letting me copy and paste the block"
lStr = lStr & "of text I need from another source and shove it into "
lStr = lStr & "a string."
I think this method is easier to work with than the line continuation method and there is no line number limit to get in with this way. You can comment out individual lines, which is useful for debugging SQL strings.
When handling long strings, I find it easier to use short variable names because VBA does not have the equivalent of += operator. largeString = largeString & "" takes up too much space and gets repetitive, so shortening the string name makes the format somewhat bearable.
For very large blocks of text, write it in a text editor then copy and paste it into your procedure. Then copy
lStr = lStr & "
and paste it at the beginning of each line. The VBA editor will automatically add the quotes at the end of the line making the process simple to do.
No, this is as good as it gets.
For really long strings it might be an option to keep the string in a separate file, or use some application feature. For example, in Word, you might want to store the string in a document variable, as hidden text or AutoText. In Excel, you might consider a hidden sheet for storing long string constants.
Another way is to store the text in comments, then parse it in a function. No external files required, good readability.
' TEXT to retrieve:
' SELECT
' field1, field2
' FROM table1
Function SQL_in_comments()
SQL_in_comments = Replace(Replace(Application.VBE.ActiveCodePane.CodeModule.Lines(2, 3), "' ", ""), "'", "")
End Function
In Excel, there's a much easier way than other answers here. Let Excel do the heavy lifting for you. This method will not require to break the string into smaller chunks -- it can handle up to 32,767 characters at once.
Put the string into a cell. Set aside a scrap worksheet for this purpose. Name the cell, eg MyLongString.
[MyLongString] = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)."
Now you can refer to the name in VBA as [MyLongString]. Eg:
MsgBox [MyLongString]
I'm using Threaded Comments on my Excel workbooks. Most of them have multiple replies.
I want to convert the comments and replies to text.
I get the first comment using the function CommentThreaded.text but not the replies if any.
How to do it using VBA?
Here are some commands to work with threaded comments in VBA:
' To know if a cell has a threaded comment:
If Not Range("*c,r*").CommentThreaded Is Nothing Then
' To know if the threaded coment has replies:
Range("*c,r*").CommentThreaded. Replies.Count
' To add a threaded comment:
Range("*c,r*").AddCommentThreaded ("Add CommentThreaded")
' To response a threaded comment:
Range("*c,r*").CommentThreaded.AddReply ("*your reply here*")
' To resolve a threaded comment:
Range("*c,r*").CommentThreaded.Resolved = True
' To re-open a resolved threaded comment:
Range("*c,r*").CommentThreaded.Resolved = False
' To delete a threaded comment:
Range("*c,r*").CommentThreaded.Delete
' To get the author:
Range("*c,r*").CommentThreaded.Author.Name
' To get the date:
Range("*c,r*").CommentThreaded.Date
' To get the text on the cell where the threaded comment is:
Range("*c,r*").CommentThreaded.Parent
' To get the address where the threaded comment is:
Range("*c,r*").CommentThreaded.Parent.Address
' To get the text of the original comment:
Range("*c,r*").CommentThreaded.Text
' To get the text of the replies:
Range("*c,r*").CommentThreaded.Replies(*n*).text
' To get the replies author:
Range("*c,r*").CommentThreaded.Replies(*n*).Author.Name
' To get the date of the reply:
Range("*c,r*").CommentThreaded.Replies(*n*).Date
There is not too much information out there about threaded comments, so I hope this is useful.
I used Bernardo's list of commands to create my function (which also checks for Notes), and I'm sharing it in case anyone is looking for an already-complete solution:
Function UdfComment(rng As Range) As String
'This UDF returns a cell's Note or all its comments (prefixed by Date and Author Name).
'This UDF assumes the range is a single cell.
Application.Volatile
Dim str As String
'First, check for presence of Note (the thing that shows up when you press Shift+F2):
If Not rng.Comment Is Nothing Then
str = Trim(rng.Comment.Text)
'If the note has a standard name header on a separate line - if you want to remove it, uncomment this line:
'(to be safe, can delete the "- 1" at the end to prevent truncating if name header is NOT on a separate line)
'str = Right(str, Len(str) - InStr(1, str, ":") - 1)
'Notes and Comments seem to be mutually exclusive, so checking for comments in an "Else if":
ElseIf Not rng.CommentThreaded Is Nothing Then
'First, return the original comment (prefixed by date and author):
str = rng.CommentThreaded.Date & ", " & rng.CommentThreaded.Author.Name & ":" & vbNewLine & Trim(rng.CommentThreaded.Text)
'Now check if original comment has replies (if so, iterate through them and append them all to 'str'):
If rng.CommentThreaded.Replies.Count > 0 Then
For Each r In rng.CommentThreaded.Replies
str = str & vbNewLine & r.Date & ", " & r.Author.Name & ":" & vbNewLine & Trim(r.Text)
Next
End If
'Without notes and comments, simply return an empty string:
Else
str = ""
End If
UdfComment = str
End Function
I've got a text box in a user form where the user puts unit (for example "kg"). I am trying to use this unit to format number of a cell (to add the unit after the value in a cell)
Sub Custom_unit()
Dim Cust_un As String
Cust_un = IM_Form1.TextBox1.Value
Selection.NumberFormat = "0" & Cust_un
End Sub
This is my attempt but when I try to run it, I get a Run-time error '1004': Unable to set the NumberFormat property of the Range class.
How to add quotes to NumberFormat
Given an input from a textbox, you get a string value, e.g. "kg".
In order to use that as a NumberFormat suffix
you need to enclose the unit string with quotes.
You could do that by assigning
Selection.NumberFormat = "0" & """" & Cust_un & """"
or alternatively
Selection.NumberFormat = "0" & Chr(34) & Cust_un & Chr(34)
Note that VBA can neither interprete a single quote (") as string,
nor a single quote enclosed by a start and end quote (""").
Yyou have to include a pair of quotes within the surrounding quotes instead, i.e. """" to make it understood.
The alternative uses the Chr() function
returning a string containing the character associated with the specified character code 34, i.e. the double quote character.
You might prefer to use a help function to return the correct NumberFormat, such as
Function getNumberFormat(ByVal unit As String, Optional NumFmt As String = "0 ") As String
unit = Replace(unit, Chr(34), vbNullString) ' remove existing double quotes
getNumberFormat = NumFmt & Chr(34) & unit & Chr(34) ' build NumberFormat including quotes around unit
' or: getNumberFormat = NumFmt & """" & unit & """"
End Function
Example call
Assuming your Custom_unit() procedure resides in the UserForm code module use Me.TextBox1.Text to refer to the current UserForm instance instead of referring to the default instance of the userform. An example call could be as follows:
Sub Custom_unit()
Selection.NumberFormat = getNumberFormat(Me.TextBox1.Text)
End Sub
BTW a hint: in most cases it's better to avoid selection references, c.f. How to avoid using Select in VBA?
I've written some VBA code that cycles through the rows of an open Excel datasheet doing an Evaluate(Index(Match)) statement, that I found online, that searches for 2 values (LastName & LabCat) in a second Excel Workbook. The result (FirstName) is returned from the second Excel Workbook and stored in a local variable. I successfully return the value I'm looking for with this statement:
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000,MATCH(""Cain""&""ABO1"",'[MyMatrix.xlsm]MySheet'!$B$2:$B$1000&'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
But what I haven't been able to solve is how to substitute local variables for the hard coded values, such as this statement (where I substitute the string "cain" with the variable strLastName and I substitute the string "ABO1" with the variable strLabCat):
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000,MATCH(""strLastName""&""strLabCat"",'[MyMatrix.xlsm]MySheet'!$B$2:$B$1000&'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
This statement returns "Error 2042" into strFirstName.
Sometimes it's easier to use a token replacement approach than trying to do a bunch of concatenation.
E.g.:
Dim sht As Worksheet, f, strFirstName
Set sht = Workbooks("MyMatrix.xlsm").Worksheets("MySheet")
f = "INDEX($C$2:$C$1000,MATCH(""{LastName}""&""{LabCat}"",$B$2:$B$1000&$H$2:$H$1000,0))"
f = Replace(f, "{LastName}", "Cain")
f = Replace(f, "{LabCat}", "AB01")
strFirstName = sht.Evaluate(f) '<< note using the Worksheet.Evaluate form here
You almost had it but it's easy to get mixed up with all the ""double-double"" quotes you need in a string in order to assign "double quotes" to the value of the string.
With your specific example, if this is successful:
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000,MATCH(""Cain""&""ABO1"",'[MyMatrix.xlsm]MySheet'!$B$2:$B$1000&'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
...but instead of hardcoding Cain and AB01, you want to get the values from string variables as stated in your question, change the line to:
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000,MATCH(""" & strLastName & """&""" & strLabCat & """,'[MyMatrix.xlsm]MySheet'!$B$2:$B$1000&'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
Backing up a bit, consider the following simple example of a string being assigned to a variable:
Option Explicit
Sub varTest1()
Dim myGreeting As String
myGreeting = "Hello, My name is Juan and I live in Mexico"
MsgBox myGreeting
End Sub
Running this subroutine, causes a message to pop-up with a greeting from Juan. Very basic, but note:
The variable is explicitly declared. It would work without the As String part but it's good coding practice to always specify a data type.
I used the optional statement Option Explicit. If used, it must be the first line in the module, and only appear once per module. What is does is force you to declare all variables and handle objects properly, by generating "pickier" errors during compilation or prior to code execution. Why would you want more errors? Better now than [unexpectedly] later, and is highly recommended to newer-coders and/or when troubleshooting.
Next, let's replace Juan's hardcoded name with another variable.
Still simple, like this:
Sub varTest2()
Dim myGreeting As String, myName As String
myName = "Juan"
myGreeting = "Hello, My name is " & myName & " and I live in Mexico"
MsgBox myGreeting
End Sub
Basically you're just concatenating a string. A plus symbol + works for adding strings or numbers together, but again, best practice is to stick with the one specifically meant for strings, which is the ampersand &.
Let's add another variable again:
Sub varTest3()
Dim myGreeting As String, myName As String, myCountry As String
myName = "Juan"
myCountry = "Mexico"
myGreeting = "Hello, My name is " & myName & " and I live in " & myCountry
MsgBox myGreeting
End Sub
Same idea as #2; the reason I added example #2 is to demonstrate that if the variable is at the end of the string, there's no need for an additional " quote.
However if we wanted a period at the end of the sentence, the line would look like:
myGreeting = "Hello, My name is " & myName & " and I live in " & myCountry & "."
Note that all of the above examples have identical results.
Adding Double-Quotes within the String:
This is where the " double quotation marks can get a little confusing, since they are needed around the text that you are assigning to the string, but how do we put them inside the string? Well there's a couple ways.
Backing up again, we could add single quotes ' around the name like this:
myGreeting = "Hello, My name is 'Juan' and I live in Mexico."
...and the string would contain:
Hello, My name is 'Juan' and I live in Mexico.
...but if we instead wanted double-quotes " around part od the string for cosmetic reasons like these examples, or for mandatory reasons like requirement in a formula (like your example), we can do one of two things:
add the " symbol using the ASCII character code of 34. The function in VBA would be Chr(34) as as a worksheet function CHAR(34).
or, each time we want a " double-quote, we use two double-quotes together "" (or a quad-quote as I call it.)
For example, hardcoded:
Sub varTest4()
Dim myGreeting As String
myGreeting = "Hello, My name is ""Juan"" and I live in Mexico."
MsgBox myGreeting
End Sub
This would display a popup message box that says:
Hello, My name is "Juan" and I live in Mexico
Or, we could again use a variable with the name and double-quotes around it:
Sub varTest5()
Dim myGreeting As String, myName As String
myName = """Juan"""
myGreeting = "Hello, My name is " & myName & " and I live in Mexico"
MsgBox myGreeting
End Sub
...which will produce the same result as the previous example (#4).
Or we could add the double-quotes to myGreeting instead of via myName:\
Sub varTest6()
Dim myGreeting As String, myName As String
myName = "Juan"
myGreeting = "Hello, My name is """ & myName & """ and I live in Mexico"
MsgBox myGreeting
End Sub
...we will again get the exact same result as above.
And, to demonstrate what that would look like if we wanted double-quotes around both the Name and the Country (so with a double-quote appearing at the very end of the string):
Sub varTest7()
Dim myGreeting As String, myName As String, myCountry As String
myName = "Juan"
myCountry = "Mexico"
myGreeting = "Hello, My name is """ & myName & """ and I live in """ & myCountry & """"
MsgBox myGreeting
End Sub
There are at least a couple ways to add a double-quote within a string:
Using the "" "quad-quote" as above, or,
Using the ASCII character code (which is 34 for a double-quote.)
...because sometimes all these excessive quotes can get confusing.
For example, to show three double-quotes, like this:
"""
I would need to use:
MsgBox """"""""
...that's eight double-quotes: one to begin the string, one to end the string, and three sets of two for each one to display. (Would that be called an Octo-quote?!)
We could display this:
"Juan"
with:
MsgBox """Juan"""
...or exact same results (but a little cleaner in my mind):
MsgBox Chr(34) & "Juan" & Chr(34)
...so therefore changing the line in example #7, this:
myGreeting = "Hello, My name is " & Chr(34) & myName & Chr(34) & _
" and I live in " & Chr(34) & myCountry & Chr(34)
...would produce identical result as example #7 did:
Hello, My name is "Juan" and I live in "Mexico"
Finally, one more thought about the example from your question, on the topic of clarity with confusing strings. You can use the underscore _ as a "line continuation symbol" to break up a long line of code to multiple lines, making it easier to read, both in your VBA Editor (VBE), as well as for others to read or copy/paste when posting code on sites like this!
For example, my "correction" to your line of code was:
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000,MATCH(""" & strLastName & """&""" & strLabCat & """,'[MyMatrix.xlsm]MySheet'!$B$2:$B$1000&'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
...one loooong line (and they can get much loooonger!)
I didn't want to make my original response more confusing my adding extra symbols until I explained the quotes, but a "cleaner" to write lengthy lines is to add underscores anywhere you want to continue on the following line, like this:
strFirstName = Evaluate("INDEX('[MyMatrix.xlsm]MySheet'!$C$2:$C$1000, " & _
"MATCH(""" & strLastName & """&""" & strLabCat & """,'[MyMatrix.xlsm]" & _
"MySheet'!$B$2:$B$1000'[MyMatrix.xlsm]MySheet'!$H$2:$H$1000,0))")
You won't have to scroll-right in your VBE to see the code, and notice how Stack Overflow doesn't have to add the horizontal scroll bar either.
Clear as mud? :-)
Good Luck! (and your question was well-posted question too. Welcome, by the way!)
Further info on:
Quotation Marks in Strings
Declaring Variables in VBA
Breaking and Combining Statements in VB/VBA Code
String Basics in VB/VBA
i have a problem with VisualBasic6 .
i have 3 Textbox and 2 Command Button .
i want to detect VbCrLf when checking first textbox , letter by letter then write in another in second textbox: 101
and another function check the second textbox , if found 101 then make a new line (vbcrlf) in the third textbox.
in a Command :
For i = 1 to len(text1.text)
if Mid(txtD.Text, i, 1) = vbcrlf then
text2.text = 101
endif
next
in Command2 :
if text2.text = 101 then
text3.text = text3.text & vbcrlf
endif
but when i check for Vbcrlf , it seem that character is not a newline-character and text2.text doesn't fill 101
Also i tried VbNewLine but there is same problem !
Thank You my friends !
You need to check exactly what the new line delimiter is. I have seen vbcr, vblf, & vbcrlf. VbNewLine is the same as vbcrlf.
Also, even if you do have vbcrlf in your string, your test will still fail. vbcrlf is TWO characters. You're testing just one character at a time.
A better test would the InStr() function. This is a quick test to see if a smaller string is anywhere in the larger one. So, your test would look like this:
If InStr(txtD.Text, vbcr) or InStr(txtD.Text, vblf) then
text2.text = 101
End If