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.)
Related
I've tried searching quite a bit for this answer but haven't been able to find a good solution.
I have a datagridview on my form where users can drag and drop files onto the grid and certain columns are filled in. This works fine.
I want to be able to check if a user has a certain key pressed at the time the file is dropped. If so, I want to use that to add specific data to one of the columns in the datagrid.
Is this possible?
EDIT:
I have used keydown outside of dragdrop before but it seems that I'm missing something. The code I have is below. No matter what I do, I never get "T is pressed" but I always get "T is not pressed".
Private Sub frmReader_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
Dim files As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
If Keyboard.IsKeyDown(Keys.T) Then
MsgBox("T is pressed.")
' Put certain info into the datagridview
Else
MsgBox("T is not pressed.")
' Put other data into the datagridview
End If
End Sub
God, embarassing... I changed "Keys.T" to "Key.T" and it's working fine. Sorry for the bother.
I'm porting an Access database I wrote over to VB.NET and while I've used Access for 25 years, I've used VB.NET for 2 days so I'm unclear what the capabilities are.
In my Access form, I have a Combo Box which allows the user to select a part record from a table. Using Access I can show multiple columns to the user making a selection - PartID, Description, notes, whatever. However once the part is selected and the combo box is closed, I show only the unique part ID. The other columns help the user choose but make the form look really cluttered.
In my VB.NET form, I set the ValueMember to the PartID. But I only know how to set the DisplayMember to a single field. I created a composite field "PartID | Description | Notes" but then that's what appears even after the selection is made.
Is it possible in VB.NET to show one thing when the box is open and another thing once the selection has been made?
qry = "SELECT PartID, cast(PartID as string) & " | " & [partName] AS PartString
FROM [Part_List]
GROUP BY PartID, cast(PartID as string) & " | " & [partName]
ORDER BY PartID;"
cmd = New SqlCommand(qry, conn)
adapter = New SqlDataAdapter(cmd)
Dim tblPN As New DataTable()
adapter.Fill(tblPN)
ChoosePartNum.DataSource = tblPN
ChoosePartNum.DisplayMember = "PartString"
ChoosePartNum.ValueMember = "PartID"
ChoosePartNum.SelectedIndex = -1
I see what you are trying to do. I was doing that in MS Access too but it was more than 20 years ago.
I agree with Jimi. From a design point of view, trying to reproduce Access-like features in a new environment is not a great idea.
Users are familiar with Winforms controls and have certain expectations in term of UI layout and consistency. If you are building a new UI from scratch you should take the opportunity to redesign it and modernize it. Don't just carry 25-year old habits over. Especially if you are not the only user of that application, because you don't want people to think that your coding methods are stuck in the 20th century...
Is it possible in VB.NET to show one thing when the box is open and
another thing once the selection has been made?
If you really want to achieve this behavior you need to use combo box events. The two events you need would be DropDownClosed and DropDown.
Here is how it can be done:
Private Sub ComboBox1_DropDown(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.DropDown
With Me.ChoosePartNum
.DisplayMember = "composite field"
End With
End Sub
Private Sub ComboBox1_DropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.DropDownClosed
With Me.ChoosePartNum
.DisplayMember = "PartID"
End With
End Sub
The trick is to change the DisplayMember property whenever the combo box is folded/unfolded by the user. The underlying value remains the same.
I don't know if you require autocomplete from keyboard or have other needs. A treeview or listview sound like obvious alternatives. But you said you want a 'compact' control. Here you go.
I have the next piece of code:
Private Sub TextBox_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged, TextBox2.TextChanged,
TextBox3.TextChanged, TextBox4.TextChanged, TextBox5.TextChanged, TextBox6.TextChanged, TextBox7.TextChanged,
TextBox8.TextChanged, TextBox9.TextChanged, TextBox10.TextChanged, TextBox13.TextChanged, TextBox14.TextChanged,
TextBox15.TextChanged, TextBox18.TextChanged, TextBox19.TextChanged, TextBox20.TextChanged
Double.TryParse(TextBox1.Text, Component.Methane.Mole)
Double.TryParse(TextBox2.Text, Component.Ethane.Mole)
Double.TryParse(TextBox3.Text, Component.Propane.Mole)
Double.TryParse(TextBox4.Text, Component.iButane.Mole)
Double.TryParse(TextBox5.Text, Component.nButane.Mole)
Double.TryParse(TextBox6.Text, Component.neoPentane.Mole)
Double.TryParse(TextBox7.Text, Component.nHexane.Mole)
Double.TryParse(TextBox8.Text, Component.nHeptane.Mole)
Double.TryParse(TextBox9.Text, Component.N2.Mole)
Double.TryParse(TextBox10.Text, Component.CO2.Mole)
Double.TryParse(TextBox13.Text, Component.iPentane.Mole)
Double.TryParse(TextBox14.Text, Component.nPentane.Mole)
Double.TryParse(TextBox15.Text, Compress.Avol)
Double.TryParse(TextBox18.Text, Compress.AF)
Double.TryParse(TextBox19.Text, Compress.AP)
Double.TryParse(TextBox20.Text, Compress.AT)
End Sub
Is there any possibility to make this procedure more compact, beatiful and smarter ? Use some kind of control, loop or something else? It would be greate to make it shorter and avoid typing. I am quite new in programming, any help is very appreciated!
Thanks in advance!
If your range of textboxes should only contain numbers, create a new Control that inherits from Textbox and alter the Textbox to only accept valid numbers. See an example here:
https://www.tigraine.at/2008/10/28/decimaltextbox-for-windows-forms/
The other thing I would point you is to look into Databinding. Databinding is something where a WinForm or WPF Control will "push" the value contained within the Control back to a property on an object. Windows has made a nice and quick write up on it here:
https://msdn.microsoft.com/en-us/library/2b4be09b.aspx
Finally, for "beautiful code" give things meaningful names. Currently your textbox have the default name and this can result in some very confusing problems later on in life. A quick and easy naming convention I follow is , for example, instead of "TextBox15" you could have TextboxCompressAvol (Or, txbxCompressAvol).
I need a little advice in manipulating DataGridView in Visual Basic .Net. This is my first time playing with DataGridView. My program is to load a .txt file containing data of every room in a building and display them in the DataGridView.
Here is a portion of the file showing 2 rooms:
1;1812;1812;F18;T1;26808.16;.00;.00;.00;.00;.00;.00;
1;1813;1813;F18;T1;24000.00;3500.00;.00;300.00;.00;.00;.00
A room is a one-line string that ends with an endline character.
I have no problem with loading the file and getting all these information. I store data of each room into an object of class Room, and put them in a list.
Right now, I put all of the properties of the room into the data grid's columns like this:
Now, how can I put all these rooms' data into the grid? I tried follow http://www.dotnetperls.com/datagridview-vbnet at first, but setting DataSource to a list of class Room imitating from the given link doesn't show anything in the grid view. How can we tell the program to link rows of the grid to a list of objects? Or am I doing it wrong?
So any advise is appreciated. How do we do this?
I think you'll be pleasantly surprised at how easy this is to do with BindingSources and Visual Studio.
Create your Room Class (already done I presume)
Build your project
Set your Class as your DGV's DataSource:
Click the DGV's smart tag (or the DataSource field in the DGV's property sheet) and on the 'Choose Data Source' pulldown select 'Add Project Data Source...'
On the 'Data Source Config Wizard' select Object and click Next
Navigate your assembly's class structure to select the Class you created in step 1 and click Next
Click Finish to close the Wizard
You'll notice that a new BindingSource has been added to your project. You'll use this BindingSource to bind your List of Room objects to your DGV. Your code will look something like this after you fill your List of Rooms:
roomBindingSource.DataSource = roomList
Note that you'll start with a DataGridView with no columns. After you follow the steps above your DGV will be populated with columns based on the accessibility of the fields in the class you're binding to. At this point you can customize the appearance of the columns such as including removing/adding, etc.
Your problem is that you are using the designer to create your columns and then not linking the DataSource properties to these columns.
The link you posted is talking about automatically generating columns based upon the DataSource which is a list of objects, in that link they do not create the columns in the designer, but instead rely upon the AutoGenerateColumns property being set to true for the DataGridView.
There is a forum post here which talks about what you need to do. It is also described quite well on MSDN.
Basically you need to set the DataPropertyName for each column to match the desired property from your object.
So if your object looks like:
class Room
{
public string RoomName() {get; set;}
}
You need to set the DataPropertyName property to RoomName for the room name column in the grid designer.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Uses Test class from above.
Dim list = New List(Of Test)
list.Add(New Test("Mac", 2200))
list.Add(New Test("PC", 1100))
DataGridView1.DataSource = list
End Sub
Private Sub DataGridView1_SelectionChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles DataGridView1.SelectionChanged
' Get the current cell location.
Dim y As Integer = DataGridView1.CurrentCellAddress.Y
Dim x As Integer = DataGridView1.CurrentCellAddress.X
' Write coordinates to console.
Console.WriteLine(y.ToString + " " + x.ToString)
End Sub
End Class
So I was reading a bit on AutoComplete of textboxes in VB.NET, but I can't really understand where these are stored? Is it a fully built in feature, or do I have to write some code for it to work? I've found the AutoCompleteMode and AutoCompleteSource properties of textboxes. But I want to append whatever I've written in the textbox to the autocomplete source. Do I connect the source to My.Settings or something? Wouldn't I have to use an array as well? Any help would be appreciated :)
You would have to add new entries to your auto-complete data source manually... which makes sense, when you think about it: How is Windows Forms supposed to know when a new entry should be added to the list of suggestions and when the text entered is only something temporary?
You could add new values e.g. when validation of the input field happens, or when the user presses an OK / Apply button, or whatever fits your need best. But you will have to do this yourself.
The properties you've already discovered are the right ones.
Dim suggestions As New List(Of String)
suggestions.Add("Abba")
suggestions.Add("Nirvana")
suggestions.Add("Rolling Stones")
...
textBox.AutoCompleteSource = suggestions
You can bind AutoCompleteSource to almost anything; this is very similar to data-binding. One thing to keep in mind is that if you're adding new entries to the auto-complete data source, the UI control might not immediately notice if your data source doesn't implement the INotifyCollectionChanged interface.
first create the list to use as the custom source.
Dim MySource As New AutoCompleteStringCollection()
and then set the property of the textbox
With MyTextbox
.AutoCompleteCustomSource = MySource
.AutoCompleteMode = AutoCompleteMode.SuggestAppend
.AutoCompleteSource = AutoCompleteSource.CustomSource
End With
put this code in eventlistener you use for validating the input field, e.g. btnOK.Click
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
MySource.Add(txtinput.text)
End Sub