I 'm currently creating a form in VB that uses a text file to gather information for a combo box then automatic use the next word in the text file that's comma delimited to automatically populate a textbox.
But the current code I wrote show the list for the combo box but always auto fills the textbox with the second word on the second line and will not change the textbox after i select another option from the combo box can someone help?
Sorry if this isn't very clear.
My Text file is in this format:
Robert,5 BellView Road
Martin,6 BellView Road
Ect....
My code is as follows:
Dim LineString As String
Dim FieldString As String()
Try
Dim ContactInfoStreamReader As StreamReader = New StreamReader("C:\temp\test1.txt")
Do Until ContactInfoStreamReader.Peek = -1
LineString = ContactInfoStreamReader.ReadLine()
FieldString = LineString.Split(CChar(","))
LineString = FieldString(0)
ComboBox1.Items.Add(LineString)
Loop
RichTextBox2.Text = FieldString(1)
ContactInfoStreamReader.Close()
Catch ex As Exception
MsgBox("""Customers Name & Address.txt"" file was not found")
End Try
Ok, I know you have provided code, however this is how I would do it.
First of all we make a Person Class and create a Person List to store each Person
After that we read / load the text file into a string array and then we use Linq to split the text files line where the comma is and have it sort the name and address into two keys one being NAME and the other being ADDRESS. Once that is done we loop through each of the items and create a new Person with the values value.NAME and Value.Address for the persons name and address as well as add the person's name into a comboboxand finally we add a ComboBox Event that changes the Textbox's text to the selected ComboBox's Index that matches the List of People that was created to store each Person
Public Class Form1
Dim People As New List(Of Person)
Private Sub ComboBox1_SelectedValueChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedValueChanged
Try
TextBox1.Text = People(ComboBox1.SelectedIndex).address
Catch
''this will prevent errors if there is a name with no address
TextBox1.Text = ""
End Try
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
''This is the location I used, feel free to change it here.
Dim location As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim fileName As String = "test1.txt"
Dim path As String = System.IO.Path.Combine(location, fileName)
Dim query = From line In System.IO.File.ReadAllLines(path)
Let val = line.Split(",")
Select New With {Key .NAME = val(0), Key .ADDRESS = val(1)}
For Each value In query
Dim p = New Person(value.NAME, value.ADDRESS)
People.Add(p)
ComboBox1.Items.Add(value.NAME)
Next
''ADD OTHER COMBOBOX ITEMS HERE IF NEED BE, SO EVERYTHING IS IN ORDER
ComboBox1.Items.Add("ANDREW")
End Sub
End Class
Class Person
Public Property name() As String
Public Property address() As String
Public Sub New(name As String, address As String)
Me.name = name
Me.address = address
End Sub
End Class
EDIT:This is how I formatted the text file.Robert,5 BellView Road
Martin,6 BellView Road
Related
I have a form with a button and a label. I also have a text file with the following contents:
Bob:Available:None:0
Jack:Available:None:0
Harry:Available:None:0
Becky:Unavailable:Injured:8
Michael:Available:None:0
Steve:Available:None:0
Annie:Unavailable:Injured:12
Riley:Available:None:0
The values in the text file are:
person-name:available-or-unavailable:sick-or-injured:months-they-will-be-unavailable
What I would like to do is to have the user click the button and a random (available) person will be selected from the text file. The label's text will then say:
personname & " has gotten injured and will be unavailable for 10 months."
I would then like to overwrite the text file with the corresponding values for that particular person. For example that person's second value will now be "Unavailable", the third value will be "Injured" and the fourth value will be 10.
I hope this makes sense.
I don't have any code, as I literally have no idea how to do this. Any help would be much appreciated!
Explanations and code in line.
Private r As New Random
Private RandomIndex As Integer
Private dt As New DataTable
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddColumnsToDataTable()
FillDataTable()
End Sub
Private Sub AddColumnsToDataTable()
'Need to prepare the table to receive the data
dt.Columns.Add("Name")
dt.Columns.Add("Available")
dt.Columns.Add("Injury")
dt.Columns.Add("Months")
End Sub
Private Sub FillDataTable()
'ReadAllLines returns an array of lines in the text file
Dim lines = File.ReadAllLines("workers.txt")
'Loop through each line in the lines array
For Each line As String In lines
'.Split returns an array based on splitting the line
'by the colon. The c following ":" tells the compiler
'that this is a Char which the split function requires.
Dim items = line.Split(":"c)
'We can add a row to the data table all at once by
'passing in an array of objects.
'This consists of the elements of the items array
dt.Rows.Add(New Object() {items(0), items(1), items(2), items(3)})
Next
'Now we have an in memory DataTable that contains all the data from the text file.
End Sub
Private Function GetRandomWorker() As String
'A list of all the row indexes that are available
Dim AvailableList As New List(Of Integer)
For i As Integer = 0 To dt.Rows.Count - 1
'Loop through all the data in the date table row by row
If dt.Rows(i)("Available").ToString = "Available" Then
'Add only the row indexes that are Available
AvailableList.Add(i)
End If
Next
'Get a random index to use on the list of row indexes in IndexList
If AvailableList.Count = 0 Then
'No more workers left that are Available
Return ""
End If
'Get a random number to use as an index for the available list
Dim IndexForList = r.Next(AvailableList.Count)
'Selects a row index based on the random index in the list of Available
RandomIndex = AvailableList(IndexForList)
'Now use the index to get information from the row in the data table
Dim strName = dt.Rows(RandomIndex)("Name").ToString
Return strName
End Function
Private Sub SaveDataTable()
'Resave the whole file if this was a real app you would use a database
Dim sb As New StringBuilder
'A string builder keeps the code from creating lots of new strings
'Strings are immutable (can't be changed) so every time you think you are
'changing a string, you are actually creating a new one.
'The string builder is mutable (changable)
For Each row As DataRow In dt.Rows
'The ItemsArray returns an array of objects containing all the
'values in each column of the data table.
Dim rowValues = row.ItemArray
'This is a bit of Linq magic that turns the values into strings
Dim strRowValues = From s In rowValues
Select DirectCast(s, String)
'Now that we have strings we can use the String.Join with the colon
'to get the format of the text file
sb.AppendLine(String.Join(":", strRowValues))
Next
'Finally we change the StringBuilder to a real String
'The workers.txt is stored in the Bin\Debug directory so it is current directory
'no additional path required
File.WriteAllText("workers.txt", sb.ToString)
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
SaveDataTable()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim WorkerName As String = GetRandomWorker()
If WorkerName = "" Then
MessageBox.Show("There are no available workers")
Return
End If
Label1.Text = $"{WorkerName} has gotten injured and will be unavailable for 10 months."
dt.Rows(RandomIndex)("Available") = "Unavailable"
dt.Rows(RandomIndex)("Injury") = "Injured"
dt.Rows(RandomIndex)("Months") = "10"
End Sub
I have a form with a button and a textbox. I also have a text file with the following contents..
Bob:Available:None:0
Jack:Available:None:0
Harry:Available:None:0
Becky:Unavailable:Injured:8
Michael:Available:None:0
Steve:Available:None:0
Annie:Unavailable:Injured:8
Riley:Available:None:0
When the user loads the form each value of the text file gets stored into an array. This works fine. What i would like to happen is when the button is pressed a random person (name) who has the value 'Available' will be retrieved from the array and displayed in the textbox.
The code i have so far (which stores each item in text file into arrays):
Public Class Form1
'define profile of person
Public Structure PersonInfo
Public name As String
Public status As String
Public status_type As String
Public monthsunavailable As Integer
End Structure
'Profile List of persons
Public Shared personInfos As New List(Of PersonInfo)() 'roster data
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'read all infomations of person from file. lines is profile array
Dim lines() As String = IO.File.ReadAllLines(filelocation)
For Each line In lines
'Parses the line string, make Person Info and add it to Person List
'split string with ":"
If line.Trim.Equals("") Then Continue For
Dim strArr() = line.Split(":")
'make Person Info
Dim pi As New PersonInfo()
pi.name = strArr(0)
pi.status = strArr(1)
pi.status_type = strArr(2)
pi.monthsunavailable = strArr(3)
'add Person Info to Person List
personInfos.Add(pi)
Next
How do i select a random name from the array and display it in a textbox?
You can use something like this. Try reading the docs!
Private Sub Button1_click (sender As Object, e As EventArgs) Handles Button1.Click
Dim r As New Random ()
Textbox1.Text = personinfos(r.Next (0,personinfos.count)).name
End Sub
Update
To select only names with status "Available"
Private Sub Button1_click (sender As Object, e As EventArgs) Handles Button1.Click
'Instantiate a new random variable
Dim r As New Random ()
'This is a LINQ query to select all items from the list where a property of the item
'(in this case , status) is equal to something.
'Try changing Available to something else and see what you get
Dim qr = From pi in personinfos
Where pi.status = "Available"
Select pi
'The following line can be simplified as follows
'Dim i As Integer = r.Next (0,qr.count)
'Dim s As String = qr (i).name
'Textbox1.Text = s
Textbox1.Text = qr(r.Next (0,qr.count)).name
End Sub
This code is intended to delete the actual files from the system when it is selected from the system:
Dim file As String()
file = System.IO.Directory.GetFiles("C:\Users\User\Desktop", "lalala.txt", IO.SearchOption.AllDirectories)
If ListBox1.SelectedIndex = -1 Then
MsgBox("No files selected")
Else
System.IO.File.Delete(ListBox1.Items(ListBox1.SelectedIndex).ToString())
ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
End If
However, only the items in the listbox are deleted. The actual file still exists. I am unsure where I should put the file into the Delete function.
I have referred to this but it has not helped me.
________UPDATE________
I have discovered where it went wrong: it is because only the file name is added to the listbox:
ListBox1.Items.Add(Path.GetFileName(fileFound))
Instead of Path.GetFullPath.
Anyhow, can I delete the file with GetFileName only?
The problem, as you realised, is that the filename only is not enough information to delete a file. You need the whole path to the file as well. So you need some way of storing the whole path but only showing the filename. This is also important because there might be two (or more) files with same name in separate directories.
A ListBox can have its Datasource property set to show items from "an object that implements the IList or IListSource interfaces, such as a DataSet or an Array."
Then you set the DisplayMember and ValueMember properties to tell it what to display and what to give as the value.
For example, I made up a class named "FileItem" which has properties for the full filename and for whatever you want to display it as, filled a list with instances of "FileItem", and told ListBox1 to display it:
Imports System.IO
Public Class Form1
Class FileItem
Property FullName As String
Property DisplayedName As String
Public Sub New(filename As String)
Me.FullName = filename
Me.DisplayedName = Path.GetFileNameWithoutExtension(filename)
End Sub
End Class
Private Sub PopulateDeletionList(dir As String, filter As String)
Dim files = Directory.EnumerateFiles(dir, filter, SearchOption.AllDirectories)
Dim fileNames = files.Select(Function(s) New FileItem(s)).ToList()
Dim bs As New BindingSource With {.DataSource = fileNames}
ListBox1.DataSource = bs
ListBox1.DisplayMember = "DisplayedName"
ListBox1.ValueMember = "FullName"
End Sub
Private Sub ListBox1_Click(sender As Object, e As EventArgs) Handles ListBox1.Click
Dim lb = DirectCast(sender, ListBox)
Dim sel = lb.SelectedIndex
If sel >= 0 Then
Dim fileToDelete = CStr(lb.SelectedValue)
Dim choice = MessageBox.Show("Do you really want to delete " & fileToDelete, "Confirm file delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If choice = DialogResult.Yes Then
Try
File.Delete(fileToDelete)
lb.DataSource.RemoveAt(sel)
Catch ex As Exception
MessageBox.Show("Could not delete " & fileToDelete & " because " & ex.Message)
End Try
End If
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PopulateDeletionList("C:\temp", "*.txt")
End Sub
End Class
Edited I had forgotten to delete the item from the ListBox. To do that, it needs to be tied to the DataSource through a BindingSource.
Extra feature Seeing as there could be more than one file with the same name, you might want to add a tooltip to the listbox items so that you can see which directory it is in. See how to add tooltips on winform list box items for an implementation which needs only minor adjustments to work, such as:
Dim toolTip As ToolTip = New ToolTip()
' ...
Private Sub ListBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles ListBox1.MouseMove
Dim lb = DirectCast(sender, ListBox)
Dim index As Integer = lb.IndexFromPoint(e.Location)
If (index >= 0 AndAlso index < ListBox1.Items.Count) Then
Dim desiredTooltip = DirectCast(lb.Items(index), FileItem).FullName
If (toolTip.GetToolTip(lb) <> desiredTooltip) Then
toolTip.SetToolTip(lb, desiredTooltip)
End If
End If
End Sub
The most simple (and reliable) solution would be to create a custom data type and add that to the ListBox instead.
By overriding the ToString() method you can make it display only the file name, while the back-end object still contains the full path.
Public Structure FileEntry
Public FullPath As String 'A variable holding the full path to the file.
'Overriding the ToString() method, making it only return the file name.
Public Overrides Function ToString() As String
Return System.IO.Path.GetFileName(Me.FullPath)
End Function
Public Sub New(ByVal Path As String)
Me.FullPath = Path
End Sub
End Structure
Now whenever you want to add paths to the ListBox you've got to add a new instance of the FileEntry structure, instead of a regular string:
ListBox1.Items.Add(New FileEntry(fileFound))
And to delete you just cast the currently selected item into a FileEntry, and then pass its FullPath onto the File.Delete() method.
Dim Entry As FileEntry = DirectCast(ListBox1.Items(ListBox1.SelectedIndex), FileEntry)
System.IO.File.Delete(Entry.FullPath)
NOTE: For this to work every item in the list box must be a FileEntry.
Online test: https://dotnetfiddle.net/x2FuV3 (pardon the formatting, DotNetFiddle doesn't work very well on a cellphone)
Documentation:
How to: Declare a Structure (Visual Basic) - Microsoft Docs
Overriding the Object.ToString() method - MSDN
You can use Path.Combine.
Since you are going to search in C:\Users\User\Desktop, you can do this to delete:
System.IO.File.Delete(Path.COmbine("C:\Users\User\Desktop",ListBox1.Items(ListBox1.SelectedIndex).ToString())
Here, "C:\Users\User\Desktop" and the selected index's text will be combined to make a single path.
Edit:
I get it, you want to show the file name onlyy in the textbox but want to delete the file from the system too but can't do it, right?
Well you can do this:
Put two listbox and while you add a file to a listbox1, put it's path to the listbox2 whose visibility will be False, meaning it won't be shown in the runtime.
DOing this, while an item is selected in the listbox1, use the path.combine to make a path by adding the filename & path from the list with same index number.
Something like this:
System.IO.File.Delete(path.combine(ListBox1.Items(ListBox1.SelectedIndex).ToString(), ListBox2.Items(ListBox1.SelectedIndex).ToString())
What I want to do is import a CSV file (called fwlist.txt), that looks like this:
modelname,power type,pic,part number,firmware, option1, option 1, etc
End result, i would like a combobox that shows the modelname, and when the model name is selected from the pulldown, it updates various labels and text boxes on the form with other information.
Here's what I have so far:
Dim filename As String = "fwlist.txt"
Dim pwrtype As String
Dim pic As String
Dim partnum As String
Dim lineread As String
Dim FirmwareName As String
Private Sub ReadFirmwaresLoad(sender As Object, e As EventArgs) Handles Me.Load
' Load the items into the NameComboBox list.
Dim ResponseDialogResult As DialogResult
Try
Dim FirmwareStreamReader As StreamReader = New StreamReader(filename)
' Read all the elements into the list.
Do Until FirmwareStreamReader.Peek = -1
lineread = FirmwareStreamReader.ReadLine()
Dim fields As String() = lineread.Split(",")
FirmwareName = fields(0) 'Take First Field
cbFW.Items.Add(FirmwareName)
'Set Text labels based on position in line.
pwrtype = fields(1)
pic = fields(2)
partnum = fields(3)
(...etc through options)
Loop
' Close the file.
FirmwareStreamReader.Close()
Catch ex As Exception
' File missing.
ResponseDialogResult = MessageBox.Show("File not Found!", "File Not Found",
MessageBoxButtons.OK, MessageBoxIcon.Question)
If ResponseDialogResult = DialogResult.OK Then
' Exit the program.
Me.Close()
End If
End Try
End Sub
Private Sub cbFW_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbFW.SelectedIndexChanged
lblPwrType.Text = pwrtype
lblPic.Text = pic
lblPartNum.Text = parnum
....etc thruogh options
End Sub
This code works, but only sort of. If i select anything from the combo box, it only gives me the information from the very last line of the CSV file - even if its the first entry in the box. I'm pretty sure it's something simple that I'm messing up.. anyone help?
First we read the lines into Firmware objects then we set this List(Of Firmware) as the DataSource of the ComboBox.
Then we handle the SelectedIndexChanged event of the ComboBox which will get the currently selected firmware and loads its data to the TextBox controls.
This is a tested, working example below:
Public Class Firmware
Public Property ModelName As String
Public Property PowerType As String
Public Property Pic As String
Public Property PartNumber As String
Public Property Firmware As String
Public Property Option1 As String
End Class
Public Class MainForm
Private Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
Dim lines As String() = Nothing
Try
' Read the file in one step
lines = File.ReadAllLines("fwlist.txt")
Catch ex As Exception
Dim dialogResult As DialogResult = MessageBox.Show(Me, "File not found! Would you like to exit program?", "Error reading file", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2)
If dialogResult = DialogResult.Yes Then
Me.Close()
End If
Exit Sub
End Try
Dim firmwares As List(Of Firmware) = New List(Of Firmware)
For Each line As String In lines
Dim rawData As String() = line.Split({","}, StringSplitOptions.None)
' Create Firmware object from each line
Dim firmware As Firmware = New Firmware() With
{
.ModelName = rawData(0),
.PowerType = rawData(1),
.Pic = rawData(2),
.PartNumber = rawData(3),
.Firmware = rawData(4),
.Option1 = rawData(5)
}
' We store the read firmwares into a list
firmwares.Add(firmware)
Next
' Set the list as the data source of the combobox
' DisplayMember indicates which property will be shown in the combobox
With cboModelNames
.DataSource = firmwares
.DisplayMember = "ModelName"
End With
End Sub
Private Sub cboModelNames_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboModelNames.SelectedIndexChanged
RefreshForm()
End Sub
Private Sub RefreshForm()
' Get the selected item as Firmware object
Dim currentFirmware As Firmware = DirectCast(cboModelNames.SelectedItem, Firmware)
' Refresh all the textboxes with the information
With currentFirmware
txtPowerType.Text = .PowerType
txtPic.Text = .Pic
txtPartNumber.Text = .PartNumber
txtFirmware.Text = .Firmware
txtOption1.Text = .Option1
End With
End Sub
End Class
Every time you set the value of a variable like pwrtype, its old value is thrown away and there is no way to get it back. And even if you did save multiple values for each variable, you would need a way to figure out which value goes with the currently selected item in the combo box. To resolve these two issues you need:
a way to group values from the same line together
a way to save all values read from the file, not just the most recent ones
Let's start with the first issue, grouping values together. In the example code below, I created a Structure type called FirmwareInfo to serve that purpose. Each FirmwareInfo has its own pwrtype, pic, etc.
The other piece of the puzzle that you are missing is a way to save more than one Firmware. The easiest way to do this is to add each FirmwareInfo to the combo box's item list, instead of adding just the display string. The combo box will convert each item to a string in the UI; the ToString() method tells it how you want this conversion to be done.
I haven't run this example code so I can't guarantee there aren't mistakes in there, but hopefully it's enough to show the general idea.
Dim filename As String = "fwlist.txt"
Structure FirmwareInfo
Public pwrtype As String
Public pic As String
Public partnum As String
Public FirmwareName As String
Public Overrides Function ToString() As String
Return FirmwareName
End Function
End Structure
Private Sub ReadFirmwaresLoad(sender As Object, e As EventArgs) Handles Me.Load
' Load the items into the NameComboBox list.
Dim ResponseDialogResult As DialogResult
Try
Dim FirmwareStreamReader As StreamReader = New StreamReader(filename)
' Read all the elements into the list.
Do Until FirmwareStreamReader.Peek = -1
Dim lineread = FirmwareStreamReader.ReadLine()
Dim fields As String() = lineread.Split(",")
Dim info As New FirmwareInfo With {
.FirmwareName = fields(0),
.pwrtype = fields(1),
.pic = fields(2),
.partnum = fields(3)
}
cbFW.Items.Add(info)
Loop
' Close the file.
FirmwareStreamReader.Close()
Catch ex As Exception
' File missing.
ResponseDialogResult = MessageBox.Show("File not Found!", "File Not Found",
MessageBoxButtons.OK, MessageBoxIcon.Question)
If ResponseDialogResult = DialogResult.OK Then
' Exit the program.
Me.Close()
End If
End Try
End Sub
Private Sub cbFW_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbFW.SelectedIndexChanged
Dim currentInfo = DirectCast(cbFW.SelectedItem, FirmwareInfo)
lblPwrType.Text = currentInfo.pwrtype
lblPic.Text = currentInfo.pic
lblPartNum.Text = currentInfo.parnum
' ....etc through options
End Sub
I'm currently designing an application within visual basic using vb.net. The first form asks for login information and then prompts the next form to select a customer. The customer information is stored in a text file that gets put in an array. I next have a form for the user to display and edit that information. How can I use the array I already created in the previous form in my display and edit form?
Private Sub frmCustomerList_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim sr As StreamReader = File.OpenText("customer.txt")
Dim strLine As String
Dim customerInfo() As String
Do While sr.Peek <> -1
strLine = sr.ReadLine
customerInfo = strLine.Split("|")
cboCustomers.Items.Add(customerInfo(0))
customerList(count) = strLine
count = count + 1
Loop
End Sub
Private Sub cboCustomers_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboCustomers.SelectedIndexChanged
Dim customerInfo() As String
Dim index As Integer = cboCustomers.SelectedIndex
Dim selectedCustomer As String = customerList(index)
customerInfo = selectedCustomer.Split("|")
End Sub
Make the next form require it in the constructor:
Public Class EditCustomer
Public Sub New(customerInfo As String())
InitializeComponent() 'This call is required, do not remove
'Yay! Now you have your info
End Sub
End Class
You'd call it by doing something like...
Dim editForm = New EditCustomerFrom(customerInfo)
editForm.Show()
Alternatively, you could have a property on the form you set.
Dim editForm = New EditCustomerFrom()
editForm.Customer = customerInfo
editForm.Show()
Then inside the Load event of that form, you'd write the logic that would display it looking at that property.
Aside: You should look at maybe defining an object to hold customer info and do some JSON or XML serialization for reading/writing to the file, IMO. This architecture is kind of not good as is....