Validate a textbox value to contain certain strings - forms

I have a VB.NET form and the user would select from 40 different values.
I want instead of a dropbox with all 40 values to use a textbox that validates if the stirng given by user is one valuo out off those 40 words.
so for example I need the user writes something and validate the string is one of those 40 reserved words like "urgent","post"... maybe these words stored in an array, and then compared what user wrote against that?
TextBox1.Text.Contains("urgent")
TextBox1.Text.Contains("post")
TextBox1.Text.Contains("standard")
TextBox1.Text.Contains("stay")
Maybe a method
Public Function Contains(ByVal value As String) As Boolean
Return ( TextBox1.Text(value, ...) >= 0)
End Function
What would be the best way to do this?

You can create a List(of String) of your Reserved Words use the Contains method to check if what was typed was one of them using your Contains function something like this. I am not sure how large the Text you are going to be parsing so I am splitting it into individual words.
Public Class Form1
Dim reservedWords As List(Of String) = New List(Of String)({"urgent", "post", "standard", "stay"})
Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged
Dim text As String = CType(sender, TextBox).Text
If ContainsReservedWord(text) Then Beep()
End Sub
Public Function ContainsReservedWord(value As String) As Boolean
Dim x As Integer
Dim stringSplit As String() = value.Split
If stringSplit.Count > 0 Then
For x = 0 To stringSplit.Count - 1
If reservedWords.Contains(LCase(stringSplit(x))) Then Return True
Next
End If
Return False
End Function
End Class

Use the below code to validate the string that belongs to reserved words or not.
var lstReservedWords = new List<string> {"urgent", "post", "standard", "stay", .......};
bool isReservedWord = lstReservedWords.Any(r => String.Compare(r, TextBox1.Text, true) == 0);
Hope this helps.

Related

using class module properties in UDF's excel vba

I have the following function that works fine:
Function GetAgentEmailWorksheet(AgentObjectId As String)
Dim specific_agent As clsAgent
Set specific_agent = New clsAgent
specific_agent.AgentSheetName = "agentsFullOutput.csv"
Dim id_array() As Variant
id_array = specific_agent.AgentIDArray
Dim email_array() As Variant
email_array = specific_agent.AgentEmailArray
GetAgentEmailWorksheet = vlook_using_array(AgentObjectId, id_array, email_array)
End Function
When, however, I change the last line to:
GetAgentEmailWorksheet = vlook_using_array(AgentObjectId, specific_agent.AgentIDArray, specific_agent.AgentEmailArray)
I get the following error:
Compile error:
Type mismatch:array or user-defined type expected
and it hi-lights AgentIDArray (or AgentEmailArray if I sub out the first parameter.
Why?
EDIT
here's the fuction vlook_using_array:
Function vlook_using_array(target_string As String, _
input_array() As Variant, _
output_array() As Variant)
Dim rows_dim As Long
Dim cols_dim As Integer
For rows_dim = 1 To UBound(input_array, 1)
For cols_dim = 1 To UBound(input_array, 2)
If input_array(rows_dim, cols_dim) = target_string Then
vlook_using_array = output_array(rows_dim, cols_dim)
End If
Next cols_dim
Next rows_dim
End Function
Here are the clsAgent properties:
Public Property Get AgentClientsArray() As Variant
AgentClientsArray = get_column_array(AgentClientsCol)
End Property
Public Property Get AgentIDArray() As Variant
AgentIDArray = get_column_array(1)
End Property
Public Property Get AgentEmailArray() As Variant
AgentEmailArray = get_column_array(AgentEmailCol)
End Property
Here's the function that's in the class module:
Private Function get_column_array(col_num As Integer) As Variant
' create a range out of the used range (of the sheet) in the column specified
' used to create array properties in the class
Dim total_rows As Long
total_rows = Worksheets(Me.AgentSheetName).UsedRange.rows.Count
Dim target_range As Range
With Worksheets(Me.AgentSheetName)
Set target_range = .Range(.Cells(1, col_num), .Cells(total_rows, col_num))
End With
Dim target_arr() As Variant
target_arr = target_range
get_column_array = target_arr
End Function
There's a difference between this:
Public Property Get AgentIDArray() As Variant
AgentIDArray = get_column_array(1)
End Property
and
Public Property Get AgentIDArray() As Variant()
AgentIDArray = get_column_array(1)
End Property
The first (which you have in your class) returns a Variant which happens to be an array, the second an array of variants.
So, the return value of your AgentIDArray is not consistent with the parameter types expected by vlook_using_array

Working with Classes in vb6 with user selected fields

I found out how to create the properties of the class, like this:
private m_Name as string
public property get Name() as string
Name = m_Name
end sub
public property let Name(sval as string)
m_name = sval
end sub
The user will create a document and choose some fields (Name, Birthday, Phone....) inside this document, as I can't know exactly which fields will be chosen by the user, I thought create a class would be the best option.
After I create the class like above, how may I make a loop through this class to check which fields has been chosen by the user?
Any better option for my situation, please, let me know...
If I understood you correctly, you want to know which fields (out of a number of existing fields) user used/initialized?
I see several ways to do it:
1) If your variables do not have default values and must have a non-empty/non-zero values, then you can simply check if a variable is empty or zero. If it is, it hasn't been initialized.
If m_name = "" Then MsgBox "Variable is not initialized"
2) for each field you have, create a boolean fieldName_Initialized so for each field, you would have something like this:
private m_Name as string
private m_name_Initialized as Boolean
public property get Name() as string
Name = m_Name
end sub
public property let Name(sval as string)
m_name = sval
m_name_Initialized = True
end sub
3) you could have a list and add variable names to the list as they become initialized:
Make sure to add Microsoft Scripting Runtime to your References for Dictionary to work.
Dim initialized As Dictionary
Set initialized = New Dictionary
private m_Name as string
private m_name_Initialized as Boolean
public property get Name() as string
Name = m_Name
end sub
public property let Name(sval as string)
m_name = sval
initialized.Add "m_name", True
end sub
Then, to check if the var has been initialized:
If initialized.Exists("m_name") Then
' Var is initialized
4) similar to #3, except use an array of booleans. Tie specific var to a specific index, like m_name is index 0. This way you skip the hassle of controlling variable names (adds to maintenance cause as far as I know you can't get the name of the variable)
Personally, #1 is the most simple, but may not be possible in a certain situations. If #1 does not apply, I would personally pick #2, unless someone can figure out how to get a string representation of a variable name from the variable itself, then #3 is preferred.
I guess what you need is a kind of Nullable-behaviour. Yes you can do this ins VB6 with the datatype Variant. Then you can use the function "IsEmpty()" to check if a property was already set or not.
a little code-example:
Option Explicit
Private m_Vars()
'0 : Name
'1 : Birthday
'2 : Phone
Private Sub Class_Initialize()
ReDim m_Vars(0 To 2)
End Sub
Public Property Get Name() As String
Name = m_Vars(0)
End Property
Public Property Let Name(RHS As String)
m_Vars(0) = RHS
End Property
Public Property Get Birthday() As Date
Birthday = m_Vars(1)
End Property
Public Property Let Birthday(RHS As Date)
m_Vars(1) = RHS
End Property
Public Sub DoSomething()
Dim i As Integer
For i = 0 To UBound(m_Vars)
Dim v: v = m_Vars(i)
If IsEmpty(v) Then
MsgBox "is empty"
Else
MsgBox v
End If
Next
End Sub
If I understand correctly, the user will add tags or something to something like a document (e.g. [Name]) and you want to know how to map your class members to these tags. To do this, you dont really want to loop thru the class members, but the document tags to find out what is needed. When you find a "tag", submit it to your class to fill in the blank.
Part 1 is a parser to find the tags...my VB6 is very rusty, so pseudocode for this:
Const TagStart = "[" ' "$[" is better as it is not likely to appear
Const TagStop = "]" ' "]$" " " "
' make this a loop looking for tags
i = Instr(docScript, TagStart)
j = Instr(docScript, TagStop)
thisTag = Mid(docScript, TagStart, TagEnd )
' not correct, but the idea is to get the text WITH the Start and Stop markers
' like [Name] or [Address]
' get the translation...see below
docText = MyClass.Vocab(thisTag)
' put it back
Mid(docScript, TagStart, TagEnd) = docText
It is actually better to look for each possible legal tag (ie Instr(docScript, "[Name]")) which are stored in an array but you may have to do that in 2 loops to allow that a given tag could be requested more than once.
Part 2 supply the replacement text from MyClass:
Friend Function Vocab(tag as string) As String
Dim Ret as string
Select Case tag
Case "$[NAME]$"
ret = "Name: " & m_Name
' if the caption is part of the "script" then just:
'ret = m_Name
Case "$[ADDRESS]$"
ret = "Address: " & m_Addr
' if not found, return the tag so you can add new Vocab items
' or user can fix typos like '[NMAR]'
Case Else
ret = tag
...
End Select
Return Ret
End Function
The parsing routines in Part 1 could also be a method in your class to process the document "script" which calls a private Vocab.
Edit
a fraction of a 'script' might look like this:
Customer's Name:| $[CUST_FNAME]$ $[CUST_LNAME]$ (ignore the pipe (|) it was a table cell marker)
The parser looks thru the string to find "$[", when it does, it isolates the related tag $[CUST_FNAME]$. If you have a large number, the first part (CUST) can be used as a router to send it to the correct class. Next, call the method to get the translation:
newText = Cust.Vocab(thisTag)
Cust Class just looks at the tag and returns "Bob" or whatever and the parsing loop replaces the tag with the data:
Customer's Name:| Bob $[CUST_LNAME]$
Then just continue until all the tags have been replaced.
With "just" 22 vocab items, you could create a dedicated class for it:
Vocab.Translate(tag ...) as string
Case "$[CUST_FNAME]$"
return Cust.FirstName
...or
Are you trying to work out a way to do this via a DOC object from office? The above is more of from the ground up document composition type thing. For office I'd think you just need some sort of collection of replacement text.
HTH
If the user has a form that they do something to create their document, why not use simple checkbox controls, one for each possible field? If you want to use a loop to check for selected fields make the checkboxes a control array and loop though the array. You can assign the field name to the Tag property, then if the checkbox is checked add the field to an array.

VBA giving method or data member not found for a method I have defined

I have been trying to implement a very simple tree structure in VBA for some basic text parsing needs. The relevant code follows.
Private pLeaves() As CTree
Private numLeaves As Integer
Private leavesLen As Integer
Private iterate As Integer
Private pParent As CTree
Public pValue As Object
Public Sub class_initialize()
ReDim pLeaves(10)
leavesLen = 10
numLeaves = 0
Set pParent = Nothing
iterate = 0
Set pValue = Nothing
End Sub
Public Sub Class_terminate()
'We'll leave it empty for now
'if it looks like it's not working right we might change it
End Sub
Public Property Get Parent() As CTree
Parent = pParent
End Property
Public Property Get Leaves() As CTree
Leaves = pLeaves
End Property
Private Property Set Parent(ByRef p As CTree)
Set pParent = p
End Property
Private Property Set value(ByRef value As Object)
Set pValue = value
End Property
Public Sub Add_Leaf(ByRef value As Object)
Dim n As Integer
n = numLeaves
If numLeaves >= leavesLen Then
ReDim Preserve pLeaves(leavesLen + 10)
leavesLen = leavesLen + 10
End If
Set pLeaves(n) = New CTree
Set pLeaves(n).Parent = Me
Set pLeaves(n).value = value
End Sub
Public Function DFFind_Leaf(value As Object) As CTree
Dim i As Integer
If pValue = value Then
Set DFFind_Leaf = Me
Return
End If
If numLeaves = 0 Then
Set DFFind_Leaf = Nothing
Return
End If
For i = 0 To numLeaves
Set DFFind_Leaf = pLeaves(i).DFFind_Leaf(value)
If DFFind_Leaf <> Nothing Then
Return
End If
Next i
Set DFFind_Leaf = Nothing
Return
End Function
When I try to call the Add_Leaf function with an object though I end up getting a
method or data member not found error from VBA on the line where I'm trying to
set pLeaves(n).Parent= me
Any idea what the reason for that might be?
Edit: ok I figured out how to fix this. Changing the set property Parent(...) from private
to public fixed the problem. So apparently I don't quite understand what private exactly does in vba. If someone wants to explain how to do this without essentially exposing the variables for anyone to set as they want I would be grateful.
Private tells the VBA interpreter that the scope of the variable is restricted to the class/module only. You can therefore access it only inside of the original class, but not from the outside. Publicon the other hand allows you to access the variable from the outside.
However, note that when working with classes, it is strongly discouraged to publicly expose any variables. Instead it is best practice to expose so called properties, using the SET PROPERTYand GET PROPERTY keywords.
I.e. instead of
Public Amount as Double
It is often better to use this approach:
Private mAmount as Double
Property Set Amount(amt as Double) 'Note that by default, properties are public
If amt > 0 Then
mAmount = amt
Else
mAmount = 0
End If
End Property
Property Get Amount as Double
Amount = mAmount
End Property
As you can see from this example, the advantage is that you can actually add additional verifications/modifications and therefore ensure the consistency.
In your example, you could consider to simply provide a method, i.e. a public sub, that adds a leave and takes care of all the settings, e.g.
Public Sub AddLeave(strName as String)
Dim objLeave as New CTree
objLeave.Init strName, Me
mLeaves.Add objLeave, strName
End Sub
Public Sub Init(strName as String, objParent as CTree)
Me.Name = strName
Set Me.Parent = objParent
End Sub

How have you dealt with the lack of constructors in VB6?

VB6 classes have no parameterized constructors. What solution have you chosen for this? Using factory methods seems like the obvious choice, but surprise me!
I usually stick to factory methods, where I put the "constructors" for related classes in the same module (.BAS extension). Sadly, this is far from optimal since you can't really limit access to the normal object creation in VB6 - you just have to make a point of only creating your objects through the factory.
What makes it worse is having to jump between the actual object and your factory method, since organization in the IDE itself is cumbersome at best.
How about using the available class initializer? This behaves like a parameterless constructor:
Private Sub Class_Initialize()
' do initialization here
End Sub
I use a mix of factory functions (in parent classes) that then create an instance of the object and call a Friend Init() method.
Class CObjects:
Public Function Add(ByVal Param1 As String, ByVal Param2 As Long) As CObject
Dim Obj As CObject
Set Obj = New CObject
Obj.Init Param1, Param2
Set Add = Obj
End Function
Class CObject:
Friend Sub Init(ByVal Param1 As String, ByVal Param2 As Long)
If Param1 = "" Then Err.Raise 123, , "Param1 not set"
If Param2 < 0 Or Param2 > 15 Then Err.Raise 124, , "Param2 out of range"
'Init object state here
End Sub
I know the Friend scope won't have any effect in the project, but it acts as a warning that this is for internal use only.
If these objects are exposed via COM then the Init method can't be called, and setting the class to PublicNotCreatable stops it being created.
This solution is far from perfect, however, this is what I ended up doing for some of my legacy projects. It's somewhat memory-efficient for storage, but just looking up something definitely takes more time than otherwise required, especially for the huge structs.
Suppose you need to implement the following,
type user
id as long
name as String
desc as String
end type
But notice that VB6 natively supports Strings. So Instead of using structs, have "fake-struct" functions such as
Public Function user_raw(ByVal id as Long, ByRef name as String, ByRef desc as String) as String
Const RAW_DELIM as String = "|"
user_raw = cstr(id) & RAW_DELIM & name & RAW_DELIM & desc
End Function
So as a result, you will have passable-by-value (hence stackable into arrays, or multiline Strings), "fake-structs" made of strings such as
1|Foo|The boss of VB
2|Bar|Foo's apprentice
So the construct also counts as a crude way to perform to_string(). On the slightly good side, such storage is almost instantly convertible to the Markdown table syntax.
Later on, you can retrieve data from the struct by doing something like,
Public const RAW_DELIM as String = "|"
Public Const POS_USER_ID = 0
Public Const POS_USER_NAME = 1
Public Const POS_USER_DESC = 2
'...
public function get_raw_variable(byref user as String, byval var as integer) as String
dim buf() as String
buf = SPLIT(user, "|")
Assert ubound(buf) <= var
get_raw_variable = buf(var)
end function
The retrieval is sure inefficient, but is about the best you can have. Good luck

Can I return a List from a LotusScript Function?

I want to return a List from a Function in LotusScript.
eg.
Function myfunc() List As Variant
Dim mylist List As Variant
mylist("one") = 1
mylist("two") = "2"
myfunc = mylist
End Function
Dim mylist List As Variant
mylist = myfunc()
Is this possible?
If so, what is the correct syntax?
It seems you can't return a List from a Function.
You can easily wrap it in a class though and return the class.
eg.
Class WrappedList
Public list List As Variant
End Class
Function myfunc() As WrappedList
Dim mylist As New WrappedList
mylist.list("one") = 1
mylist.list("two") = "2"
Set myfunc = mylist
End Function
Answer was found here: LotusScript's List bug strikes again
This works fine for me. I've set one value to string and the other to integer so you can see that the variants behave themselves.
Sub Initialize
Dim mylist List As Variant
Call myfunc(mylist)
Msgbox "un = " + mylist("one")
Msgbox "deux = " + cstr(mylist("two"))
End Sub
Sub myfunc(mylist List As Variant)
mylist("one") = "1"
mylist("two") = 2
End Sub
Simply put you gotta have a function that returns a variant. I can see that you like to do it in an object oriented fashion, but if you just want to "get it done" procedurally is the easiest.
Although there are a couple of ways to do this, this is my preferred way. Note that you can create a list of any primitive data type, (ie string, variant, integer, long etc).
Function myfunc as variant
dim mylist list as variant
mylist("somename") = "the value you want to store"
mylist("someothername") = "another value"
myfunc = mylist
End Function
To use myfunc ..
sub initialise
dim anotherlist list as variant
anotherlist = myfunc
end sub
You can add parameters to myfunc if you need to by simply defining myfunc this way
function myfunc(val1 as variant, val2 as variant) as variant
You call it the same ways with parameters like this
anotherlist = myfunc("a value", "another value")
Note that "variant" is your universal datatype. What's important is that myfunc as a variant is the only way you can return lists and variants from a function.
You can get a function toi return a list, just get rid of "List" bit in your function, so instead of
Function myfunc() List As Variant
...
End Function
...do:
Function myfunc() As Variant
then you can call your function as you already do.
Dim mylist List As Variant
mylist = myfunc()
Lists are great and I use them all the time, but I have found one limitation with lists...
if if have a function like:
Public Function returnMyList() as Variant
Dim myList List as Variant
Dim s_test as String
Dim i as Integer
Dim doc as NotesDocuemnt
Dim anotherList List as String
'// ...set your variables however you like
myList( "Some Text" ) = s_text
myList( "Some Integer" ) = i
myList( "Some Doc" ) = doc
'// If you returned the list here, you're fine, but if you add
'// another list...
anotherList( "one" ) = ""
anotherList( "two" ) = ""
myList( "Another List" ) = anotherList
'// This will cause an error
returnMyList = myList
'// I bodge around this by writting directly to a List
'// that is set as global variable.
End Function