I am about to admit defeat on this, I'm fairly new to VB and I am sure there is something quite basic I've managed to miss,
My problem with the following code is when the Button3_Click function is executed a "Value cannot be null. Parameter name: item" exception arises at run-time, if I forget to include the ".Name" on this line "ListBox2.Items.Add(test.Name)" then stuff still gets vomited out to the listbox so assuming there's something there,
any help?
Regards
Dan
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles
Button3.Click
Dim test As comdevice
Dim usbcoms() As comdevice = FindComs()
For Each test In usbcoms
ListBox2.Items.Add(test.Name)
Next
End Sub
Private Function FindComs() As comdevice()
Dim USBClass As New System.Management.ManagementClass("Win32_PNPEntity")
Dim USBCollection As System.Management.ManagementObjectCollection =
USBClass.GetInstances()
Dim USB As System.Management.ManagementObject
Dim temp() As comdevice
Dim n As Integer
n = 0
For Each USB In USBCollection
If USB("Name").ToString().Contains("P") Then
n += 1
End If
Next USB
ReDim temp(n)
n = 0
For Each USB In USBCollection
If USB("Name").ToString().Contains("COM") Then
temp(n).Name = USB("Name").ToString()
temp(n).DeviceID = USB("DeviceID").ToString()
End If
Next
Return temp
End Function
Private Structure comdevice
Public Name As String ' This employee's given name.
Public DeviceID As String ' This employee's family name.
End Structure
Your problems lies in the FindComs method.
The first loop search the USBCollection for devices that contains the letter P and you count them.
In the second loop, after dimensioning the return array with the number of devices found you try to fill this array with devices that contains the string COM, of course there is no relation between devices with COM and P in their name. You end up with an array bigger than the effective number of devices with COM in their name.
When the array returns, you add every slot of the array, but you have slots with NULL values and thus the error.
You can fix the problem dimensioning the array only for the devices with COM in their names
For Each USB In USBCollection
If USB("Name").ToString().Contains("COM") Then
n += 1
End If
Next USB
ReDim temp(n)
Oded's comment is the most likely cause of this failure.
An easy test is to set the Name property to some default string, like "test" in your comdevice class.
That way, the Name property will never be null and you can see if it ever gets changed.
Related
So I am trying to make a code where the user inputs an ID number and receives the line of text that the ID corresponds to. I am having trouble with the code as I am unable to display the result (or correct result) in a label box.
For example:
If I type in ID 1, it displays the data that corresponds to ID 2, ID 2 corresponds to ID 3 and ID 3 ends the loop (there are only 3 records in the data currently).
I have included my code below
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Filename = "NamesAndAges.txt"
FileOpen(1, Filename, OpenMode.Input,,,)
Dim ID As Integer
ID = txtSearch.Text
Dim Found As Boolean
Found = False
Do While Not EOF(1) And Found = False
If LineInput(1).Contains(ID) Then
lblDisplaySearch.Text = LineInput(1)
Found = True
Else MsgBox("Not Found")
End If
Loop
FileClose(1)
End Sub
Thanks in advance, would also really appreciate if anyone could explain the code they use as I am still a visual basic beginner.
Every time you call LineInput(1), it reads a line, so you're reading a line and checking if it contains ID then reading another line and setting lblDisplaySearch.Text to that value.
Try something like this:
Dim line As String
Do While Not EOF(1) And Found = False
line = LineInput(1)
If line.Contains(ID) Then
lblDisplaySearch.Text = line
Found = True
Else MsgBox("Not Found")
End If
Loop
I would strongly suggest simplifying this code, by using File.ReadLines:
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Filename = "NamesAndAges.txt"
Dim line = File.ReadLines(Filename).FirstOrDefault(Function(x) x.Contains(txtSearch.Text))
If line Is Nothing Then
MsgBox("Not Found")
Exit Sub
End If
lblDisplaySearch.Text = line
End Sub
The primary advantage is that you don't need to manage the Do While loop. Instead, you are passing the match condition (Function(x) x.Contains(txtSearch.Text)) to the FirstOrDefault method, which internally will find the first line matching the condition and return it, or return Nothing if it's not found.
For Each and IEnumerable
VB.NET allows you to loop over a set of items without knowing or caring about the index within the set. For example:
For Each x As String In File.ReadLines(Filename)
'do something with the line, which is now in x
Next
In order to use For Each with an object, that object has to implement a specific interface — the IEnumerable interface.
Interfaces
Interfaces guarantee that a given object has, or implements, specific members. In this case, if an object implements the IEnumerable interface, that means it has a GetEnumerator method, which the For Each uses under the hood.
IEnumerable(Of T)
The object returned from File.ReadLines implements another more advanced generic interface called IEnumerable(Of T). With this interface, the compiler can figure out automatically that each step of the For Each will be parsing a string, and we don't need to specify it as a string:
For Each x In File.ReadLines(Filename)
'x is known to be a String here
Next
Lambda expressions
The condition "the line which contains the search text" is written as a lambda expression, which (in this case) is made into a method without an explicit name or definition. This is convenient, because we don't have to write this:
Function ContainsCondition(line As String, toFind As String) As Boolean
Return line.Contains(toFind)
End Function`
every time we want to express a condition this way.
Type of x in the condition
Because File.ReadLines returns an IEnumerable(Of T), in this case an IEnumerable(Of String), the compiler can figure out that the condition is working on strings, so we don't have to specify that x is a string within the condition.
I am making a dvd database system in windows form and trying to display the dvd's entered by a user. Then display the Title, Director and Genre in 3 separate listBoxes.
When the user enters the information through 3 separate text boxes, the information is stored in a structure I made called TDvd. This means I can call for example dvd.Title or dvd.Director. I also use the variable index to add this information to an array I made called Dvd(100) (just a random number I used to test).
Here is the code I currently have for adding the items to the ListBox:
For i = 1 To noOfAddedDvds
lstTitle.Items.Add(dvd(i).Title)
lstDirector.Items.Add(dvd(i).Director)
lstGenre.Items.Add(dvd(i).Genre)
Next
The variable NoOfDvdsAdded is just a way of keeping track of the number of dvd's the user has already entered.
I run this and enter the Title, Director and Genre, but when I try and display this information across the 3 listboxes, I get the error:
An unhandled exception of type 'System.ArgumentNullException' occurred in System.Windows.Forms.dll
Public Class Form1
Structure TDvd
Dim Title As String
Dim Director As String
Dim Genre As String
End Structure
Dim dvd(100) As TDvd
Dim index As Integer = 0
Dim noOfAddedDvds As Integer
Private Sub btnAddToDatabase_Click(sender As Object, e As EventArgs) Handles btnAddToDatabase.Click
If txtDirector.Text <> "" Or txtGenre.Text <> "" Or txtTitle.Text <> "" Then
txtTitle.Text = dvd(index).Title
txtDirector.Text = dvd(index).Director
txtGenre.Text = dvd(index).Genre
index += 1
noOfAddedDvds += 1
End If
End Sub
Private Sub btnDisplayDatabase_Click(sender As Object, e As EventArgs) Handles btnDisplayDatabase.Click
Dim i As Integer
For i = 0 To noOfAddedDvds
MessageBox.Show(index & ", " & i)
lstTitle.Items.Add(dvd(i).Title)
lstDirector.Items.Add(dvd(i).Director)
lstGenre.Items.Add(dvd(i).Genre)
MessageBox.Show(index & ", " & i)
Next
End Sub
End Class
According to the documentation, an ArgumentNullException is thrown by the Add() method if the argument passed to it is null. (Or Nothing in VB.) So one of these is Nothing at runtime:
dvd(i).Title
dvd(i).Director
dvd(i).Genre
You'll have to debug to determine which. It would seem that the error is because you're starting your iteration at 1 instead of 0, I would think it should be:
For i = 0 To noOfAddedDvds - 1
So when you get to the index of noOfAddedDvds in your collection, that element will be an uninitialized struct with Nothing strings.
You'll definitely want to fix the iteration (indexes start at 0). Additionally, you may also benefit from initializing the String properties in your struct to String.Empty internally. Depends on whether you want similar errors to manifest as an exception or as an empty record. Sometimes the latter makes the problem more obvious since at runtime you'd see that your output started on the second record.
Just a few pointers...
The Items collection on the ListBox is actually 0 indexed, by which I mean that instead of going "1,2,3", it actually goes (0,1,2).
That's what your problem is.
Hint - think about perhaps using a List instead of an array as well... (for dvd)
Your thing cries out for being rewritten in OO form:
Friend DVDGenres
Undefined
Comedy
Action
Adventure
Sci-Fi
End Enum
Friend Class DVD
Public Property Title As String
Public Property Director As String
Public Property Genre As DVDGenres
Public Sub New
Title = ""
Director = ""
Genre = DVDGenres.Undefined
' other stuff too
End Sub
Public Overrides Function ToString As String
Return Title
End Sub
End Class
Now something to store them in. Arrays went out with Rubik's Cubes, so a List:
Private myDVDs As New List(of DVD)
A list and a class can do what arrays and structures can without the headaches. Add a DVD:
Dim d As New DVD
d.Name = TextBoxName.Text
d.Director = TextBoxDir.Text
d.Genre = comboboxGenre.SelectedItem
' add to the container:
myDVDs.Add(d)
Display all the DVDs in a ListBox to pick from:
AllDVDsLB.DataSource = myDVDs
AllDVDsLB.DisplayMember = "Title"
This will set your list as the datasource for the listbox. Whatever is in the List is automatically displayed without copying data into the Items collection. Then, say from selectedindex changed event, display the selected item details to some labels:
Label1.Text = Ctype(AllDVDsLB.SelectedItem, DVD).Title
Label2.Text = Ctype(AllDVDsLB.SelectedItem, DVD).Director
Label3.Text = Ctype(AllDVDsLB.SelectedItem, DVD).Genre.ToString
Iterate to do something like what is in the Question:
For Each d As DVD in myDVDs ' CANT run out of data
lstTitle.Items.Add(d.Title)
lstDirector.Items.Add(d.Director)
lstGenre.Items.Add(d.Genre.ToString)
Next
Or iterate and reference with an Int32:
For n As Integer = 0 To myDVDs.Count - 1
lstTitle.Items.Add(myDVDs(n).Title)
' etc
Next n
HTH
How can I change the order of data in a list on a random order (Shuffle). easiest method with the least coding effort without definition of new functions or sub please.
I usually tag the items with random data and sort that. You can implement the shuffle directly, but that's more work - especially proving the algorithm actually shuffles randomly...
Well, I just made this code snippet here for a future reference, if you want to use a list just replace all instances of "Stack" with "List" and make sure to change ".Push" to ".Add" and it should work fine. To be honest I'm surprised a shuffle function isn't built in.
Dim Deck As New Stack
Sub Main()
For i As Integer = 1 To 10
Deck.Push("Card #" & i)
Next
Do
Console.Clear()
For i As Integer = 0 To Deck.Count - 1
Console.WriteLine(Deck(i))
Next
Console.ReadKey(True)
Shuffle()
Loop
End Sub
Private Sub Shuffle()
Dim NewDeck As New Stack
Dim i As Integer
Dim s As String 'Change type depending on what is in your stack.
Dim r As New Random
Do
i = r.Next(0, Deck.Count)
s = Deck(i)
'Stops you getting several of one item and then none of others, etc.
If Not NewDeck.Contains(Deck(i)) Then
NewDeck.Push(s)
End If
Loop Until NewDeck.Count = Deck.Count
Deck = NewDeck
End Sub
Please have a look at the code below:
Public Class TestClass
Public TestProperty As Integer
End Class
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object,
ByVal e As System.EventArgs) Handles Me.Load
Dim i As Integer
Dim j As Integer
For j = 0 To 2
For i = 0 To 10
Dim k As Integer
Dim tc As TestClass
tc = New TestClass
tc.TestProperty = tc.TestProperty + 1
k = k + 1
Next
Next
End Sub
End Class
There is a new object (called tc) created on every iteration of the FOR loop, so tc.TestProperty is always 1. Why is this not the case with variable k i.e. the value of k increments by one on every iteration? I realise this is probably to do with how value types and reference types are dealt with, but I wanted to check.
It's because when something is defined as block level it applies to the entire block level, regardless of loops. normally with control logic like an IF block statement the scope starts and ends and no code lines repeat.
Inside a loop structure the variable is defined inside that block, even though the Dim statement appears to be called multiple times it is not, it is not actually an executable statement (just a definition and reservation of a placeholder as mentioned above in one comment)
To cause it to behave in the same way as "tc" you also need to initialize it in a similar way. (the assignment to 0 would occur each loop, not the definition)
Dim k As Integer = 0
Alternately if you change how your dealing with tc it would behave the same way as k where it is in block scope the entire time inside the loop. In the below example tc is not redefined each loop either.
Dim tc as TestClass
if tc is nothing then tc = New TestClass
You would have to Dim k As Integer = 0 to keep it at 1.
This is because Dim k As Integer retains it's value, while Dim k As Integer = 0 "declares and initializes" it.
Specifically: "If you alter the value but then return to the Dim statement, your altered value is replaced by the value supplied in the Dim statement."
Actually, I don't know why it doesn't seem go out of scope. Maybe without the New keyword it's using the same block of memory.
As implied by the title of this question, you're querying the scope versus the lifetime of the variable.
The scope of the local variables k and tc is the inner For loop. The lifetime is the whole of the Sub.
If you adjusted the tc = New TestClass to If tc Is Nothing Then tc = New TestClass (and ignored the warning that causes), you should then see the tc.TestProperty increment too.
"Dim k As Integer" isn't actually translate into any code except "space reservation" (that is surely made at compile time). So the application does not pass on that sentence 10 times.
As a matter of proof, you can not put a trace bullet on that line of code !
On the other hand, your code create on each loop a fresh new object TestClass (holding a brand new variable "TestProperty) and assign it to the variable "tc". The previous object is lost and carbage collected anytime soon.
I am getting the error "Format Exception was unhandled at "Do While objectReader.Peek <> -1
" and after. any help would be wonderful.
'Date: Class 03/20/2010
'Program Purpose: When code is executed data will be pulled from a text file
'that contains the named storms to find the average number of storms during the time
'period chosen by the user and to find the most active year. between the range of
'years 1990 and 2008
Option Strict On
Public Class frmHurricane
Private _intNumberOfHuricanes As Integer = 5
Public Shared _intSizeOfArray As Integer = 7
Public Shared _strHuricaneList(_intSizeOfArray) As String
Private _strID(_intSizeOfArray) As String
Private _decYears(_intSizeOfArray) As Decimal
Private _decFinal(_intSizeOfArray) As Decimal
Private _intNumber(_intSizeOfArray) As Integer
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'The frmHurricane load event reads the Hurricane text file and
'fill the combotBox object with the data.
'Initialize an instance of the StreamReader Object and declare variable page 675 on the book
Dim objectReader As IO.StreamReader
Dim strLocationAndNameOfFile As String = "C:\huricanes.txt"
Dim intCount As Integer = 0
Dim intFill As Integer
Dim strFileError As String = "The file is not available. Please restart application when available"
'This is where we code the file if it exist.
If IO.File.Exists(strLocationAndNameOfFile) Then
objectReader = IO.File.OpenText(strLocationAndNameOfFile)
'Read the file line by line until the file is complete
Do While objectReader.Peek <> -1
**_strHuricaneList(intCount) = objectReader.ReadLine()
_strID(intCount) = objectReader.ReadLine()
_decYears(intCount) = Convert.ToDecimal(objectReader.ReadLine())
_intNumber(intCount) = Convert.ToInt32(objectReader.ReadLine())
intCount += 1**
Loop
objectReader.Close()
'With any luck the data will go to the Data Box
For intFill = 0 To (_strID.Length - 1)
Me.cboByYear.Items.Add(_strID(intFill))
Next
Else
MsgBox(strFileError, , "Error")
Me.Close()
End If
End Sub
Put a break-point on these lines and step through your code:
_decYears(intCount) = Convert.ToDecimal(objectReader.ReadLine())
_intNumber(intCount) = Convert.ToInt32(objectReader.ReadLine())
Whatever is being returned by ReadLine isn't in the proper decimal or integer format when passed to the converters. You'll need to add some try/catch blocks around the do-while loop operations to handle it gracefully and make sure your data is formatted the way you expected since the wrong parts are being used in this scenario.
Also, each call to ReadLine is returning an entirely new line and the Peek check won't account for those. As long as you can trust your data that's fine.
Instead of using Convert.ToDecimal an Convert.ToInt32, try using Decimal.TryParse() and Int32.TryParse(). If they don't parse, you know your file isn't setup correctly. If they do parse, then you've loaded the value into a variable for your use.
EDIT:
Use it like this.
Dim myDecimal As Decimal
If Decimal.TryParse(objectReader.ReadLine(), myDecimal) Then
'your string successfully parsed. Use myDecimal however you want. It has the parsed value.
Else
'your string is not a decimal. Now that you know that, you can handle this situation here without having to catch an exception.
End If