How to save many object (with the same class) to txt file and binding those object with listbox VB.NET - vb.net

I try to program a simple project to save data to txt file, read it and use binding data to show it. My project like this.
When I add ID Person to "Add ID" textbox (Textbox which near Button "Add ID"). It will add ID to Listbox and "ID Name" textbox. With this IDName, I insert FirstName and LastName for first person and Save Person's Name. Then, I Add new ID in "Add ID" textbox and fill First,last name and save it again
I refer this page http://vbnetsample.blogspot.de/2007/12/serialize-deserialize-class-to-file.html?m=1 to save and read data to txt file. It's run ok. But my problem is that when I save Person 2 with ID 2, Person 1 is overwrited. I think I can save Person to List Person. But it will make difficult when I want to update any Person's data. I don't know whether Is there any way to save and update each person. And by the way How can I show data by binding data in listbox.
Here is my code
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Public Class Form1
Public pPerson As New Person
'Serialize and Save Data to txt file
Private Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveButton.Click
pPerson.IDName = IDNameTextBox.Text
pPerson.FirstName = FirstNameTextBox.Text
pPerson.LastName = LastNameTextBox.Text
Dim fs As FileStream = New FileStream("C:\Users\Bruce\Desktop\test.txt", FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter()
bf.Serialize(fs, pPerson)
fs.Close()
End Sub
'Deserialize and Read Data from txt file
Private Sub ReadButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReadButton.Click
If FileIO.FileSystem.FileExists("C:\Users\Bruce\Desktop\test.txt") Then
Dim fsRead As New FileStream("C:\Users\Bruce\Desktop\test.txt", FileMode.Open)
Dim bf As New BinaryFormatter()
Dim objTest As Object = bf.Deserialize(fsRead)
fsRead.Close()
IDNameTextBox.Text = objTest.IDName
FirstNameTextBox.Text = objTest.FirstName
LastNameTextBox.Text = objTest.LastName
End If
End Sub
Private Sub AddIDButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddIDButton.Click
ListBox1.Items.Insert(0, AddIDTextBox.Text)
IDNameTextBox.Text = AddIDTextBox.Text
End Sub
End Class
'Create Class Person
<System.Serializable()>
Public Class Person
Private m_sIDName As String
Private m_sFirstName As String
Private m_sLastName As String
Public Sub New()
End Sub
Public Property IDName() As String
Get
Return Me.m_sIDName
End Get
Set(ByVal value As String)
Me.m_sIDName = value
End Set
End Property
Public Property FirstName() As String
Get
Return Me.m_sFirstName
End Get
Set(ByVal value As String)
Me.m_sFirstName = value
End Set
End Property
Public Property LastName() As String
Get
Return Me.m_sLastName
End Get
Set(ByVal value As String)
Me.m_sLastName = value
End Set
End Property
End Class

To me it seems like the issue here is
Dim fs As FileStream = New FileStream("C:\Users\Bruce\Desktop\test.txt", FileMode.OpenOrCreate)
you have to change it to this:
If not File.Exists("C:\Users\Bruce\Desktop\test.txt") Then
File.create("C:\Users\Bruce\Desktop\test.txt")
End If
Dim fs As FileStream = New FileStream("C:\Users\Bruce\Desktop\test.txt", FileMode.Append)
The whole problem was that your file was simply open (or created if it was not there before) and being written from the first line.By using FileMode.Append your File will be opened and anything new will be tried to be written at the end of the file.
Let me know if this worked :)

Related

How to Setup A Combox , always will with same data, as a user control to be used on multiple forms

I have a ComboBox that I use on multiple WinForms. Instead of dropping a ComboBox on each WinForm and then filling the ComboBox with data from a DataTable on each individual WinForm, couldn't I create a User Control (ComboBox) that has the data populated already and just use that UC on my Winforms?
Below is how I fill the data for each individual combobox now. (I have a public class for the sql stuff)
The Variable SQL comes from a Class called SQLControl. the Class has all the sql connection stuff.
Public Sub Fillcombobox()
sql.AddParam("#ExaminerType", 3)
sql.ExecQuery("MyStoredProcedure")
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = sql.DBDT
End Sub
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Call Fillcombobox()
End Sub
You can put a small Class Examiner
Public Class Examiner
Public Property Examiner_ID As Integer
Public Property Last_Name As String
Public Sub New(ID As Integer, lname As String)
Examiner_ID = ID
Last_Name = lname
End Sub
End Class
Then, when the first form loads, get the data in a list declared in a module so it can be accessed from any form in the application. Of course, you may have other things in the Module.
Module Module1
Public ExaminerData As New List(Of Examiner)
End Module
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillExaminerList()
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = ExaminerData
End Sub
Any other form that needs the data to fill a combo box can use ExaminerData. You only call FillExaminerList once at the beginning of the application. There is only a single hit on the database.
Private OPConStr As String = "Your connection string."
Private Sub FillExaminerList()
Dim dt As New DataTable
Using cn As New SqlConnection(OPConStr),
cmd As New SqlCommand("MyStoredProcedure", cn)
cmd.Parameters.Add("#ExaminerType", SqlDbType.Int).Value = 3
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
For Each row As DataRow In dt.Rows
Dim ex As New Examiner(CInt(row("Examiner_ID")), row("Last_Name").ToString)
ExaminerData.Add(ex)
Next
End Sub

Saving ListBox with Additional information in My.settings

I recently started learning listboxes on VB.net and wanted to create something like a student list with additional information:
Private Class Students
Private m_Name As String
Private m_Age As String
Public Sub New(ByVal new_Name As String, ByVal new_Age As String)
m_Name = new_Name
m_Age = new_Age
End Sub
Public Overrides Function ToString() As String
Return m_Name
End Function
Public Function Age() As String
Return m_Age
End Function
End Class
So, students add to the listbox as follows:
ListBox1.Items.Add(New Students(StudentName.Text, StudentAge.Text))
'StudentName and StudentAge are textboxes.
but I also wanted to save this information so that it is automatically entered when the program is restarted. I tried to do first an event that saves every item in the ListBox but using the function above it doesn't work.
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
For Each item In ListBox1.Items
My.Settings.Students.Add(item)
My.Settings.Save()
Next
End Sub
Then I would like to load this information, but just like the event with saving information when closing the program it does not work.
I got an System.InvalidCastException The conversion of 'Students' to 'String' is not valid. error.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Settings.Students Is Nothing Then
My.Settings.Students = New Specialized.StringCollection
Else
For Each item In My.Settings.Students
ListBox1.Items.Add(item)
Next
End If
End Sub
I would like to have these the information in listbox after load.
My.Settings is a feature that enables you to manage your application's settings such as; the last location and size of a form, the last selected properties of controls, recently opened files, ...etc. It's not - as already commented above - a place to store and retrieve your objects' data. Your data should be in a database or serialized as an alternative. Let's try the serialization approach to solve your problem.
Create your Serializable objects
The Student class:
Imports System.IO
Imports System.Xml.Serialization
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable()>
Public Class Student
#Region "Enums"
<Serializable()>
Public Enum Genders
Unknown
Female
Male
End Enum
#End Region
#Region "Constructors"
Public Sub New() : End Sub
Public Sub New(firstName As String, lastName As String, dob As DateTime, gender As Genders)
Me.FirstName = firstName
Me.LastName = lastName
Me.DOB = dob
Me.Gender = gender
End Sub
#End Region
#Region "Public Properties"
Public Property FirstName As String
Public Property LastName As String
Public ReadOnly Property FullName As String
Get
Return $"{FirstName} {LastName}"
End Get
End Property
Public Property DOB As DateTime
Public ReadOnly Property Age As Integer
Get
If DOB = Nothing Then Return 0
Dim y As Integer = Today.Year - DOB.Year
If DOB > Today.AddYears(-y) Then y -= 1
Return y
End Get
End Property
Public Property Gender As Genders
#End Region
#Region "Public Methods"
Public Overrides Function ToString() As String
Return $"{FullName}, {Age}, {Gender}"
End Function
#End Region
End Class
The Students class:
<Serializable()>
Public Class Students
#Region "Public Properties"
Public Property List As New List(Of Student)
#End Region
#Region "Public Functions"
Public Sub SaveXml(filePath As String)
Dim xs As New XmlSerializer(GetType(Students))
Try
Using fs As New FileStream(filePath, FileMode.Create)
xs.Serialize(fs, Me)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to serialize!")
End Try
End Sub
Public Shared Function LoadXml(filePath As String) As Students
Dim students As Students = Nothing
Dim xs As New XmlSerializer(GetType(Students))
Try
Using fs As New FileStream(filePath, FileMode.Open)
students = DirectCast(xs.Deserialize(fs), Students)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to deserialize!")
End Try
Return students
End Function
'Or if you prefer the binary format.
Public Sub SaveBytes(filePath As String)
Dim bf As New BinaryFormatter
Try
Using fs As New FileStream(filePath, FileMode.Create)
bf.Serialize(fs, Me)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to serialize!")
End Try
End Sub
Public Shared Function LoadBytes(filePath As String) As Students
Dim students As Students = Nothing
Dim bf As New BinaryFormatter
Try
Using fs As New FileStream(filePath, FileMode.Open)
students = DirectCast(bf.Deserialize(fs), Students)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to deserialize!")
End Try
Return students
End Function
#End Region
End Class
Your new classes in action!
In your form which contains the ListBox, create a class level variable of BindingSource type which will be used to populate the list box, add, edit, and remove Student objects.
Private bs As BindingSource
In your Form's constructor or Load event, load or create a new object of Students type, create a new instance of the BindingSource and attach it to the ListBox's DataSource property as follow:
If String.IsNullOrEmpty(My.Settings.MyStudentsFile) Then
'XML file.
My.Settings.MyStudentsFile = Path.Combine(My.Application.Info.DirectoryPath, "MyStudentsData.xml")
'or a binary file name if you prefer the binary formatter, for example.
'My.Settings.MyStudentsFile = Path.Combine(My.Application.Info.DirectoryPath, "MyStudentsData.dat")
End If
If File.Exists(My.Settings.MyStudentsFile) Then
obj = Students.LoadXml(My.Settings.MyStudentsFile)
'or
'obj = Students.LoadBytes(My.Settings.MyStudentsFile)
End If
If obj Is Nothing Then obj = New Students
bs = New BindingSource(obj, "List")
ListBox1.DataSource = bs
Where the My.Settings.MyStudentsFile is the path of the data file.
What will be displayed in the list box is what the ToString returns for each Student object. If you'd like instead to display the FullName only, then set the DisplayMember property of the list box as follow:
ListBox1.DisplayMember = "FullName"
To add new Student:
Dim stu As New Student(
"FirstName",
"LastName",
New DateTime(2000, 5, 22),
Student.Genders.Male
)
bs.Add(stu)
ListBox1.ClearSelected()
ListBox1.SelectedItem = stu
To modify the properties of a Student entry:
If bs.Current IsNot Nothing Then
Dim stu As Student = DirectCast(bs.Current, Student)
stu.FirstName = "CorrectFirstName"
stu.LastName = "CorrectLastName"
stu.Gender = Student.Genders.Female
bs.ResetCurrentItem()
End If
To remove entries:
If bs.Current IsNot Nothing Then
bs.RemoveCurrent()
End If
'In case of multixxx SelectionMode
ListBox1.SelectedItems.OfType(Of Student).ToList.ForEach(Sub(stu) bs.Remove(stu))
To display a selected item in other controls, handle the SelectedValueChanged event of the list box as follow:
Private Sub ListBox1_SelectedValueChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedValueChanged
If bs.Current IsNot Nothing Then
Dim stu As Student = DirectCast(bs.Current, Student)
TextBox1.Text = stu.FirstName
TextBox2.Text = stu.LastName
DateTimePicker1.Value = If(stu.DOB <> Nothing, stu.DOB, Now)
TextBox3.Text = stu.Age.ToString
End If
End Sub
Finally, to save your data in Form.FormClosing event:
bs.EndEdit()
Dim stus As Students = DirectCast(bs.DataSource, Students)
stus.SaveXml(My.Settings.MyStudentsFile)
'or
'stus.SaveBytes(My.Settings.MyStudentsFile)
bs.Dispose()
That's it all.

retrieve data out of a public structure and collection

I'm using a collection and structure to store some parsed data in a class, but when i try to retrieve the data it is null. On form1 i'm i can get the response string which is all the raw data. Am I adding the parsed data to the collection correctly? Did I call it on form1 correctly?
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
Dim dloader as new Downloader(blah)
'this does not work
Dim test as new _ListInfo
msgbox(test.Name) ' produces an empty message box
'this works
msgbox(dloader.Download)
End Sub
Here is my code for the class:
Public Structure _Info
Dim Name As String
End Structure
Public Class Downloader
Dim _ListCollection As New Collection(Of _ListInfo)
Public ReadOnly Property ListCollection() As Collection(Of _ListInfo)
Get
ListCollection = _ListCollection
End Get
End Property
Public Function Download() As String
'doing the download
ParseList()
Return _ResponseString
End Function
Private Sub ParseList()
_ListCollection.Clear()
Dim Name As String
Dim MyInfo As _ListInfo
MyInfo.Name = Name
_ListCollection.Add(MyInfo)
End Sub
Why do you expect it to work? You are just newing-up a structure and accessing a property. Don't you want to do something like:
Dim dloader as new Downloader(blah)
dloader.Download()
' Show first name.
MsgBox(dloader.ListCollection(0).Name)

Storing the path of a file in a listbox

I am making a music player application. I am using a ListBox to display the songs. When the user adds a song, it displays the full path of the song. But I want it to only display the song name(The songs can be located in any folder in any drive). A windows media player control is playing the songs. Thanks in advance!
So You want to extract only the song name from the whole path of the song. A simple tricky logic will do these. This is VBA
sub song_name_extractor()
song_path = "c:\songs\favret\mylove.mp3" ' Assume this is where the song is
song_name = Right(song_path, Len(song_path) - InStrRev(song_path, "\"))
Msgbox song_name 'outputs mylove.mp3
song_name = Left(song_name, InStrRev(song_name, ".") - 1)
Msgbox song_name ' outputs only mylove removes extensions of file
end sub
Explaination:
Right Func, cuts the right part of the string into sub-string of given number
Len Func, To find the length of the string
InStrRev Fun, gives the point of occurrence, of the given character in a string
searching from right to left
Left Func, cuts the Left part of the string into sub-string of given number
I would do it something like this:
1. Create an object that can hold the info of the song.
2. Create an list that holds all songs in the playlist.
3. Add that list as a datasource to the listbox, By setting .DisplayMember, you are telling wich property will be visible as listitemtext in the listbox.
4. When listindex changes, get the object stored in listbox.SelectedItem and ctype it to an songobject to work with it.
Public Class Form1
Structure SongObject
Public SongPath As String
Public NameNoExtension As String
Public SongLength As Integer
Public SongRating As Integer
Private _FileName
Public Property FileName() As String
Get
Return _filename
End Get
Set(ByVal value As String)
_FileName = value
End Set
End Property
Public Sub New(ByVal path As String)
SongPath = path
FileName = IO.Path.GetFileName(path)
NameNoExtension = IO.Path.GetFileNameWithoutExtension(path)
SongLength = 0 'fix
SongRating = 5 'fix
End Sub
End Structure
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim musicpath As String = "C:\Users\Public\Music\Sample Music\"
'Load songs into listbox
ListBox1.DisplayMember = "FileName"
Dim songs As New List(Of SongObject)
For Each f As String In IO.Directory.GetFiles(musicpath, "*.mp3")
songs.Add(New SongObject(f))
Next
ListBox1.DataSource = songs
End Sub
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
'Get songitem
Dim SelectedSong As SongObject = CType(ListBox1.SelectedItem, SongObject)
MsgBox("Path:" & SelectedSong.SongPath & vbCrLf & "FileName:" & SelectedSong.FileName)
'Todo: play selected song...
End Sub
End Class
Use IO.Path.GetFileName(path) and IO.Path.GetFileNameWithoutExtension(path) to get filename instead of right/left/instr/mid and so on.

Bind a Class Property that returns a list to a DataGridViewComboBoxColumn

I have a file name that is found at multiple paths. I want to present this data to the user in the format of a DGV. I have a DGV with one text box column for the name, and another combobox column for the paths. I am having trouble getting the combobox column to bind to the property of the class that returns the paths.
Any help would be much appreciated.
Public Class fileTest
Public Property FileName As String
Public Property Paths As String()
Public Sub New(ByRef _name As String, ByVal _paths As String())
Me.FileName = _name
Me.Paths = _paths
End Sub
End Class
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim fileList As New BindingList(Of fileTest)
fileList.Add(New fileTest("TEST", {"ABC", "123"}))
Me.DataGridView1.AutoGenerateColumns = False
Me.DataGridView1.DataSource = fileList
Me.DataGridView1.Columns("FileName").DataPropertyName = "FileName"
CType(Me.DataGridView1.Columns("Paths"), DataGridViewComboBoxColumn).DataPropertyName = "Path"
End Sub
DataGridViewComboBoxColumn is horrible. HORRIBLE.
We eventually gave up using it and now just float a standard Combobox over the grid.