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.
Related
i have a checklisbox with some value, let's say
"Apple"
"Peach"
"Lemon"
These values came from a dataset.
I have an array with Apple and Lemon: {"Apple", "Lemon"}.
How to check in the checklistbox each value read in this array?
EDIT: In my case, the checklistbox was populate using a dataset provided by a SQL query
In the following code sample, data from SQL-Server (database doesn't matter but this is what I used, what is important is the container the data is loaded into is loaded into a list.
Container to hold data
Public Class Category
Public Property Id() As Integer
Public Property Name() As String
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Class to read data
Imports System.Data.SqlClient
Public Class SqlOperations
Private Shared ConnectionString As String =
"Data Source=.\SQLEXPRESS;Initial Catalog=NorthWind2020;Integrated Security=True"
Public Shared Function Categories() As List(Of Category)
Dim categoriesList = New List(Of Category)
Dim selectStatement = "SELECT CategoryID, CategoryName FROM Categories;"
Using cn As New SqlConnection With {.ConnectionString = ConnectionString}
Using cmd As New SqlCommand With {.Connection = cn}
cmd.CommandText = selectStatement
cn.Open()
Dim reader = cmd.ExecuteReader()
While reader.Read()
categoriesList.Add(New Category() With {.Id = reader.GetInt32(0), .Name = reader.GetString(1)})
End While
End Using
End Using
Return categoriesList
End Function
End Class
Extension method
Which can check or uncheck a value if found in the CheckedListBox and is case insensitive.
Public Module Extensions
<Runtime.CompilerServices.Extension>
Public Function SetCategory(sender As CheckedListBox, text As String, Optional checkedValue As Boolean = True) As Boolean
If String.IsNullOrWhiteSpace(text) Then
Return False
End If
Dim result = CType(sender.DataSource, List(Of Category)).
Select(Function(item, index) New With
{
Key .Column = item,
Key .Index = index
}).FirstOrDefault(Function(this) _
String.Equals(this.Column.Name, text, StringComparison.OrdinalIgnoreCase))
If result IsNot Nothing Then
sender.SetItemChecked(result.Index, checkedValue)
Return True
Else
Return False
End If
End Function
End Module
Form code
Public Class ExampleForm
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckedListBox1.DataSource = SqlOperations.Categories
End Sub
Private Sub CheckCategoryButton_Click(sender As Object, e As EventArgs) Handles CheckCategoryButton.Click
CheckedListBox1.SetCategory(CategoryToCheckTextBox.Text, StateCheckBox.Checked)
End Sub
End Class
To check all at once
Private Sub CheckAllButton_Click(sender As Object, e As EventArgs) Handles CheckAllButton.Click
CType(CheckedListBox1.DataSource, List(Of Category)).
ForEach(Sub(cat) CheckedListBox1.SetCategory(cat.Name, True))
End Sub
You haven't mentioned exactly how the CheckedListBox gets populated by the DataSet, I've made the assumption that you add Strings directly to the Items collection.
This code will simply loop through the CheckedListBox and compare the values with the array, whatever the match result, the Checkbox is either ticked or cleared.
Dim theArray() As String = {"Apple", "Lemon"}
For counter As Integer = 0 To CheckedListBox1.Items.Count - 1
Dim currentItem As String = CheckedListBox1.Items(counter).ToString
Dim match As Boolean = theArray.Contains(currentItem.ToString)
CheckedListBox1.SetItemChecked(counter, match)
Next
Use the SetItemChecked method like this:
CheckedListBox1.SetItemChecked(CheckedListBox1.Items.IndexOf("your item goes here"), True)
Note that if the item does not exist, an exception will be thrown, so be sure to check for the item before calling the SetItemChecked() method. To do that, you can check for the return value of IndexOf(). It will be -1 if the item does not exist.
Having issues when clicking the remove button. If more of my code is needed, let me know. I get this error on the line AddressList.Remove(selectedName):
System.ArgumentException: 'Argument 'Key' is not a valid value.
I've tried many variations but can't figure out why this doesn't work. I think it has something to do with how the strings are concatenated in the listbox. I need to be able to remove entries from the collection and the listbox. Any help would be greatly appreciated.
Module EmailCollection
Public AddressList As New Collection
Public Sub AddRecord(ByVal a As cAddress)
Try
AddressList.Add(a)
Catch ex As Exception
MessageBox.Show("Error: inputs must be characters valid in string format")
End Try
End Sub
End Module
public class form1
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim frmAdd As New AddNewName
frmAdd.ShowDialog()
UpdateListBox()
End Sub
Private Sub UpdateListBox()
lstAddress.Items.Clear()
Dim a As cAddress
For Each a In AddressList
lstAddress.Items.Add(String.Concat(a.strName, a.strEmail, a.strPhone, a.strComment))
Next
If lstAddress.Items.Count > 0 Then
lstAddress.SelectedIndex = 0
End If
End Sub
Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
Dim selectedName As String
Try
' Get the selected value.
selectedName = lstAddress.SelectedItem.ToString()
' Remove the selected name from the list box and the collection.
If MessageBox.Show("Are you sure?",
"Confirm Deletion",
MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then
lstAddress.Items.Remove(selectedName)
AddressList.Remove(selectedName)
End If
Catch ex As NullReferenceException
MessageBox.Show("Select an item to remove.", "Selection Needed")
End Try
End Sub
end class
In your Module I changed AddressList from the old VB6 Collection type to the .net List(Of T). The T stands for Type.
Module EmailCollection
Public AddressList As New List(Of cAddress)
Public Sub AddRecord(ByVal a As cAddress)
AddressList.Add(a)
End Sub
End Module
I guessed that your cAddress class looks something like this. I added a custom .ToString method so the list box will display the data you wish but the item, itself, will be a cAddress object.
Public Class cAddress
Public Property strName As String
Public Property strEmail As String
Public Property strPhone As String
Public Property strComment As String
Public Overrides Function ToString() As String
Return $"{strName}, {strEmail}, {strPhone}, {strComment}"
End Function
End Class
In the Form...
Instead of adding a string to the list box I added the cAddress object. The list box calls .ToString on the object to get the display value.
Private Sub UpdateListBox()
ListBox1.Items.Clear()
For Each a As cAddress In AddressList
ListBox1.Items.Add(a)
Next
If ListBox1.Items.Count > 0 Then
ListBox1.SelectedIndex = 0
End If
End Sub
In the remove button I cast the selected item to its underlying type, cAddress. This is the item removed from the AddressList. Then simply remove the selected item from the list box.
Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
If MessageBox.Show("Are you sure?",
"Confirm Deletion",
MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then
AddressList.Remove(DirectCast(ListBox1.SelectedItem, cAddress))
ListBox1.Items.Remove(ListBox1.SelectedItem)
End If
End Sub
I changed the name of the list box to ListBox1 to match my test project.
Here is something to try, use a BindingSource for setting up the ListBox. In the class override ToString to what is to be shown in the ListBox rather than what you are doing now without a DataSource.
My version of your class has name and property name changes.
Public Class Address
Public Property Name() As String
Public Property Email() As String
Public Property Phone() As String
Public Property Comment() As String
Public Overrides Function ToString() As String
Return $"{Name} {Email} {Phone} {Comment}"
End Function
End Class
Mocked data is used to populate the ListBox
Public Class Form1
Private ReadOnly _bsAddresses As New BindingSource
Private Sub UpdateListBox()
Dim AddressList = New List(Of Address) From
{
New Address() With {
.Name = "John",
.Email = "john#gmail.com",
.Phone = "555-444-3456",
.Comment = "AAA"},
New Address() With {
.Name = "Mary",
.Email = "mary#gmail.com",
.Phone = "888-333-2222",
.Comment = "BBB"},
New Address() With {
.Name = "Bob",
.Email = "bob#gmail.com",
.Phone = "111-555-9999",
.Comment = "CCC"}
}
_bsAddresses.DataSource = AddressList
lstAddress.DataSource = _bsAddresses
lstAddress.SelectedIndex = 0
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) _
Handles Me.Shown
UpdateListBox()
End Sub
Private Sub RemoveButton_Click(sender As Object, e As EventArgs) _
Handles RemoveButton.Click
If lstAddress.Items.Count > 0 AndAlso lstAddress.SelectedItem IsNot Nothing Then
Dim address = CType(_bsAddresses.Current, Address)
If My.Dialogs.Question($"Remove {address.Name}") Then
_bsAddresses.RemoveCurrent()
RemoveButton.Enabled = _bsAddresses.Count > 0
End If
End If
End Sub
End Class
Code module for asking a question
Namespace My
<ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)>
Partial Friend Class _Dialogs
Public Function Question(text As String) As Boolean
Return (MessageBox.Show(
text,
My.Application.Info.Title,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) = MsgBoxResult.Yes)
End Function
End Class
<HideModuleName()>
Friend Module WinFormsDialogs
Private instance As New ThreadSafeObjectProvider(Of _Dialogs)
ReadOnly Property Dialogs() As _Dialogs
Get
Return instance.GetInstance()
End Get
End Property
End Module
End Namespace
Karen's post seems quite comprehensive. My response is an attempt to focus on your direct question.
I don't see all of the type definitions shown in your code, but in answer to your question, which I believe is "Why am I getting System.ArgumentException: 'Argument 'Key' is not a valid value":
In the offending line of code:
AddressList.Remove(selectedName)
AddressList is a collection. To use .Remove, you must pass in an object of the AddressList collection to remove it. You are passing a simple string, and that is not an AddressList object. You need to create an object based on your string selectedName to pass into .Remove and that line should work. The rest of what you are doing seems more complex.
Okay so I think I've confused myself. I'm working on a class inventory database. My plan is to have a person enter their information and a book's info into the textboxes, and I want that informationan to be placed into the lstbox (basically saying who is now renting this book). I also have a Datagridview where I want my txtfile to open up in and display the books, but only the objects that are in my txtfile (Title, Author, ISBN, YearPublishished). I tried to do it with a query but it doesn't seem to be working.
I have a few errors that I need help with
When I start the application and click on display books. My OFD opens and I click the txtfile I need, but it doesnt display to my DataGridView properly. IT only shows numbers going down the columns.
We had to use objects of classes, and I just want to know if Im using them correctly. This is my first time doing this so any help would be appreciated.
I've attached a picture of my Application
Okay so this what I have so far:
Public Class Library
Implements IComparable
'Private Instance Variables - Always declared as Private
Private m_Title As String
Private m_Author As String
Private m_ISBN As String
Private m_YearPublished As Date
Private m_DateRented As Date 'use maskedtxtbox for dates and phone number
Private m_DueDate As Date
Private m_Name As String
Private m_Email As String
Private m_PhoneNumber As String
Private Shared LibraryCount As Integer
'Property Blocks for each Private Instance Variable
Public Property Title() As String
Get
Return m_Title
End Get
Set(value As String)
m_Title = value
End Set
End Property
Public Property Author() As String
Get
Return m_Author
End Get
Set(value As String)
m_Author = value
End Set
End Property
Public Property ISBN As String
Get
Return m_ISBN
End Get
Set(value As String)
m_ISBN = value
End Set
End Property
Public Property YearPublished As Date
Get
Return m_YearPublished
End Get
Set(value As Date)
m_YearPublished = value
End Set
End Property
Public Property DateRented As Date
Get
Return m_DateRented
End Get
Set(value As Date)
m_DateRented = value
End Set
End Property
Public Property DueDate As Date
Get
Return m_DueDate
End Get
Set(value As Date)
m_DueDate = value
End Set
End Property
Public Property Name As String
Get
Return m_Name
End Get
Set(value As String)
m_Name = value
End Set
End Property
Public Property Email As String
Get
Return m_Email
End Get
Set(value As String)
m_Email = value
End Set
End Property
Public Property PhoneNumber As String
Get
Return m_PhoneNumber
End Get
Set(value As String)
m_PhoneNumber = value
End Set
End Property
Public Sub New()
m_Title = ("")
m_Author = ""
m_ISBN = ""
m_DateRented = ""
m_DueDate = ""
m_Name = ""
m_Email = ""
m_PhoneNumber = 0
LibraryCount += 1
End Sub
'Overloaded /Parameterized Constructors
Public Sub New(ByVal p_Title As String, ByVal p_Author As String, ByVal p_ISBN As String, ByVal p_YearPublished As Date,
ByVal p_DateRented As Date, ByVal p_DueDate As Date, ByVal p_Name As String, ByVal p_Email As String, ByVal p_PhoneNumber As String)
m_Title = p_Title
m_Author = p_Author
m_ISBN = p_ISBN
m_YearPublished = p_YearPublished
m_DateRented = p_DateRented
m_DueDate = p_DueDate
m_Name = p_Name
m_Email = p_Email
m_PhoneNumber = p_PhoneNumber
LibraryCount += 1
End Sub
'QUESTION? How would I calculate when the books is due
' Public Function CalcDueDate() As String 'person gets charged if they are late returning the book
' If m_DateRented.Date > m_DueDate.Date Then
'm_DueDate = m_DateRented + 10
'MessageBox.Show("You will be charged with a late of .50 cents")
'End If
'End Function
Public Overrides Function ToString() As String
Return "The user rented a book on" & DateRented & " it will be due on" & DueDate
End Function
Public Function CalcDueDate(ByVal p_DueDate As Date) As Date
Return p_DueDate
End Function
Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
Throw New NotImplementedException()
Return m_YearPublished.CompareTo(CType(obj, Library).m_YearPublished)
End Function
Public Overrides Function Equals(obj As Object) As Boolean
'create a library object from the paramer - check to see if it exist
Dim libraryobj As Library = TryCast(obj, Library)
If libraryobj Is Nothing Then
Return False
Else
'code for the comparison based on m_yearpublished
Return m_YearPublished.Equals(libraryobj.m_YearPublished)
End If
End Function
End Class
End Class
Public Class Project1Data
'create counter variable for the array
Dim counter As Integer = 0
'create an array of objects
Private books(12) As Library
'Collection List
Dim mybooklist As New List(Of Library)
Dim abook As Library
Private Sub btnDisplayBooks_Click(sender As Object, e As EventArgs) Handles btnDisplayBooks.Click
Dim textfile As String
OpenFileDialog1.ShowDialog()
textfile = OpenFileDialog1.FileName
DGVbooks.DataSource = IO.File.ReadAllLines("LibraryDatabase")
End Sub
Public Sub Data()
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
'The Btn_Add method creates a Renter and book and adds it to the collectio
'validate input data
If ValidateData() = -1 Then
Exit Sub
End If
'use valid input to create an object of the class by calling overloaded constructor
Dim bookk As New Library(txtbooktitle.Text, txtbookauthor.Text, txtISBN.Text, CDate(txtyearpublished.Text),
CDate(txtdaterented.Text), CDate(txtduedate.Text), txtname.Text, txtemail.Text, (txtphonenunber.Text))
'error check that the array is not full
If counter >= books.Length Then
ReDim Preserve books(counter + 1)
End If
'add book to the array
books(counter) = bookk
'add book to the list
mybooklist.Add(bookk)
'increment counter
counter = counter + 1
'Notify user that the rental has been recorder
MessageBox.Show("Rental recorded")
lstbooksoutput.DataSource = Nothing
lstbooksoutput.DataSource = books
End Sub
Private Sub Project1Data_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim lines() As String = IO.File.ReadAllLines("LibraryDatabase")
'In the DGV I just want to display the title, author, isbn, and year published
' In the lstbox the user name, email, and phone number will be displayed
'When a user enters a new book it adds itself to the DGV
Dim query = From abook In books
Select abook.Title, abook.Author, abook.ISBN, abook.YearPublished
Order By abook.YearPublished Ascending
DGVbooks.DataSource = query.ToList()
Okay here is where I created an objectof the class using an overload constructor, and I thought I sent it it to the datagridview but its not showing.
'Populate a list with Library objects
With mybooklist
.Add(New Library("Innoncent Injustice(A Chance Reddick Thriller Book 1), David Archer,B07D6442LM", 2018))
.Add(New Library("A Breath Of Witchy Air(A Wicked Witches Of the Midwest Mystery), Amanda M Lee, B07CRLRGXH", 2018))
.Add(New Library("Booke Of the Hidden, Jeri Westerson, 978 - 1635760507", 2017))
.Add(New Library("Wolves Of Wisteria(Wisteria Witches Mystery), Angela Pepper,978-1719549591", 2018))
.Add(New Library("Soul Render(Soul Stones Book 1), TL Branson,978-1980871392", 2018))
.Add(New Library("A Thrift Shop Murder(Cats And Ghosts And Avocado Toasts Book 1), Stanford Pritchard,978-1773480114", 2018))
.Add(New Library("Cathadeus, Jeff J Peters,B077ZNHY7T", 2017))
.Add(New Library("Clockwork Alchemist (Thief's Apprenctice Series Book 1), Sara C. Roethle,B01MG26KNA", 2016))
.Add(New Library("Air Awakens Series, Elise Kova,B01N4A2TK5", 2016))
.Add(New Library("Keeper of the Dragons The Prince Returns (Keeper of Dragons Book 1), J.A Cullican,B01FYL5BD0", 2016))
.Add(New Library("Nightblade A Book of UnderRealm, Garrett Robinson,978-1941076309", 2014))
.Add(New Library("Crazy Rich Asians, Kevin Kwan,B00AP2VQEM", 2013))
.Add(New Library("Poison Princess (The Arcana Chronicles Book 1), Kresley Cole,978-1442436640", 2012))
End With
DGVbooks.DataSource = query.ToList
DGVbooks.Columns("Title").HeaderText = "Title"
DGVbooks.Columns("Author").HeaderText = "Author"
DGVbooks.Columns("ISBN").HeaderText = "ISBN"
DGVbooks.Columns("Year Publsihed").HeaderText = "Year Published"
DGVbooks.DataSource = mybooklist
End Sub
Function ValidateData() As Integer
'This validates the data in all of the textboxes
'Invalid data returns a -1, else 1
If txtbooktitle.Text = "" Then
MessageBox.Show("Please enter a Book Title")
Return -1
ElseIf txtbookauthor.Text = "" Then
MessageBox.Show("Please enter a Book Title")
Return -1
ElseIf txtdaterented.Text = "" Or IsNumeric(txtdaterented.Text) = False Then
MessageBox.Show("Please enter a valid date")
Return -1
ElseIf txtduedate.Text = "" Or IsNumeric(txtduedate.Text) = False Then
MessageBox.Show("Please enter a valid date")
Return -1
ElseIf txtyearpublished.Text = "" Or IsNumeric(txtyearpublished.Text) = False Then
MessageBox.Show("Please enter a valid date")
Return -1
ElseIf txtname.Text = "" Then
MessageBox.Show("Please enter a name")
Return -1
ElseIf txtemail.Text = "" Then
MessageBox.Show("Please enter a valid email")
Return -1
ElseIf txtphonenunber.Text = "" Or IsNumeric(txtphonenunber.Text) = False Then
MessageBox.Show("Please enter a valid phone number")
Return -1
Else
Return 1
End If
End Function
Private Sub lstbooksoutput_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstbooksoutput.SelectedIndexChanged
If lstbooksoutput.SelectedIndex > -1 Then
Dim intbook As Integer = lstbooksoutput.SelectedIndex
'get information for that user in the array
MessageBox.Show(books(intbook).Name & " has rented" & books(intbook).Title & ". The book will be due on " & books(intbook).DueDate)
End If
End Sub
Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
'clear the textboxes
txtbooktitle.Clear()
txtbookauthor.Clear()
txtISBN.Clear()
txtdaterented.Clear()
txtduedate.Clear()
txtyearpublished.Clear()
txtname.Clear()
txtemail.Clear()
txtphonenunber.Clear()
'reset focus to title box
txtbooktitle.Focus()
End Sub
End Class
enter image description here
Ok Sydney, let's get this thing working!
Either you have seriously ignored your studies or your instructor
has left you to flounder; in which case you should notify your
college that you are not getting what you are paying for.
Your object contains too much data. You need a book object, a customer object and a rental object. Suppose the library director
wants a list of all your clients. In your model you would have each
client listed many times and it would be a mess to get the director
his list.
Use Automatic Properties if you can. I think they are available in VS 2015. You only need to write Public Property Title as String
and the wonderful vb compiler will write the get, set, and the
private field for you in the Itermedite Language code. (You won't
see it in your source code but it is there)
Now your text file. One line per book. Each property of the book should be separated by a character that does not appear elsewhere in
the file. How about the pipe character (|) (the uppercase backslash)
So a line in your file would look like... 1|Innoncent Injustice(A
Chance Reddick Thriller Book 1)|David Archer|B07D6442LM|2018 Notice
I have added another field, the BookID
I haven't included the default constructor (the one with no parameters). This forces any code creating a book object to provide
values for all parameters even if it is only "".
Public Class Book
Public Property BookID As Integer
Public Property Title As String
Public Property Author As String
Public Property ISBN As String
Public Property YearPublished As Integer
Public Sub New(id As Integer, bTitle As String, bAuthor As String, bISBN As String, bPublished As Integer)
BookID = id
Title = bTitle
Author = bAuthor
ISBN = bISBN
YearPublished = bPublished
End Sub
End Class
Public Class Project1Data
Private lstBooks As New List(Of Book)
Private Sub Project1Data_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim textfile As String
OpenFileDialog1.ShowDialog()
textfile = OpenFileDialog1.FileName
Dim lines() As String = IO.File.ReadAllLines(textfile)
For Each line As String In lines
Dim fields() As String = line.Split("|"c) 'the little c after "|" indicates that this is a char
'String.Split takes a char
Dim b As New Book(CInt(fields(0)), fields(1), fields(2), fields(3), CInt(fields(4)))
lstBooks.Add(b)
Next
DataGridView1.DataSource = lstBooks
End Sub
End Class
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 :)
I'm working on a vb.net project where a user can save and open a binary text.
However, the problem is that the when opening a file, it returns a object instead of string and integer.
It returns Projectname.Classname
What I want it to return is name and age that the user entered when saving the file.
Here is my code:
''' <summary>
''' When the user clicks open
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
Try
If (openFileDialog1.ShowDialog = DialogResult.OK) Then
thefilename = openFileDialog1.FileName
Dim message = animalmgr.ReadFile(thefilename)
'Getting method from manager
If (Not (message) Is Nothing) Then
message.ToList().ForEach(Sub(msg) Resultlst.Items.Add(msg))
Else
UpdateResults()
End If
End If
Catch ex As Exception
MessageBox.Show(ex.ToString, "Error in opening file")
End Try
End Sub
ReadFile function in the manager class:
''' <summary>
''' Reads a file from binary
''' </summary>
''' <param name="filename"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function ReadFile(ByVal filename As String) As Animal()
Dim BinSerial As BinSerializerUtility = New BinSerializerUtility
Dim animals = BinSerial.BinaryFileDeSerialize(Of Animal)(filename)
Return animals.ToArray
End Function
I did the exact same project in C# which worked perfectly, I don't know why it doesn't work in vb.net however. Does anyone know the problem and how to solve it?
Update:
Class BinSerializerUtility
Public Class BinSerializerUtility
Public Sub BinaryFileSerialize(ByVal objs As List(Of Animal), ByVal filePath As String)
Dim fileStream As FileStream = Nothing
Try
fileStream = New FileStream(filePath, FileMode.Create)
Dim b As BinaryFormatter = New BinaryFormatter
For Each obj In objs
b.Serialize(fileStream, obj)
Next
Finally
If (Not (fileStream) Is Nothing) Then
fileStream.Close()
End If
End Try
End Sub
Public Function BinaryFileDeSerialize(Of T As {Class})(ByVal filePath As String) As List(Of T)
Dim list = New List(Of T)
If Not File.Exists(filePath) Then
Throw New FileNotFoundException(("The file" + " was not found. "), filePath)
End If
Dim fileStream = New FileStream(filePath, FileMode.Open)
Dim b As BinaryFormatter = New BinaryFormatter
While (fileStream.Position < fileStream.Length)
list.Add(CType(b.Deserialize(fileStream), T))
End While
Return list
End Function
End Class
Animal class:
<Serializable()>
Public MustInherit Class Animal
Implements IAnimal
Private theCatagorytype As Categorytype
Private m_name As String
Private m_age As Integer
Private m_gender As String
Private m_id As Integer
Public Sub New(ByVal typ As Categorytype)
MyBase.New()
theCatagorytype = typ
End Sub
Public Sub New()
MyBase.New()
End Sub
#Region "Properties"
Public Overridable ReadOnly Property Description As String
Get
Return String.Format("{0}, {1}, {2}, {3}", Me.Id, Me.Name, Me.Age, Me.Gender)
End Get
End Property
Public Property Categorytype As Categorytype
Get
Return theCatagorytype
End Get
Set(value As Categorytype)
theCatagorytype = value
End Set
End Property
Public Property Id As Integer Implements IAnimal.Id
Get
Return m_id
End Get
Set(value As Integer)
If (value > 0) Then
m_id = value
End If
End Set
End Property
Public Property Name As String Implements IAnimal.Name
Get
Return m_name
End Get
Set(value As String)
m_name = value
End Set
End Property
Public Property Age As Integer
Get
Return m_age
End Get
Set(value As Integer)
m_age = value
End Set
End Property
Public Property Gender As String Implements IAnimal.Gender
Get
Return m_gender
End Get
Set(value As String)
m_gender = value
End Set
End Property
#End Region
Public MustOverride Function GetEaterType() As EaterType Implements IAnimal.GetEaterType
Public MustOverride Function GetSpecies() As String Implements IAnimal.GetSpecies
End Class
Rather than writing a class wrapper for an operation, I think it makes more sense to put such Save and Load functions in the class itself:
' in Animal class:
Friend Shared Function LoadData(myFile As String) As List(Of Animal)
Dim newList As List(Of Animal)
If File.Exists(myFile) Then
Using fs As New FileStream(myFile, FileMode.Open)
Dim bf = New BinaryFormatter
newList = CType(bf.Deserialize(fs), List(Of Animal))
End Using
Else
'return empty list of there is no file
newList = New List(Of Animal)
End If
Return newList
End Function
This seems simpler than trying to read one object at a time and place it in a List. Organizationally, it makes for fewer utility classes if the Foos, Bars and Animals can all serialize and deserialize themselves. To load them:
animals = Animal.LoadData(FileName)
' put animals in a ListBox:
myfrm.ListBox1.Items.AddRange(animals.ToArray)
Even better than making a copy of the animals for the list box, is to use your List(Of Animal) as the DataSource. That way, the user sees the same data set as your code is using.
When adding objects to a listbox or dropdown list, by default it uses ToString() to give you a string representation of the object, and the class name is the default ToString() implementation.
If you override ToString() in your Animal class, it will display however you need it to.
EDIT This is assuming you really want your ReadFile method to return a collection of Animal objects, and you want to add the actual objects to the listbox instead of truly adding string representations to the listbox.