vb.net dynamically create checkboxes - vb.net

I am trying to figure out how to go about creating dynamic checkboxes on my form when I do not know exactly how many boxes I will need.
The problem is that I do not know how to DIM more than one object. This is my code for creating one checkbox
Dim checkBox As New CheckBox()
Form1.Controls.Add(checkBox)
checkBox.Location = New Point(10, 10)
checkBox.Text = "testing"
checkBox.Checked = True
checkBox.Size = New Size(100, 20)
It works just fine but i am unable to add more than one checkBox without having to do this:
Dim checkBox As New CheckBox()
Dim checkBox2 As New CheckBox()
Form1.Controls.Add(checkBox)
checkBox.Location = New Point(10, 10)
checkBox.Text = "testing"
checkBox.Checked = True
checkBox.Size = New Size(100, 20)
Form1.Controls.Add(checkBox2)
checkBox2.Location = New Point(40, 10)
checkBox2.Text = "testing2"
checkBox2.Checked = True
checkBox2.Size = New Size(100, 20)
etc...
Is there a way to dim more than 1 checkbox instead of having to write multiple dim statements for each checkBoxe?
Sorry maybe i should say this..
I'm looking to do something like this:
dim checkBox() as CheckBox
do until i = 50
Form1.Controls.Add(checkBox(i))
checkBox(i).Location = New Point(10, 10)
checkBox(i).Text = "testing " & i
checkBox(i).Checked = True
checkBox(i).Size = New Size(100, 20)
i += 1
loop

It seems like the only items that are different and not calculated between the CheckBox instances is the text. If so then you could just use the following code to add a set of CheckBox instances based off of a list of String's.
Dim data as String() = New String() { "testing", "testing2" }
Dim offset = 10
For Each cur in data
Dim checkBox = new CheckBox()
Form1.Controls.Add(checkBox)
checkBox.Location = New Point(offset, 10)
checkBox.Text = cur
checkBox.Checked = True
checkBox.Size = New Size(100, 20)
offset = offset + 30
Next

Put it in a loop, including the new statement but varing the position.
You could also clone the object, maybe with performance penalties ... Sorry but don't know Vb.net, I will give you the c# code hoping it will be similar. I think this it is not the best solution for your case (a loop will do the trick), but maybe it will be for someone with a similar but more generic problem.
CheckBox CB2 = (CheckBox)CloneObject(CheckBox1);
//change the location here...
Form1.Controls.Add(checkBoxCB2 )
private object CloneObject(object o)
{
Type t = o.GetType();
PropertyInfo[] properties = t.GetProperties();
Object p = t.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, o, null);
foreach(PropertyInfo pi in properties)
{
if(pi.CanWrite)
{
pi.SetValue(p, pi.GetValue(o, null), null);
}
}
return p;
}

Related

How to dynamicallty create multiple controls at runtime

I am trying to add multiple labels to a userform at runtime
It's for the player names of a board game; and until the game starts the number of players are not known. I have managed to figure out for myself how to use the dynamic array function to create the list of players. I used a For.....Next loop to add the player names. I thought I could do that to add the labels to the form, but it only adds one. Depending on where the new control type is declared, it either adds the first player only, or the last player
This code produces one label only within the groupbox, the last player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
Dim newText As New Label
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
This one places only the first player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
Dim newText As New Label
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
I've tried this in vs 2015 and 2019 Community
Where is it going wrong?
From the looks of the code, you are correctly creating the controls but their location is the same for all of them, essentially, they are being place one of top of the other, the first is hidden with the second, which is hidden with the third.
The line
newText.Location = New Point(12 + 5, 12 + 5)
needs to be modified to place the labels at different locations.
Perhaps, something like:
newText.Location = New Point(12 + 5, 12 + (z * 25))
This will vertically align the labels with a gap of 25 between them
You are placing them all in the same location
newText.Location = New Point(12 + 5, 12 + 5)
Use your 'z' index to place them at different locations in order to be able to see them
For me it is easier to contain controls in a TableLayoutPanel then add the TLP to what ever control collection, such as a GroupBox This way you can couple a Label with TextBox, for example. Here's an example how you can create controls from a DataTable. In your case you would only need 1 ColumnStyle for labels, I just thought I would show you a good practice for future shortcuts. (I rarely use the designer to place controls)
'Start test data
Dim DtTable As New DataTable
With DtTable
Dim NewDtRow As DataRow = .NewRow
For i As Integer = 0 To 25
Dim DtCol As New DataColumn With {.ColumnName = "Col" & i, .DataType = GetType(String)}
.Columns.Add(DtCol)
NewDtRow(DtCol.ColumnName) = "Test" & i
Next
.Rows.Add(NewDtRow)
End With
'End test data
Dim TLP1 As New TableLayoutPanel With {.Name = "TlpFields"}
With TLP1
.BorderStyle = BorderStyle.Fixed3D
.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
.AutoScroll = True
.AutoSize = True
.RowStyles.Clear()
.ColumnStyles.Clear()
.Dock = DockStyle.Fill
.ColumnCount = 2
.ColumnStyles.Add(New ColumnStyle With {.SizeType = SizeType.AutoSize})
End With
For Each DtCol As DataColumn In DtTable.Columns
With TLP1
.RowCount += 1
.RowStyles.Add(New RowStyle With {.SizeType = SizeType.AutoSize})
'create labels
.Controls.Add(New Label With {
.Text = DtCol.ColumnName,
.Anchor = AnchorStyles.Right}, 0, .RowCount)
'create textboxs
Dim TxtBox As New TextBox With {
.Name = "TextBox" & DtCol.ColumnName,
.Size = New Size(170, 20),
.Anchor = AnchorStyles.Left}
'add binding
TxtBox.DataBindings.Add("Text", DtTable, DtCol.ColumnName)
.Controls.Add(TxtBox, 1, .RowCount)
End With
Next
Controls.Add(TLP1)

Read data from dynamically created text-box

I'm trying to collect data after creating dynamic text-box with vb.net
Private Sub btn_OK_lines_number_Click(sender As Object, e As EventArgs)
Handles btn_OK_lines_number.Click
Dim i As Integer
Dim x As Integer
Dim Z As Integer
Z = 150
If IsNumeric(txt_lines_number.Text) Then
Int32.TryParse(txt_lines_number.Text, x)
For i = 1 To x
Dim newTB As New TextBox
Dim newLB As New Label
newLB.Name = "lbl_workstation_number_line" & i
newLB.Text = "Nbr Work Station in Line" & i
newLB.Size = New Size(190, 20)
newLB.ForeColor = Color.White
newLB.Font = New Font("consolas", 12, FontStyle.Regular, GraphicsUnit.Pixel)
newLB.Location = New Point(20, Z + i * 30)
newTB.Name = "Textbox" & i
newTB.Size = New Size(170, 20)
newTB.Location = New Point(200, Z + i * 30)
Me.Controls.Add(newTB)
Me.Controls.Add(newLB)
Next
i = i + 1
Else
MessageBox.Show("please enter a number")
txt_lines_number.Text = ""
End If
End Sub
Let's say you just have one row, and only create one TextBox. You set the name here:
newTB.Name = "Textbox" & i
where the resulting TextBox is named Textbox1. The problem is you can't just reference the identifier Textbox1 directly in your code, as you do with txt_lines_number. You can't even reference it as a member of the class (Me.Textbox1). This name didn't exist at compile time, and so it's not an identifier you can use, and it's not a member of the class at all. There was never a matching Dim statement for that name.
What you can do, though, is look again in the Controls collection where you added the TextBox to the form:
Me.Controls("Textbox1")
or
Me.Controls("Textbox1").Text
You may also need to cast the value to a TextBox:
Dim box As TextBox = DirectCast(Me.Controls("Textbox1"), TextBox)
MessageBox.Show(box.Text)
Remember that case matters here.
Further saving this in a DB is out of scope for one question. There are as many ways to do that as there are programmers in the world. You should make your own attempt first, and come back here with a new question when you run into specific problems.
Thank you,
this is my attempt and it is done !
Dim userInput As TextBox = Form1.Controls.Item("TextBox" & i.ToString)
mycommand.Parameters.AddWithValue("#workstation", userInput.Text)
:D
Because you creating dynamic amount of input controls, right tool for the job will be DataGridView control.
Create a class to represent your data
Public Class LineInfo
Public Property Number As Integer
Public Property WorkStationNumber As Integer
End Class
Create `DataGridView in the form designer.
Private Sub btn_OK_lines_number_Click(sender As Object, e As EventArgs) Handles btn_OK_lines_number.Click
Dim linesAmount As Integer
If Integer.TryParse(txt_lines_number.Text, linesAmount) = False Then
MessageBox.Show("please enter a number")
txt_lines_number.Text = ""
Exit Sub
End If
' Create class instance for every line
Dim lines =
Enumerable.Range(1, linesAmount)
.Select(Function(i) New LineInfo With { .Number = i })
.ToList()
'Set lines as DataSource to the DataGridView
Me.DataGridView1.DataSource = lines
End Sub
DataGridView will display all lines and provide input fields to update work station numbers.
You can access updated lines later by casting DataSource back to the List
Dim lines = DirectCast(Me.DataGridView1.DataSource, List(Of LineInfo))
' Now you can access all data and save it to the database
Dim parameters =
lines.Select(Function(line)
Return new SqlParameter With
{
.ParameterName = $"#workstation{line.Number}",
.SqlDbType = SqlDbType.Int,
.Value = line.WorkStationNumber
}
End Function)
.ToList()
myCommand.Parameters.AddRange(parameters)
You can freely change style, font colors of different columns in the datagridview.

picturebox array does not output images?

I have a picture box array set up
Dim i = 7
Dim s As PictureBox() = New PictureBox(0) {}
s(0) = New PictureBox()
s(0).Image = Image.FromFile("C:\Pic Folder\pic.png")
s(0).Location = New Point(10, 10)
s(0).Size = New Size(50, 50)
however, no image shows up at all. before, when i had declared a new picturebox and put the specifications as the new picturebox (say, foo.Image as opposed to s(0)) it worked fine

Generate 1 Letter in all Labels

Good Morning
I am creating an application that has 100 Labels from Label1 to Label100. My Target here is that all of that Labels must generate random letters in the alphabet no matter if it is repeated as long as its different.
Here is my code I tried.
Dim validchars As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim sb As New StringBuilder()
Dim rand As New Random()
For i As Integer = 1 To 1
Dim idx As Integer = rand.Next(0, validchars.Length)
Dim randomChar As Char = validchars(idx)
sb.Append(randomChar)
Next i
Label1.Text = sb.ToString()
Label2.Text = sb.ToString()
Label3.Text = sb.ToString()
Label4.Text = sb.ToString()
Label5.Text = sb.ToString()
Label6.Text = sb.ToString()
Label7.Text = sb.ToString()
Label8.Text = sb.ToString()
'and so on until i reached Label100
But my output is this :(
Please ignore the other letters because i tried to code until Label50
How can i achieve it? and is there other way to shorten up calling each label?
TYSM for future help
Is this what you're going for (c#)? Basically, creating the Labels on the fly, and adding them to a FlowLayoutPanel for stacking and positioning.
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
for (int i = 0; i < 100; i++)
{
Label label = new Label();
label.Text = alphabet[random.Next(0, alphabet.Length)].ToString();
flowLayoutPanel1.Controls.Add(label);
}
VB.NET:
Dim alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim random As New Random()
For i As Integer = 0 To 99
Dim label As New Label()
label.Text = alphabet(random.[Next](0, alphabet.Length)).ToString()
flowLayoutPanel1.Controls.Add(label)
Next
Option 1, with your existing form:
I would have created the labels in the code and not in the designer, but if you already have them, you can do this:
private void Form1_Load(object sender, EventArgs e)
{
var labels = this.Controls.OfType<Label>();
var rnd = new Random();
foreach (var label in labels)
{
label.Text = ((char)(rnd.Next(26) + 'A')).ToString();
}
}
Simply loop through all the form's labels (you may filter them if necessary) and assign each one a random letter. And that's all. No need to use StringBuilders or an array with the letters.
I also used Convertor to turn it into VB, I hope it works:
Private Sub Form1_Load(sender As Object, e As EventArgs)
Dim labels = Me.Controls.OfType(Of Label)()
Dim rnd = New Random()
For Each label As var In labels
label.Text = CChar(rnd.[Next](26) + "A"C).ToString()
Next
End Sub
Option 2, starting from scratch:
Finally, based on mariocatch's answer, which uses a FlowLayoutPanel, I suggest you do this:
Start with an empty form.
Add a Panel.
Set its Dock property to Bottom.
Add a Button inside.
Go to its Anchor property and deselect Top and Left (nothing selected).
Set the panel's height and center the button horizontally.
Add a FlowLayoutPanel in the middle of the form.
Set its Dock property to Fill.
And nothing else there. Then use this code:
Private Sub Form1_Load(sender As Object, e As EventArgs)
Dim rnd = New Random()
For i As Integer = 0 To 49
Dim label = New Label()
label.Width = 20
label.Text = CChar(rnd.[Next](26) + "A"C).ToString()
Me.flowLayoutPanel1.Controls.Add(label)
Next
End Sub
After this I think you can adjust all the details without any issue.
Here is the VB.NET version of the code provided by #mariocatch. I like it.
Dim alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim random As New Random()
For i As Integer = 0 To 99
Dim label As New Label()
label.Text = alphabet(random.Next(0, alphabet.Length)).ToString()
flowLayoutPanel1.Controls.Add(label)
Next
This is the Convertor used online for converting C# code to VB.NET code for reference.
Assuming labels up to 50 are the ones with the "F"s:
You've determined the value of sb once you left the loop. You're then taking that value and setting that to the text of each label, which is why it wouldn't work. You've also created a loop starting at 1 and ending at 1, which is a pretty pointless loop as it only runs once. If you modify the loop to generate 100 characters in the stringbuilder and then set the nth label to the nth character of the stringbuilder, this should work.

How to get the value of a textbox from sa MDI form Groupbox?

I'm new to vb.net and always searching for solution. My forms involved are MainMenu, poCustom, and rdlcForm. All are working well until I got a new look with MDI Forms.
My "New" MainMenu are now containing the poCustom into a Groupbox. Which I searched the code as
For Each f As Form In Application.OpenForms
If TypeOf f Is poCustom Then
f.Activate()
Return
End If
Next
Dim ch As New poCustom
ch.TopLevel = False
ch.Visible = True
ch.StartPosition = FormStartPosition.Manual
Dim leftStart As Integer = 1220 - (ch.Width + (SystemInformation.Border3DSize.Width * 2))
Dim topStart As Integer = 670 - (ch.Height + (SystemInformation.Border3DSize.Height * 2))
ch.Location = New Point(leftStart, topStart)
GroupBox1.Controls.Add(ch)
Problem: The rdlcForm(report) cannot get the value of textboxes in poCustom form. Code below:
Private Sub rptPOView2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim rptParam1(11) As Microsoft.Reporting.WinForms.ReportParameter
rptParam1(0) = New Microsoft.Reporting.WinForms.ReportParameter("rptDate", poCustom.Label1.Text)
rptParam1(1) = New Microsoft.Reporting.WinForms.ReportParameter("rptREF", poCustom.Label5.Text)
rptParam1(2) = New Microsoft.Reporting.WinForms.ReportParameter("rptCompany", poCustom.CompanyName.Text)
rptParam1(3) = New Microsoft.Reporting.WinForms.ReportParameter("rptQTY", poCustom.txBoxQTY.Text)
rptParam1(4) = New Microsoft.Reporting.WinForms.ReportParameter("rptUOM", poCustom.txBoxUOM.Text)
rptParam1(5) = New Microsoft.Reporting.WinForms.ReportParameter("rptDesciption", poCustom.txBoxDesc.Text)
rptParam1(6) = New Microsoft.Reporting.WinForms.ReportParameter("rptUnit", poCustom.txBoxUnit.Text)
rptParam1(7) = New Microsoft.Reporting.WinForms.ReportParameter("rptTotal", poCustom.txBoxTotal.Text)
rptParam1(8) = New Microsoft.Reporting.WinForms.ReportParameter("rptSubTotal", poCustom.Label25.Text)
rptParam1(9) = New Microsoft.Reporting.WinForms.ReportParameter("rptVAT", poCustom.Label26.Text)
rptParam1(10) = New Microsoft.Reporting.WinForms.ReportParameter("rptTotalAmount", poCustom.Label27.Text)
rptParam1(11) = New Microsoft.Reporting.WinForms.ReportParameter("rptRequest", poCustom.Label30.Text)
ReportViewer1.LocalReport.SetParameters(rptParam1)
Me.ReportViewer1.RefreshReport()
TextBox1.Text = poCustom.CompanyName.Text
End Sub
Which happens to work without using MDI Forms. I would like to know the cause of the problem for future use. Thank you in advance!
You are using two different instances of poCustom. ch is the first, and you populate its textboxes. But on the rptPOView2_Load event, you are using the other instance, which contains textboxes that has no value yet. One way to fix the problem is to use poCustom itself and not ch.
poCustom.TopLevel = False
poCustom.Visible = True
poCustom.StartPosition = FormStartPosition.Manual
Dim leftStart As Integer = 1220 - (ch.Width + (SystemInformation.Border3DSize.Width * 2))
Dim topStart As Integer = 670 - (ch.Height + (SystemInformation.Border3DSize.Height * 2))
poCustom.Location = New Point(leftStart, topStart)
GroupBox1.Controls.Add(poCustom)