I'm trying to make several TextBoxes visible and invisible, depending on the number in another textbox. In Fact I have 14(TextBox1, TextBox2, ...), this is my code so far:
Dim s As Integer = 0
While s > 14
s += 1
Dim txtBox As String = "TextBox" & CStr(s)
CObj(txtBox).Visible = False
End While
If txtBoxHowmany.Text = "" Then
Else
Dim s As Integer = 0
While s > txtBoxHowmany.Text
s += 1
Dim txtBox As String = "TextBox" & CStr(s)
CObj(txtBox).Visible = True
End While
End If
I created a list of text boxes and filled it in form Load. You can use this list in any method in your form.
In the Button.Click I used .TryParse to check the contents of TextBox7. I added a number range to the test with AndAlso's. AndAlso short circuits the If so that the following conditions will not be checked if the previous condition is False.
Next we use the .Take extension Function to get the text boxes we want to alter. A For Each loop actual changes the .Visible state.
Private TBoxes As New List(Of TextBox)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TBoxes.AddRange({TextBox1, TextBox2, TextBox3, TextBox4, TextBox5, TextBox6})
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim NumVisible As Integer
If Integer.TryParse(TextBox7.Text, NumVisible) AndAlso NumVisible > 0 AndAlso NumVisible < 15 Then
Dim VisibleBoxes = TBoxes.Take(NumVisible)
For Each tb In VisibleBoxes
tb.Visible = True
Next
Else
MessageBox.Show("Please enter a valid number.")
End If
End Sub
Design tip: use a spinner instead of a textbox.
Then you define the Minimum and Maximum values, while Value represents the currently-selected value. That makes the code more robust against incorrect input.
So the code could look like this:
dim s as integer = Me.spinner.Value
For i as Integer = 1 to s
Me.Controls("TextBox" & s.ToString).Visible = True
next
If value 5 is selected, then TextBox1 through TextBox5 become visible and you can continue with the rest and hide them as required.
Or a slightly more advanced example:
dim s as integer = me.spinner.Value
For Each ctl As Control In Me.Controls
Dim i as integer = 1
If TypeOf ctl Is TextBox Then
If ctl.Name.StartsWith("TextBox") Then
If ctl.Name = ("TextBox" & i.ToString()) ' eg TextBox1, TextBox5...
If i <= s Then ' counter <= value of spinner
ctl.Visible= True
Else
ctl.Visible= False
End If
End If
i += 1
End If
End If
Next
Here we loop on the form's child controls, look for those of Textbox type. i is an internal counter that is incremented at each occurrence of a TextBox control which name starts with 'TextBox'. If the control name starts with TextBox, and the rest of the string is a number <= spinner value, we set the Visible property to True. Otherwise, Visible is set to False.
So if the spinner value is 5, controls TextBox1 through TextBox5 should become visible, while the rest are to be hidden. The advantage to this approach is that the number of textboxes can be variable.
Disclaimer: untested code.
Update 09 March 2020
Here is a revised version that relies on a LINQ query to fetch a sorted list of textboxes in the form, assuming your controls are always named in the same pattern ie textbox1 through 14.
In theory this code could do the trick but it will not sort like I want ie textbox1 will be followed by textbox11, 12, 13, 14 and then textbox2.
Dim textboxes= From txt In Me.TableLayoutPanel1.Controls.OfType(Of TextBox)() _
Where txt.Name.StartsWith("TextBox") _
Order By txt.Name ' 1, 2, 14...
So I have tweaked the expression a little bit. I am assigning a variable textbox_number from LINQ to extract the number of the textbox:
Dim textboxes = From txt In Me.TableLayoutPanel1.Controls.OfType(Of TextBox)() _
Let textbox_number = Convert.ToInt32(txt.Name.Substring(7, txt.Name.Length - 7)) _
Where txt.Name.StartsWith("TextBox") _
Order By textbox_number ' 1, 2, 14...
This function is for demonstration purposes and should be improved to be made safer. It will work for your purpose though. By doing this trick I can perform a numeric sort so that that the controls are listed in the right order.
So if you select 5 in the spinner then controls 1 through 5 are made visible and the rest are hidden.
I have attached a screenshot. Note that I put all the textboxes in a TableLayoutPanel to improve presentation.
Probably, your controls are placed directly in the form and not in a container, then simply replace Me.TableLayoutPanel1.Controls with Me.Controls.
Public Class frmtextboxes
Private Sub butShow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butShow.Click
Dim s As Integer = Me.spinner.Value ' number of textboxes to show
' LINQ query: get list of textboxes sorted by number
Dim textboxes = From txt In Me.TableLayoutPanel1.Controls.OfType(Of TextBox)() _
Let textbox_number = Convert.ToInt32(txt.Name.Substring(7, txt.Name.Length - 7)) _
Where txt.Name.StartsWith("TextBox") _
Order By textbox_number ' 1, 2, 14...
For Each ctl In textboxes
Console.WriteLine("TextBox found, name: " & ctl.txt.Name & "=> " & ctl.textbox_number)
If ctl.textbox_number <= s Then
Me.TableLayoutPanel1.Controls(ctl.txt.Name).Visible = True ' make control visible
Else
Me.TableLayoutPanel1.Controls(ctl.txt.Name).Visible = False ' hide control
End If
Next
End Sub
End Class
Related
ok so I've done numerous searches and come up with the following code to increment up the text box name, but it just does not work. The text box is within a tab control does this matter? I've tried to reference it within the tab control but no joy.
Public Cpv_Coeffs As New List(Of Decimal)
Dim i As Integer
For i = 0 To 6
'Cpv_Coeffs.Add(txt_Cp_Coef_A1.Text) 'this line works fine
Cpv_Coeffs.Add(Me.Controls("txt_Cp_Coef_A" & 1).Text)
Next i
i just get a null reference exception
where am I going wrong?
If you want it to work, no matter which container the control is in, then use the Controls.Find() fucntion. It can recursively search for the control no matter how deeply nested it is:
Public Cpv_Coeffs As New List(Of Decimal)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim ctlName As String
For i As Integer = 0 To 6
ctlName = "txt_Cp_Coef_A" & i
Dim ctl As Control = Me.Controls.Find(ctlName, True).FirstOrDefault
If Not IsNothing(ctl) AndAlso TypeOf ctl Is TextBox Then
Dim tb As TextBox = DirectCast(ctl, TextBox)
Dim dcml As Decimal
If Decimal.TryParse(tb.Text, dcml) Then
Cpv_Coeffs.Add(dcml)
Else
MessageBox.Show("Value: " & tb.Text, "Invalid Decimal")
End If
Else
MessageBox.Show("Name: " & ctlName & vbCrLf & "Could Not Find Control, or it was not a TextBox.", "Error")
End If
Next i
End Sub
The above example is verbose, but it shows you exactly where all the failure points could occur.
Can anyone help with this school task I have
The task is to ask the user for items and the cost of the items until they chose to stop. Then combine all the costs and take 20% VAT and 10% off from 2 randomly selected items.
Here is the code I have so far (I have 2 buttons and a listbox)
Public Class Form1
Dim CurrentA As Integer
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim Items(CurrentA) As String
Dim Coins(CurrentA) As Single
Dim Stay As String
CurrentA = 0
Do Until CurrentA = 20
Items(CurrentA) = InputBox("Please Enter The Item")
Coins(CurrentA) = InputBox("Please Enter The Cost Of The Item")
Stay = InputBox("Type Yes If More Items or Type No if no More")
Stay = Stay.ToLower
If Stay = "yes" Then
End If
If Stay = "no" Then
Exit Do
End If
ListBox1.Items.Add(Items(CurrentA) & " " & Coins(CurrentA))
CurrentA += 1
Loop
End Sub
End Class
First, a few comments on the code you presented.
Dim CurrentA As Integer
'An Integers default value is zero, I don't see why this is a class level variable
'always declare variables with as narrow a scope as possible
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim Items(CurrentA) As String 'Declares an Array of Type String with an Upper Bound of 0
'Upper Bound is the highest index in the array
'Arrays start with index 0
'So your array will have 1 element at index 0
Dim Coins(CurrentA) As Single
Dim Stay As String
CurrentA = 0 'unnecessary because the default of CurrentA is already 0, but OK for clarity because it could have been changed elsewhere
'This is behaving like a console application with the code repeating in a loop.
'In Windows Forms it would be more likely to do this in a button click event (btnAddItem)
Do Until CurrentA = 20
'On the first iteration CurrentA = 0
'On the second iteration CurrentA = 1 - this exceeds the size of your array
'and will cause an index out of range error
Items(CurrentA) = InputBox("Please Enter The Item")
'With Option Strict on you must change the input to a Single
Coins(CurrentA) = CSng(InputBox("Please Enter The Cost Of The Item"))
Stay = InputBox("Type Yes If More Items or Type No if no More")
Stay = Stay.ToLower 'Good! The user might no follow directions exactly
If Stay = "yes" Then
'This is kind of silly because it does nothing
End If
'Lets say I say no on the first iteration
'This avoids the index out of range error but
'nothing is added to the list because you Exit the loop
'before adding the item to the ListBox
If Stay = "no" Then
Exit Do
End If
ListBox2.Items.Add(Items(CurrentA) & " " & Coins(CurrentA))
CurrentA += 1
Loop
End Sub
We could use arrays but not knowing how many items will be added means either making the array bigger than needed or using Redim Preserve on every addition. A much better choice is a List(Of T). They work a bit like arrays but we can just add items without the ReDim stuff.
Private lstCost As New List(Of Single)
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
'Pretend this button is called btnAdd, and you have 2 test boxes
lstCost.Add(CSng(TextBox2.Text))
'The $ introduces an interpolated string. It is a step up form String.Format
ListBox2.Items.Add($"{TextBox1.Text} - {CSng(TextBox2.Text):C}") 'The C stands for currency
TextBox1.Clear()
TextBox2.Clear()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'Pretend this button is called btnTotal
'Dim total As Single = (From cost In lstCost
' Select cost).Sum
Dim total As Single = lstCost.Sum
Label1.Text = total.ToString("C") 'C for Currency
End Sub
What i want is when i input a number in texbox1.text like for example i enter 3 it should show 3 textbox but i always get an error. and also i have to save it in database but i dont know how. Help Please..
Private boxes(TextBox1.text) As TextBox
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim newbox As TextBox
For i As Integer = 1 To TextBox1.Text
newbox = New TextBox
newbox.Size = New Drawing.Size(575, 35)
newbox.Location = New Point(10, 10 + 35 * (i - 1))
newbox.Name = "TextBox" & i
newbox.Text = newbox.Name
'connect it to a handler, save a reference to the array and add it to the form controls
AddHandler newbox.TextChanged, AddressOf TextBox_TextChanged
boxes(i) = newbox
Me.Controls.Add(newbox)
Next
End Sub
OK. The error I get when I try to run your code is :-
An unhandled exception of type 'System.InvalidCastException' occurred in Microsoft.VisualBasic.dll
Additional information: Conversion from string "" to type 'Integer' is not valid.`
This is because you're trying to start a loop using a string as the termination index for the loop. Try using
For i As Integer = 1 To Val(TextBox1.Text)
your next problem will depend on how you've declared boxes. If you have declared it like this ..
Dim boxes() As TextBox
You'll end up with a Null reference exception because when you declared boxes, you didnt supply any elements. To remedy this you'll need to add this just before your loop ..
ReDim Preserve boxes(Val(TextBox1.Text))
If boxes is a list.. and to be honest .. thats a better choice than an array, instead of the above line you'll need to change
boxes(i) = newbox
to
boxes.Add(newbox)
You might also need to change other code associated with boxes, but the work will be worth it.
Your biggest problem is that you're trying to get a value from a TextBox that hasn't even appeared yet. You've put your code inside the form's load event. It really needs to be in a separate method. Oh and rather than use the TextBox.changed event you should use a button control to execute the method. Otherwise it's too easy for someone to change the number in the textbox. With your code, each time the textbox is changed (deleting a digit or adding another digit), more TextBoxes will be added and you could end up with lots of them.
So possible final code should look like ..
Public Class Form1
Dim boxes As New List(Of TextBox)
Private Sub Addbuttons(buttonCount As Integer)
Dim newbox As TextBox
For i As Integer = 1 To buttonCount
newbox = New TextBox
newbox.Size = New Drawing.Size(575, 35)
newbox.Location = New Drawing.Point(10, 10 + 35 * (i - 1))
newbox.Name = "TextBox" & i
newbox.Text = newbox.Name
'connect it to a handler, save a reference to the array and add it to the form controls
boxes.Add(newbox)
Me.Controls.Add(newbox)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Addbuttons(Val(TextBox1.Text))
End Sub
End Class
I'm new to VB.NET and have been struggling all afternoon with something. I've found similar questions on the forum but none of them seemed to describe my problem exactly. I'm fairly sure that I'm missing something very basic.
I have made a main form which currently holds only one button which purpose is to open up a second form and close the main form. Based on the settings the user will select on the 2nd form the first form might have to be adapted to match with the new settings. But the problem occurs even before that.
The 'settings' form has 15 textboxes which I drew onto the form in development mode. They are called ID1, ID2,..,ID15. The values which I want to display in there are saved in an array:
Dim ids(15) as integer
Next, I created a module to simulate what you could call a control array as I used to use them in VB6.
Public sources() As TextBox = [frmSettings.ID1, frmSettings.ID2, //and so on
I did this to be able to iterate through all the 15 textboxes:
For i = 0 To 14
Sources(i).Text = ids(i + 1)
Next
Then I added on the main form this code to the Button1_Click() event:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
frmSettings.Show()
Me.Close()
End Sub
I did the same thing for the 'exit ' button on the frmSettings form.
This seems to work, but only once. I launch the application, push the button and frmSettings pops up and shows all the values from the array in the textboxes. When I push the 'close' button, I return to the main page.
So far so good, but if I try to return to frmSettings a second time, all the textboxes remain blank as if the code I added to the form never gets executed.
Any help would be greatly appreciated!
First, make sure the array that holds your data is accessible to both forms:
Module Module1
Public ids(15) As Integer
End Module
There should not be a declaration for "ids" in either form.
Next, make frmSettings itself responsible for loading and saving the data:
Public Class frmSettings
Private Sub frmSettings_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim matches() As Control
For i As Integer = 0 To 14
matches = Me.Controls.Find("ID" & (i + 1), True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is TextBox Then
Dim TB As TextBox = DirectCast(matches(0), TextBox)
TB.Text = ids(i)
End If
Next
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim valid As Boolean = True
Dim matches() As Control
For i As Integer = 0 To 14
matches = Me.Controls.Find("ID" & (i + 1), True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is TextBox Then
Dim TB As TextBox = DirectCast(matches(0), TextBox)
Dim value As Integer
If Integer.TryParse(TB.Text, value) Then
ids(i) = value
Else
MessageBox.Show(TB.Name & ": " & TB.Text, "Invalid Value", MessageBoxButtons.OK, MessageBoxIcon.Warning)
valid = False
End If
End If
Next
If valid Then
Me.Close()
End If
End Sub
End Class
I have a textbox in a vb form and I want to limit the range of characters that the user can put into the textbox to:" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890^-*().". The textbox is to insert SI Units into a database so i need consistent syntax. If the user types an invalid character into the textbox I would like the textbox to refuse to insert it, or remove it straight away, leaving the cursor in the same position within the textbox. I would also like the textbox to replace "/" with "^(-" and place the cursor before this.
I have found some code elsewhere which I have edited to do this but the code is bad, it activates on text changed within the textbox. This causes the code to fail, when the user inputs a disallowed value the code it activates itself when it tries to changes the text within the textbox.
Here is my code, the textbox starts with the contents "enter SI Units" from the form designer.
Private Sub TxtQuantityTextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtSIUnit.TextChanged
If txtSIUnit.Text = "Enter SI Units" Then
Exit Sub
End If
Dim charactersAllowed As String = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890^-*()."
Dim Text As String = txtSIUnit.Text
Dim Letter As String
Dim SelectionIndex As Integer = txtSIUnit.SelectionStart
Dim Change As Integer
Letter = txtSIUnit.Text.Substring(SelectionIndex - 1, 1)
If Letter = "/" Then
Text = Text.Replace(Letter, "^(-")
SelectionIndex = SelectionIndex - 1
End If
Letter = txtSIUnit.Text.Substring(SelectionIndex - 1, 1)
If charactersAllowed.Contains(Letter) = False Then
Text = Text.Replace(Letter, String.Empty)
Change = 1
End If
txtSIUnit.Text = Text
txtSIUnit.Select(SelectionIndex - Change, 0)
If txtQuantity.Text <> "Enter Quantity" Then
If cmbStateRateSumRatio.SelectedIndex <> -1 Then
bttAddQUAtoDatabase.Enabled = True
End If
End If
End Sub`
Thanks for you help.
Use the KeyPress event. Set e.Handled to true if you don't like the character. It's a one-liner:
Private Const AllowedChars = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890^-*()."
Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As PressEventArgs) Handles TextBox1.KeyPress
If e.KeyChar >= " "c AndAlso Not AllowedChars.Contains(e.KeyChar) Then e.Handled = True
End Sub
In the textbox's KeyDown event, check e.KeyCode. This lets you prevent certain characters from being handled. There's an example on the KeyDown documentation.