I have a form with combo boxes (cmbPort#) to select up to 8 serial ports. I want to first clear the item list for each, populate them with currently available system ports, and then add the option "Off" to each list. Finally, to set each combo box according to defaults saved in string spName(). I created a GroupBox (gBox1) and dragged each cmbPort onto it but I'm not sure how to reference the controls on it. I'm using VB 2015.
Can you help with VB.NET code to use loops ("For Each" or similar) to do this more efficiently?
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cmbPort1.Items.Clear()
...
cmbPort8.Items.Clear()
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort1.Items.Add(sp)
...
cmbPort8.Items.Add(sp)
Next
cmbPort1.Items.Add("Off")
...
cmbPort8.Items.Add("Off")
cmbPort1.Text = spName(1)
...
cmbPort8.Text = spName(8)
End Sub
Loops are an incredibly useful tool to master. I pitched here some code so you can get the idea, but I was working out of IDE so you might have to apply some fixes to this code to make it work as you want it to.
The main idea is that you shouldn't have to write a line more than once. If you multiply the same operation on several lines of code, you create potential problems for the future. Loops and subs are really helpful to prevent this kind of issues.
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'This is for later
Dim cmbIndex As Integer = 1
'You basically do the same operations for every Combobox in your list, son only one loop needed
For Each cmbPort As ComboBox In {cmbPort1, cmbPort2, cmbPort3 [...] cmbPort8} 'this is a way to declare an array
'Clear
cmbPort.Items.Clear()
'Add SerialPortNames
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort.Items.Add(sp)
Next
'Add "Off"
cmbPort.Items.Add("Off")
'This last one is a little bit trickier because you want to match numbers
'This is the place where you get errors when something doesn't go as planned
'If you need to keep it inside the loop here's a way to achieve that, but honestly I would't do that
'Instead I would suggest that you take this part out of the loop and do it manually
If spName(cmbIndex) IsNot Nothing Then cmbPort.Text = spName(cmbIndex)
cmbIndex += 1
Next
End Sub
You shouldn't consider efficiency into this equation, as this operation will not be called all the time, only on load. I mean: you should always do things in the best way you can, but optimization is sometimes the enemy of good, readable code.
Related
I am trying to run the code,in windows form using vb.net where I have multiple pictureboxes with properties like backgroudimage and location.
So I want to eliminate these repeated initilisations,and hence I am trying to initialise all the controls using GetType.Getproperties command. But I am getting an exception error
Private Sub Read_csv_file_itextbox_Load_1(sender As Object, e As EventArgs) Handles MyBase.Load
For Each c As Control In Me.Controls
If c.GetType().Name = "TextBox" Then
c.GetType().GetProperty("BackColor").SetValue(c, "Transparent", Nothing)
End If
Next
End Sub
And I also need to explicitly set properties for some textboxes That have naming TextBox1,TextBox2, Textbox3 so on.
And I want to access these Textbox in their even number indexing and perform my code.
The proper way to do what you're trying to do in the code you posted would be this:
For Each tb In Controls.OfType(Of TextBox)()
tb.BackColor = Color.Transparent
Next
Don't use Reflection without good reason.
Note that setting the BackColor of a TextBox to Transparent doesn't necessarily make sense, but you can use this same principle on any type of control and any property and any value.
If you want to filter any list using LINQ then you call the Where method. It's up to you to determine what the condition(s) is that you want to filter by and write the appropriate Boolean expression, e.g.
For Each tb In Controls.OfType(Of TextBox)().
Where(Function(tb) Enumerable.Range(1, 10).
Where(Function(n) n Mod 2 = 0).
Select(Function(n) $"TextBox{n}").
Contains(tb.Name))
tb.BackColor = Color.Transparent
Next
That will first generate a list of TextBoxes and then filter them on Name. It will include only those with a Name of TextBoxN where N is an even number in the range 1 to 10. This is a perfect example of determining the logic first, i.e. working out what the code needs to do, and then writing code to do that specifically.
I don't normally post on forums because I try to find information for myself, and ask as an absolute last resort. I've tried scouring the net for answers, but I'm only receiving about half of the answer I'm looking for.
I'm currently building an application that deals with state law. There's one combo box and one text box. One for the offense title, and one for the numerical code for that particular code section. So say if I select "Kidnapping", it prepopulates the text box below it with "11-5-77", for example.
The method I've been using for, oh, about the last hour now, is:
If AWOffenseTitle.Text = "Kidnapping" Then
AWCN.Text = "11-5-77"
ElseIf AWOffenseTitle.Text = "False Imprisonment" Then
AWCN.Text = "11-5-78"
With AWOffenseTitle being the combo box name, and AWCN being the text box name. While this has proved to work perfectly well so far, I'm sure you can imagine with hundreds of offense titles, this is going to take a ridiculously long time. Well, I finally found a spreadsheet with offense titles and their respective title codes. What I'm looking to do is create two text files within a folder in the local directory "Offenses". One with a vertical list of offenses, and one with a vertical list of offense code numbers that populate the same lines in each. What I'm looking to do is populate the combo box with the contents of text file one (which I can do already), but then selecting an offense title will read the second text file and display it's proper title code. That's what has me at a loss. I'm relatively well-versed with vb.NET, but I'm not an expert by any means.
I'm hoping someone here will be able to provide a code example and explain it to me line-by-line so I can gain a better understanding. I want to get more proficient with VB although it's not so popular anymore. I've been using VB since 6.0, but not on a regular basis. More on a sporadic project kind of basis.
I really appreciate any assistance anyone might be able to provide, and if you need more information, I'd be glad to answer any questions. I tried to be as thorough as I could.
Thank you in advance!
First, you need to retrieve your data. I demonstrated using an Sql Server database containing a table named Offenses with columns named OffenseTitle and OffenseCode. You will have to change this code to match your situation.
Private Function GetOffenseData() As DataTable
Dim dt As New DataTable
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("Select OffenseTitle, OffenseCode From Offenses;")
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
As the Form loads, set the properties of the ComboBox. DisplayMember matches the name of the title column and ValueMember is the name of the code column.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt = GetOffenseData()
ComboBox1.DisplayMember = "OffenseTitle"
ComboBox1.ValueMember = "OffenseCode"
ComboBox1.DataSource = dt
End Sub
Then when the selected item in the combo changes, just set the .Text property of TextBox to the SelectedValue in the combo and your code appears.
Private Sub ComboBox1_SelectionChangeCommitted(sender As Object, e As EventArgs) Handles ComboBox1.SelectionChangeCommitted
TextBox1.Text = ComboBox1.SelectedValue.ToString
End Sub
There are other ways to do this if your data source is other than a database. Please advise if you need additional help.
In addition to HardCode's comment and Mary's detailed answer, I can only add an answer that's somewhere in between them.
It might be the case, that the information is not taken from a database, but from another source, like a text/data file or a web service. So it might be useful to create an abstraction for the data source you actually use.
First, I create a class or struct that will hold the data for each combo box item.
Class Offense
Public ReadOnly Property Title As String
Public ReadOnly Property Code As String
Public Sub New(title As String, code As String)
Me.Title = title
Me.Code = code
End Sub
End Class
Next, you need a method that retrieves a list of offenses that you can bind to your combo box. It's entirely up to you how you fill/fetch the offenses list. I have simply hard coded your two values here.
Private Function GetOffenseData() As List(Of Offense)
Dim offenses As New List(Of Offense)
offenses.Add(New Offense("Kidnapping", "11-5-77"))
offenses.Add(New Offense("False Imprisonment", "11-5-78"))
Return offenses
End Function
At a certain moment (probably in your form's Load event handler), you need to initialize your combo box. Just like Mary did, I use data binding.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AWOffenseTitle.DropDownStyle = ComboBoxStyle.DropDownList
AWCN.ReadOnly = True
AWOffenseTitle.DisplayMember = NameOf(Offense.Title)
AWOffenseTitle.ValueMember = NameOf(Offense.Code)
AWOffenseTitle.DataSource = GetOffenseData()
End Sub
Note that I use the NameOf operator to get the desired property names of the Offense class. If you ever decide to rename the properties of your Offense class, you will be able to easily detect where they are used, since the compiler will complain if your code still uses the wrong property names somewhere.
Finally, the app needs to react to combo box value changes, so that the text box will show the corresponding offense code. Mary used an event handler for the SelectionChangeCommitted event, but I use a handler for the SelectedIndexChanged event instead:
Private Sub AWOffenseTitle_SelectedIndexChanged(sender As Object, e As EventArgs) Handles AWOffenseTitle.SelectedIndexChanged
AWCN.Text = AWOffenseTitle.SelectedValue
End Sub
(Up to now, I was not aware of the SelectionChangeCommitted event of the ComboBox control. I will need to look into this event to see if it is actually a better choice for this scenario, but I found that the SelectedIndexChanged event does the job just fine, so for now I sticked with that event, since I am more familiar with it.)
I am coding a zombie game in vb.net and need to make the zombies(which I have put in picture boxes and an array, there are 13) appear randomly, maybe two each time 1 zombie is killed. How can I make this in code ? I am new to coding and cannot figure it out even after numerous searching.
I think I understand what you're trying to do.
You'll want to construct a picture box in code then define where you want it to "spawn" on your form.
You can start with something like this, if you're going to have more than one zombie at a time you'll want to either make a list of them or name them uniquely so you can reference them later on (moving them/despawning/etc)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim zombie As New PictureBox
With zombie
zombie.Width = 100 'or the size you need
zombie.Height = 100 ' same thing
zombie.Top = 20 'or where you need it could be a random
zombie.Left = 20 'same as top
zombie.ImageLocation = "C:\mydocuments\zombie.png" 'change this to the location of your zombie image. if you're storing it in a resource you can call it here
End With
Me.Controls.Add(zombie)
End Sub
This should get you started at least
edit: I missed the part where you said you have an array for your zombies, but you can do an array of your picture boxes as well
I'm using VS2010 for VisualBasic, and I'm working with several similar forms. What I need to have happen is for the buttonclick on each page to cycle through the My.Resource images in order: adj_01, adj_02, adj_03,... and each form will have a different three-letter prefix.
This is what I have so far:
It might not be clear, but I'm trying to have the images cycle trough one after the other with each button click. Apparently there is an issue with either my referencing, or that the images are .png format. Simultaneously, I'm trying to have 2 separate label update information with each image change. This is what I have so far with that:
EDIT I just noticed an error that might confuse everyone on the photos: The first lines starting the If statements are checking to see if the PictureBox is empty. Needless to say, I don't know how to do that.
Here you go...
Private Sub NextAdjButton_Click(sender As Object, e As EventArgs) Handles NextAdjButton.Click
If AdjectivesPictureBox.Tag Is Nothing Then
AdjectivesPictureBox.Tag = 0
End If
Dim number As Integer = CInt(AdjectivesPictureBox.Tag)
If number < 5 Then
number = number + 1
AdjectivesPictureBox.Image = My.Resources.ResourceManager.GetObject("adj_" & number.ToString("00"))
AdjectivesPictureBox.Tag = number
End If
End Sub
I have a section of code that loops through a second DataGridView, trying to match the same index number from the main DataGridVew that the user interacts with:
Private Sub AllegationsDataGridView_CellEnter(sender As Object, e As EventArgs) Handles AllegationsDataGridView.CellEnter
Try
Dim currentcolumn As DataGridViewColumn =
AllegationsDataGridView.Columns(AllegationsDataGridView.CurrentCell.ColumnIndex)
For Each row As DataGridViewRow In parentgrid.Rows
If row.Cells.Item(0).Value = AllegationsDataGridView.CurrentRow.Cells(0).Value Then
parentgrid.CurrentCell = parentgrid(0, row.Index)
End If
Next
Catch ex As Exception
Debug.Print(ex.Message)
End Try
endsub:
End Sub
The problem is is that the datasource could potentially be thousands and thousands of entries, and I don't want this to loop through everysingle row until it finds the match. I wondered if there was a quicker way to approach this? The only examples I have seen when searching all use either a For Each Row approach, or a Loop Until approach, which would still present the same issue.
I suggest you to build a dictionary, when you initialize your datagridview with your datasource, that makes correspond the row key (AllegationsDataGridView.CurrentRow.Cells(0).Value) with the row index in the parentGrid. Use the datasource, and not the datagrid, to build this dictionary.
You will be able to access fastly to the corresponding row.
From comments: They both use the same datasource
I am not sure why a DGV the user doesnt interact with needs to have the CurrentCell kept in synch because if they are bound to the same datasource, changes to A are seen in B immediately. Nonetheless:
Private Sub dgv1_CellClick(sender As Object,
e As DataGridViewCellEventArgs) Handles dgv1.CellContentClick
If dgv2.Rows.Count = 0 Then Exit Sub
dgv2.CurrentCell = dgv2.Rows(e.RowIndex).Cells(e.ColumnIndex)
End Sub
Here, dgv1 is the user grid, dgv2 is the other ("parent" in your code?).
The For/Each iteration is not needed if they share a DS because the same data will be at each row and each column index. It will work even if the user has reordered the columns because the column index stays the same, just an internal DisplayIndex changes.
This uses the row and column indices from the DataGridViewCellEventArgs to set the CurrentCell. The Exit Sub is to account for starting up when one may have rows and not the other.
You may want to play with which event to respond to. CellContentClick seems least useful: if they click on any cell whitespace, it doesnt fire. It will also crash if the can click on a cell in the parent/dgv2 grid. CellEnter will also crash if they click on dgv2 directly.
CellClick seems to work okay, but there is the very slightest of delays before they synch up.