Saving ListBox with Additional information in My.settings

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.

Leave a Comment