Convert String to Textbox Name - vb.net

I have a 9 Textboxes and I want to get their values with Val(Me.TxtBoxName.Text)).
The Textboxes already all exists and are named. I want to build the names and access their values with
For i = 0 To 2
For j = 0 To 2
getText = "Me." & "eing_" & i & j & ".Text"
Debug.WriteLine(Val(getText))
Next
Next
(This will get me the Textbox Names eing_00, eing_01, eing_02, eing_10, eing_11, eing_12, eing_20, eing_21, eing_22)
But this does not work as getText is a String. How can I convert it so I can access the Textbox properly?

That's not how it works. You don't magically turn a String into an identifier.
Fortunately though, all controls have a Name property and you can use that property to index the Controls collection of the parent to get a reference to a control. When you add a control in the designer, the Name property is the same as the field declared to refer to the control, e.g. if you add a Button to a form and do this:
MessageBox.Show(Button1.Name)
then it will display "Button1".
So, in your case, that means that you would replace this nonsense:
getText = "Me." & "eing_" & i & j & ".Text"
with this:
getText = Controls($"eing_{i}{j}").Text
That assumes that the controls were added directly to the form. If they were added to some other container, e.g. a Panel, then you must use the Controls collection of that container, rather than that of the form.

I think you need to pay attention to the difference between numbers and strings. The Name property of a Control is a String but the control itself is type Control and the inherited type TextBox in this case. We can refer to an item in the ControlsCollection by providing a String which is the Name property of the control.
The .TryParse takes a String and checks if the string can be converted to a the Type you are parsing. In this case Double. It returns True or False and if True it assigns the converted value to the second parameter. It is important to use this because users can enter any old thing in a text box.
I wasn't sure what kind of number you were expecting so I chose Double. Change to a narrower type if you can. If you need to manipulate numbers with arithmetic they cannot be strings (the Text property is a string)
As a side note, you can do several things with a list of numbers, Average, Max, Min, or Sum.
Dim total = numbers.Sum
Turn on Option Strict to help identify type problems.
Private Sub OpCode()
Dim numbers As New List(Of Double)
For i = 0 To 2
For j = 0 To 2
Dim getText = "eing_" & i & j
Dim ctrl = Controls(getText)
Dim d As Double
If Double.TryParse(ctrl.Text, d) Then
numbers.Add(d)
End If
Next
Next
For Each d In numbers
Debug.Print(d.ToString)
Next
End Sub

Related

Update NumericUpDown.Maximum from corresponding txt.text control using for each loop

As newbie in VBA.net i want to solve following.
I have a form with 38 text controls in 4 groupboxes. These get filled by clicking a row in a datagridview. I have 38 corresponding NUD's where i want the maximum to be equal to its corresponding text.
A 'pair' is always in one of the 4 groupboxes. Besides that there are also textboxes on the form itself to control the DGV.
I have a naming convention that makes it possible to match them easily . NUDGeel corresponds with txtGeel , NUDRood with TxtRood, NUDGroen with txtGroen etc et
Now that update is easily done if you do them one by one
NUDGeel.maximum = txtGeel.text
NUDRood.maximum = txtRood.text
etc
What i want to achieve is that this gets done in a for each loop. (Order to prevent me typing this 38 times (and also just to 'understand' it)
I can figure out how to start the loop
Dim c As Control
For Each c In Me.Controls
If TypeName(c) = "NumericUpDown" Then
'do some magic
End If
Next
I have tried to search for the magic code, and i guess from research it is pretty simple, i just donot get it .
Anyone with an idea how to fix ?
For Each tb In Controls.OfType(Of TextBox)
Dim nud = DirectCast(Controls(tb.Name.Replace("txt", "NUD")), NumericUpDown)
If nud IsNot Nothing Then
nud.Maximum = CDec(tb.Text)
End If
Next
You don't need the If statement if there are no other TextBoxes on the form besides those with corresponding NumericUpDowns.
Private Sub SetMaximum()
Dim controlNames() As String = {
"Geel", "Rood", "Name1", "Name2", ' list all the names of your controls here excluding NUD/txt
}
For Each controlName As String In controlNames
CType(Me.Controls("NUD" & controlName),NumericUpDown).Maximum = CType(Me.Controls("txt" & controlName),TextBox).Text
Next
End Sub
This assumes that all the controls are not inside any containers. If they are, you must use the container (panel, groupbox, etc) instead of Me.

Retrieve specific data in a text file

I have a text file called "Games.txt" that looks like this:
Call of Duty: 50
Assasins Creed: 23
Watch Dogs: 140
If i want to know the number of "Assasins Creed" How would I get that?
What i have tried so far:
I have tried finding it by knowing the line, length of the string. Then reading that and removing the first 15 charachters ("Assasins Creed:") That would leave me with: 23. But this is a pretty bad way of doing it, and it needs me to know the exact line.
What would a better solution be?
If you only want to get the values for "Assasins Creed", with Regex, you can do something like this:
Dim contents As String = IO.File.ReadAllText(filePath)
Dim re As New Regex("(Assasins Creed):\s*(\d+)")
Dim m As Match = re.Match(contents)
If m.Success Then
Console.WriteLine($"The number of '{m.Groups(1).Value}' games is {m.Groups(2).Value}.")
End If
Output:
The number of 'Assasins Creed' games is 23.
If you need to retrieve the values for all games, you can adjust the above code to something like this:
Dim contents As String = IO.File.ReadAllText(filePath)
Dim re As New Regex("(.+):\s*(\d+)")
Dim matches As MatchCollection = re.Matches(contents)
For Each m As Match In matches
Console.WriteLine($"The number of '{m.Groups(1).Value}' games is {m.Groups(2).Value}.")
Next
Output:
The number of 'Call of Duty' games is 50.
The number of 'Assasins Creed' games is 23.
The number of 'Watch Dogs' games is 140.
Notes:
Remember to replace filePath with the actual path of your text file.
You need to add Imports System.Text.RegularExpressions at the top of your class in order to be able to use the Regex class.
Getting started in this game can be tricky because lots of people expect beginners to do their own research and understand it before offering a simple solution and explanation as to why it works. Giving an absolute beginner a regex statement looks more like someone flexing their ego than offering assistance, so if you're looking for a solution that a beginner can not only use, but also have a hope of understanding (and possibly utilise again elsewhere in their code), then the split command could be used like this:
If you Import System.IO it be more useful than regular expressions at this stage I believe
'Place this at the top of the code block above the Class statement
Imports System.IO 'This provides access to File.ReadAllLines (Input/Output)
Then you could create a function which retrieves the information you need and returns the result like this:
''' <summary>
''' Returns the game value plus the index in the list
''' </summary>
''' <param name="FilePath">Full path of the file</param>
''' <param name="Game">Name of the game to return</param>
''' <param name="index">(Optional) Index of the position in the list (-1 if not found)</param>
''' <returns></returns>
Function GameValue(ByVal FilePath As String, ByVal Game As String, Optional ByRef Index As Integer = -1) As Integer
'This returns the file as an array of lines
Dim Lines() As String = File.ReadAllLines(FilePath)
'This loop will iterate through each item in the array
For i As Integer = 0 To Lines.Length - 1
'This turns each line into an array of name and value (either side of the colon + space)
Dim Segments() As String = Lines(i).Split({": "}, StringSplitOptions.None)
'This will bypass any blank lines or lines without ": "
If Segments.Length > 1 Then
'This tries to match the first segment against the name of the game
If Segments(0) = Game Then
'This handles a successful match
'Store the index of the position of the game in the list
Index = i
'Convert final value into Integer and return result
Return Convert.ToInt32(Segments(1))
End If
End If
Next i
'If no correct match for Game is found, 0 is returned
Return 0
End Function
When you want to call the function, you can do this from within a button click for example:
Private Sub cmdApply_Click(sender As Object, e As EventArgs) Handles cmdApply.Click
'Specify the filename however you like
Dim f As String = "C:\ProgramData\game.txt"
'Specify the name of game however you like
Dim g As String = "Watch Dogs"
'Start an index at -1 (This will get modified by the function if the result is found)
Dim i As Integer = -1
'Use the function to return process a result (value)
Dim v As Integer = GameValue(f, g, i)
Console.WriteLine("Game: " & g)
Console.WriteLine("Value: " & v)
Console.WriteLine("Index: " & i)
End Sub
Sometimes, simple things can actually be pretty complicated once you really look into how to do it. And like most things, there's always another way to go about it.
If you try this code you should be able to take out a few things:
How to make and use a function
How to add xml tags to functions to make calling them easier to understand
One method of utilising ByRef to affect the value of a variable declared elsewhere (index)
How to read a text file and process its contents
How to split a string into an array for independent processing
How to convert a string into an integer
Things to note:
An error will occur if file path or name does not exist
An error might occur if Game: xys value is not a number
An error will not occur if the line does not contain ": " because we tested for that
In this case, the game name is case sensitive. You could use Game.ToUpper and Segments(0).ToUpper to convert both values to uppercase when checking if they match if you want to remove case sensitivity

Object required error when executing function

In my user form I have a textbox and only numeric values are allowed to be input into this textbox, once a numeric value is input and the button next to the texbox is clicked it is supposed to do a calculation on the numeric value and add it to my listbox. When I click the button to add the numeric value I get an error object required. Below shows how I am trying to execute this process
userInput= Textbox1.Value
List.AddItem executeFormula(userInput)
Function executeFormula(inputs As Integer)
inputs = inputs * 5
End Function
I have narrowed down the issue, the function is working perfectly but it is when I am trying to add the function onto the end of the List.AddItem
Have you defined Option Explicit at the top of your code? Is the List object correctly named based on what you have on your form? I suspect this is the old VBA equivalent of a null reference exception and VBA isn't able to get a working reference to that listbox.
You could try using:
Set List = UserForm1.List ' or whatever the name of your form is.
Two small problems:
your function should return its result thru the function name
you should explicitly add a String
starting with a blank "forms-style" listbox:
Sub trewr()
Dim lb As ListBox, ii As Integer
Set lb = ActiveSheet.ListBoxes(1)
ii = 56
lb.AddItem (CStr(executeFormula(ii)))
End Sub
Function executeFormula(inputs As Integer) As Integer
executeFormula = 5 * inputs
End Function

How should I handle filling multiple textboxes when I don't know how many of them will have data?

I'm writing an application in VB in which I need to show the user some information which will be copy and pasted into another application however limitations of the other application mean that the string needs to be split into chunks no larger than 55 characters (it's just written notes). I thought the neatest way to do this was to have several textboxes each with a 'copy to clipboard' button to make it convenient for the user.
The code I have is:
Dim invdesc As List(Of String) = Split(splitstring, 55)
txtinvDesc1.Text = invdesc(0)
txtinvDesc2.Text = invdesc(1)
txtinvDesc3.Text = invdesc(2)
...
Split uses a regular expression to return a list of several lines without breaking up words and most of the time this will return a maximum of seven results but occasionally six (my original string max length is 330) and often fewer so my original idea to fill out any strings shorter than 330 with trailing spaces won't work as it's still possible I will either miss text or call a result that isn't there.
Ideally I would just do some kind of loop that only inputs to txtinvDesc(x) while there is data available and ignores the rest (or hides them) but I don't know any way to refer to a textbox other than explicitly or how to put them in any kind of list/array.
So it's a bit of an open question in "how best can I handle this requirement?"
You can create a collection (e.g., Array or List) of TextBox like with any other type/class (as you are doing with String in your code). Sample:
Dim allTextBoxes As New List(Of TextBox)
allTextBoxes.Add(txtinvDesc1)
allTextBoxes.Add(txtinvDesc2)
allTextBoxes.Add(txtinvDesc3)
Alternatively, you might iterate through all the controls in the main form by checking its type (a textbox or not). In that case you would have to set a relationship between the given name of the textbox and the data list index, via other collection for example:
Dim mappingList As New List(Of String)
mappingList.Add("txtinvDesc1")
mappingList.Add("txtinvDesc2")
mappingList.Add("txtinvDesc3")
For Each ctr As Control In Me.Controls
If (TypeOf ctr Is TextBox AndAlso mappingList.Contains(ctr.Name)) Then
ctr.Text = invdesc(mappingList.IndexOf(ctr.Name))
End If
Next
--- CLARIFICATION (not as evident as I thought)
The proposed for each loop relies on a mapping approach, that is, it relates each element in invdesc with the corresponding TextBox name. By definition, both arrays HAVE TO have the same number of elements (otherwise the mapping system wouldn't have made any sense). This is the most efficient and overall-applicable alternative; if the names of the textboxes and invdesc have elements in common (e.g., the numbers), you might just compare the names. BUT WHEN MAPPING YOU HAVE TO ACCOUNT FOR ALL THE ELEMENTS (if there is no associated TextBox to a given item, let the value blank; but all the items have to be accounted).
If you want to index the tbs:
Private TBs as New List (of TextBox)
Early on (after FormLoad) maybe in a FormSetup:
TBs.Add(txtinvDesc1)
TBs.Add(txtinvDesc2)
TBs.Add(txtinvDesc3)
...
Then:
Dim invdesc As List(Of String) = Split(splitstring, 55)
For n As Integer = 0 To invdesc.Count-1
TBs(n).Text = invdesc(n)
Next
' handle the varying 7th TB:
For n As Integer = invdesc.Count-1 To TBs.Count - 1
TBs(n).Enabled = False
TBs(n).Text =""
Next
Or a For/Each:
Dim ndx As Integer = 0
For Each tb As TextBox In TBs
tb.Text = invdesc(ndx)
ndx += 1 ' thanks varo!
Next
Then hide/disable or at least clear the text from any empty ones.
If it turns out there are always 6 you really only need an if statement:
txtinvDesc1.Text = invdesc(0)
txtinvDesc2.Text = invdesc(1)
txtinvDesc3.Text = invdesc(2)
...
If incDesc.Count-1 = 6 Then
txtinvDesc7.Text = invdesc(6)
Else
txtinvDesc7.Enabled= False
txtinvDesc7.Text = ""
End If
I would change the TB names to start at txtinvDesc0.Text to avoid getting confused (as I may have)
Use multiline textbox and in OnKeyPress event force 55 chars per line. You can find subclassed TextBox with that feature in this SO answer :
https://stackoverflow.com/a/17082189/351383

Identify and populate a listbox

It's a riddle for me: what is the syntax to populate a listbox? But first: how do you identify a listbox? In many forums I read: ListBox1.Additem... But how do they know it's 'ListBox1'?
That's the default name for a ListBox control when you add it to your form. VB and VBA automatically name new or unnamed controls with the name of the type of control, suffixed with an integer identifier.
It's completely irrelevant what your control is called. The point of the sample code is to demonstrate a concept. You should replace ListBox1 with whatever your control is named.
And you should definitely name your controls something other than the default, because as you observe here, it's not very descriptive.
It used to be recommended by everyone to name controls following some type of quasi-Hungarian notation with a 3-letter prefix indicating the type of control, followed by your regular descriptive name. Over the past few years, there's been a big backlash against anything that looks like Hungarian notation, but I think it's still quite useful with regards to naming GUI controls, so I still use it. For a ListBox control, I might call it something like: lstCustomers or lstItemsForSale. But it's completely optional: again, what you choose to name your controls is irrelevant to how the code works or how the application will behave.
So, to populate a listbox in VB 6 and/or VBA, you'd use the following code, where myListBox is the name of your ListBox control:
' Add an item to the end of the list
myListBox.AddItem "Peaches"
' Insert an item at the top of the list
myListBox.AddItem "Apples", 0
' Remove the first item in the list
myListBox.RemoveItem 0
ListBox1.AddItem is for loading a single column ListBox (CodyGrey's answer covers that).
If you are using a multi column ListBox (property .ColumnCount > 1), then you need to load an Array into .List. The following snippet loads 3 rows into a 2 column ListBox
Dim dat(0 To 2, 0 To 1) As Variant
dat(0, 0) = "A"
dat(0, 1) = "B"
dat(1, 0) = "C"
dat(1, 1) = "D"
dat(2, 0) = "E"
dat(2, 1) = "F"
ListBox1.List = dat
Accessing the List Box: (this will vary for different versions of Word, this is for 2003)
To access the ListBox properties:
Display the "Control Toolbox" toolbar
Go To Design Mode
Click the control, and select Properties on the "Control Toolbox"
The Name property should now be visible and editable
Other properties, such as ColumnCount etc can also be set
When using VBA in Access, a good way to fill in the whole listbox at once is to use the RowSource property when the ListBox is a ValueList. The AddItem method is a bit slow when adding many entries.
For example, to fill in a list of files from a directory, you could use:
Dim src As String
Dim S as String
S = Dir(FullPathToCurrentDirectory & "\*.*")
While Len(S) <> 0
src = src & """" & S & """;"
S = Dir
Wend
If Len(src) = 0 Then src = """"";"
Me.lstFiles.RowSource = Left(src, Len(src) - 1) ' leave off the last ;