What is the best way to loop this program? - vb.net

This is one of the form, all the usercontrol value in this form will store in My.Settings
I have another form with a FlowLayoutPanel, everytime when the application start,
if Active checked then it will add a Button with discount value to the FlowLayoutPanel.
Should I add those usercontrol to a list and then loop through the list? Or what is the best way to solve this kind of problem?
UPDATED
How can I add multiple item to list in 1 code? I getting this error when system run to line 5
An exception of type 'System.NullReferenceException' occurred in XXX.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object.
Public Sub RefreshDiscount(ByRef ref As scr_mainDiscount)
Dim li_disName As New List(Of TextBox)
Dim li_disValue As New List(Of TextBox)
Dim li_disType As New List(Of ComboBox)
Dim li_active As New List(Of CheckBox)
Dim tb_disName As TextBox() = {ref.tb_name1, ref.tb_name2, ref.tb_name3, ref.tb_name4, ref.tb_name5, ref.tb_name6, ref.tb_name7, ref.tb_name8, ref.tb_name9, ref.tb_name10}
Dim tb_disValue As TextBox() = {ref.tb_value1, ref.tb_value2, ref.tb_value3, ref.tb_value4, ref.tb_value5, ref.tb_value6, ref.tb_value7, ref.tb_value8, ref.tb_value9, ref.tb_value10}
Dim cb_disType As ComboBox() = {ref.cb_type1, ref.cb_type2, ref.cb_type3, ref.cb_type4, ref.cb_type5, ref.cb_type6, ref.cb_type7, ref.cb_type8, ref.cb_type9, ref.cb_type10}
Dim chkb_active As CheckBox() = {ref.CheckBox1, ref.CheckBox2, ref.CheckBox3, ref.CheckBox4, ref.CheckBox5, ref.CheckBox6, ref.CheckBox7, ref.CheckBox8, ref.CheckBox9, ref.CheckBox10}
li_disName.AddRange(tb_disName)
li_disValue.AddRange(tb_disValue)
li_disType.AddRange(cb_disType)
li_active.AddRange(chkb_active)
For index As Integer = 0 To li_active.Count - 1
If li_active(index).Checked = False Then
li_disName.RemoveAt(index)
li_disValue.RemoveAt(index)
li_disType.RemoveAt(index)
li_active.RemoveAt(index)
Else
Dim btn As New ctrl_DiscountButton
With btn
.Text = li_disName(index).Text
.Price = li_disValue(index).Text
.Type = li_disType(index).Text
End With
scr_sales.flp_discount.Controls.Add(btn)
End If
Next
li_disName.Clear()
li_disValue.Clear()
li_disType.Clear()
li_active.Clear()
End Sub

Here's a simple example showing how to find CheckBox1 thru CheckBox10, "by name", using the "searchAllChildren" option of Controls.Find():
For i As Integer = 1 To 10
Dim ctlName As String = "CheckBox" & i
Dim matches() As Control = Me.Controls.Find(ctlName, True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is CheckBox Then
Dim cb As CheckBox = DirectCast(matches(0), CheckBox)
' do something with "cb"
If cb.Checked Then
' ... code ...
' possibly use code just like this to find the matching discount value control?
End If
End If
Next

Related

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.

How to check if Variable value is increaseing vb.net?

Hi I have listview and lable ,the lable is showing how many items in the listview
I want if the lable is increased will show msgbox
* may be it will increased alot such as 100 or 1 whatever
* It will execute the command just one if the value is increased
* it must be execute the msgbox any time if the value is increased
I'm sorry for my bad language
thank you
the code :
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim b As String = New WebClient().DownloadString(TextBox2.Text + "/Getinfo.php")
Dim mc As MatchCollection = Regex.Matches(b, "(\w+):")
For Each x As Match In mc
Dim infoName As String = x.Groups(1).Value
Try
Dim download As String = New WebClient().DownloadString(TextBox2.Text + "/sender/info/" & infoName & "/info.txt")
Dim f As String() = download.Split(":")
Dim ls As New ListViewItem
ls.Text = infoName
For Each _x As String In f
ls.SubItems.Add(_x)
Next
Dim hTable As Hashtable = New Hashtable()
Dim duplicateList As ArrayList = New ArrayList()
Dim itm As ListViewItem
For Each itm In ListView1.Items
If hTable.ContainsKey(itm.Text) Then 'duplicate
duplicateList.Add(itm)
Else
hTable.Add(itm.Text, String.Empty)
End If
Next
'remove duplicates
For Each itm In duplicateList
itm.Remove()
Next
ListView1.Items.Add(ls)
'here I want to excute the command if the value is increased but it's timer and the code will be execute every time
Catch
End Try
Next
End Sub
At the top of your function do
Dim oldlength = ListView1.Items.Count
At the bottom do
If oldlength < ListView1.Items.Count Then
'here I want to excute the command if the value is increased
End If
I'm pretty sure that your MessageBox isn't really what you want but a debugging example because you think if you can get the MessageBox at the right time you've solved one of your problems (which is true).

How to get the object's name in vb.net

I'm trying to dynamically adding and removing objects in my form. I'm stuck on how to get the unique identifier of which object to remove.
'Collection of controls
For Each ctl In Me.Controls
'Get control type
If TypeOf ctl Is Label Then
'Get control name/index id/text or any property of current ctl
'How do I continue from here?
'Me.Controls.Remove(ctl)
End If
Next
Thanks in advance for solutions/suggestions.
If it's alright, I would like to know the explanation of the solutions.
If you guys need to know how I added objects dynamically, here it is:
For i = 1 To Spots
Dim newLabel As New Label
Dim newLoc As Integer = iLoc + (i * 30)
With newLabel
.Name = "lblSpot" & i
.Text = "Spot " & i
.Size = New Size(100, 20)
.Location = New Point(3, newLoc)
End With
AddHandler Me.Load, AddressOf frmParking_Load
Me.Controls.Add(newLabel)
Next
You can cast ctl to Label and then use .Name to find the controls to delete
Using For Each when change collection is not recommended, so here is the code you want
Dim i = 0
While i < Me.Controls.Count
Dim c = Me.Controls(i)
If TypeOf c Is Label Then
Dim Lbl As Label = CType(c, Label)
If Lbl.Name.Contains("lblSpot") Then
Me.Controls.Remove(c)
End If
End If
End While

Mutually Exclusive Comboboxes?

So, I've got 4 Comboboxes. They all share the same list of options, lets say Apple, Banana, Carrot, Dill.
However, once the user selects one, that selection should not be available in the other comboboxes. E.g. if they pick Apple in Combobox1, then the only selections available in Combobox2, 3, and 4 should be Banana, Carrot, and Dill.
Is there a good way to do this? I originally thought to link the boxes datasource to a list containing the options, but they need separate datasources for separate choices.
Radio and checkboxes aren't really an option in the actual program, as the list of options is much larger than 4. A Combobox seems to be the best way to represent the user's available choices here.
Here's a fairly simple way to do what you are asking. This code assumes that the datasource uses an integer value to keep track of the items. If you are using the string value itself {Apple, Banana, etc.} as the id, the code will need to be amended slightly. If you need further help let me know but hopefully this gets you on the right track.
You can test this by creating a new blank form (Form1) and drop 4 combo boxes onto it (ComboBox1, ComboBox2, ComboBox3, ComboBox4). Copy/paste this code over top of the form code and run to see how it works:
Public Class Form1
Dim oComboBoxArray(3) As ComboBox
Dim bPreventSelectedChange As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Set up an array with the combo box references to reduce code size by using loops later
oComboBoxArray(0) = Me.ComboBox1
oComboBoxArray(1) = Me.ComboBox2
oComboBoxArray(2) = Me.ComboBox3
oComboBoxArray(3) = Me.ComboBox4
' Populate all four combo boxes with the same datasource to start
For Each oCbo In oComboBoxArray
PopulateDataSource(oCbo)
Next
End Sub
Private Sub PopulateDataSource(oCombo As ComboBox, Optional nIdToSelect As Integer = 0)
bPreventSelectedChange = True ' Prevent code in ComboBox_SelectedIndexChanged from executing while we change the datasource
' Using manually populated datatable as datasource because it's quick and easy to use
Dim dt As New DataTable
Dim dr As DataRow
dt.Columns.Add("ID", GetType(Int32))
dt.Columns.Add("Name", GetType(String))
' Need to have some kind of "Please select an item:" in the list or else we will be unable to clear an already selected combo box
dr = dt.NewRow
dr("ID") = 0
dr("Name") = "Select..."
dt.Rows.Add(dr)
' If you are populating from a database or other dynamic source you will only have one of these 'if' statements within a loop
If CheckSkipItem(oCombo, 1) = False Then
dr = dt.NewRow
dr("ID") = 1
dr("Name") = "Apple"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 2) = False Then
dr = dt.NewRow
dr("ID") = 2
dr("Name") = "Banana"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 3) = False Then
dr = dt.NewRow
dr("ID") = 3
dr("Name") = "Carrot"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 4) = False Then
dr = dt.NewRow
dr("ID") = 4
dr("Name") = "Dill"
dt.Rows.Add(dr)
End If
oCombo.DataSource = dt
oCombo.DisplayMember = "Name"
oCombo.ValueMember = "ID"
oCombo.SelectedValue = nIdToSelect ' Set value to either a) the "Select..." item or b) the item that was selected previously depending on the situation
bPreventSelectedChange = False ' Allow code in ComboBox_SelectedIndexChanged to be executed by user again
End Sub
Private Function CheckSkipItem(oCombo As ComboBox, nID As Integer) As Boolean
Dim bSkip As Boolean = False
' Loop through all combo boxes and see if this id has already been chosen in another combo box
For Each oCbo In oComboBoxArray
If oCbo IsNot oCombo AndAlso oCbo.SelectedValue = nID Then
' It has been chosen already so it is not valid for the current combo box that we are checking
bSkip = True
Exit For
End If
Next
Return bSkip
End Function
Private Sub ComboBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged, ComboBox2.SelectedIndexChanged, ComboBox3.SelectedIndexChanged, ComboBox4.SelectedIndexChanged
' Jump out of this event if the bPreventSelectedChange boolean is set to true (ie. if the combo box is being repopulated)
If bPreventSelectedChange = False Then
' A value was chosen by the user. Reset all other combo box datasources and remove the recently selected value
For Each oCbo As ComboBox In oComboBoxArray
If sender IsNot oCbo Then
PopulateDataSource(oCbo, If(oCbo.SelectedValue = Nothing, 0, oCbo.SelectedValue))
End If
Next
End If
End Sub
End Class

How do I associate multiple user input fields using a for/next loop?

VB.NET 2008 windows app form.
I have a groupbox with several checkboxes, comboboxes, and textboxes within it.
With the help of StackOverFlow members, I've learned how to use the FOR/NEXT loop to find all checkboxes that are checked.
Code:
Dim chk As CheckBox
Dim sb As New System.Text.StringBuilder
For Each chk In gbInterior.Controls.OfType(Of CheckBox)()
If chk.Checked Then
sb.AppendLine(chk.Text)
End If
Next chk
I am then using this information to write the checkbox names in the body of an email, using the following code:
Dim Outl As Object
Outl = CreateObject("Outlook.Application")
If Outl IsNot Nothing Then
Dim omsg As Object
omsg = Outl.CreateItem(0)
omsg.To = ""
omsg.Subject = "Cabinet Request"
omsg.Body = "A cabinet request has been created with the following information:" _
+ vbCrLf + sb.ToString
As you can see by the code sb.tostring will print every checked checkbox name on its own line...great.
How do I associate the proper combobox.selecteditem, textbox.value, and checkbox name to print out on the same line.
When I say proper...example; checkbox=pen, combobox=color, textbox=quantity.Let's say my other checkboxes are different items and the rest of the boxes are the same. Since I'm using a loop, how do I create the association? This is the result I'm looking for:
pen blue 1
pencil black 3
eraser pink 2
Thanks in advance
Final EDIT (tested and confirmed):
Public Class Form1
Private Enum interiorTypes
Rack
RackShelf
RackSlide
RackDrawer
BackPanel
Shelf
Drawer
Light
Fan
Therm
End Enum
Private Sub btnOrder_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnOrder.Click
Dim sb As New System.Text.StringBuilder
For Each type As interiorTypes In System.Enum.GetValues(GetType(interiorTypes))
Dim chk = CType(gbInterior.Controls.Find("c" & type.ToString(), True)(0), CheckBox)
Dim cb = CType(gbInterior.Controls.Find("cb" & type.ToString(), True)(0), ComboBox)
Dim tb = CType(gbInterior.Controls.Find("tb" & type.ToString(), True)(0), TextBox)
If chk.Checked Then
sb.AppendFormat("{0} {1} {2}", chk.Text, cb.SelectedItem.ToString, tb.Text)
sb.AppendLine()
End If
Next
MsgBox(sb.ToString, MsgBoxStyle.OkOnly, "Order Summary")
End Sub
End Class
If you don't have all 3 inputs for each Interior Type, then you will need to check for Nothingness. To go with your example of a missing ComboBox, after your Dim declarations inside your For loop, you could do:
If chk.Checked Then
sb.Append(chk.Text)
If cb IsNot Nothing Then sb.Append(" " & cb.SelectedItem.ToString)
sb.Append(" " & tb.Text)
sb.AppendLine()
End If
If all checkboxes, text boxes and comboboxes are part of a single parent (eg. the group box) it's nearly impossible to link them together.
I think you should look at grouping each set of items within a panel:
+- Panel 1 -----------------------------------------+
| Check box 1-------- Text Box 2 ---- Combobox 1 ---|
+---------------------------------------------------+
+- Panel 2 -----------------------------------------+
| Check box 2-------- Text Box 2 ---- Combobox 2 ---|
+---------------------------------------------------+
You can then loop through all panels in the group, and for each panel loop through all the controls within:
Dim stringbld As New System.Text.StringBuilder
Dim fullstring As String
For Each pnl As Panel In gbInterior.Controls.OfType(Of Panel)()
Dim pen As Boolean
Dim color As String = ""
Dim quantity As Integer
For Each ctrl As Control In pnl.Controls
If TypeOf (ctrl) Is CheckBox Then
Dim tmpchk As CheckBox = ctrl
pen = tmpchk.Checked
End If
If TypeOf (ctrl) Is ComboBox Then
Dim tmpchk As ComboBox = ctrl
color = tmpchk.SelectedValue
End If
If TypeOf (ctrl) Is TextBox Then
Dim tmpchk As TextBox = ctrl
If IsNumeric(tmpchk.Text) Then
quantity = CInt(tmpchk.Text)
End If
End If
Next
stringbld.AppendLine(pen & " " & color & " " & quantity)
Next
fullstring = stringbld.ToString
An alternative might be to look at using a data repeater, this has methods for getting all values:
This stackoverflow thread