TheSwamp
Code Red => .NET => Topic started by: Keith™ on November 16, 2009, 12:54:31 PM
-
I have designed a private collection to hold filter values. This collection is simply a bunch of class objects that hold other data .... it is built from reading a file, as shown below.
Public Class Utility
Private mFilterCollection As New Collection
Private Sub LoadFilterList()
'Open the file stream
Dim fs As IO.FileStream = New IO.FileStream(My.Application.Info.DirectoryPath & "/file.flt", IO.FileMode.OpenOrCreate, IO.FileAccess.Read)
Dim sr As New IO.StreamReader(fs)
Dim N As Integer
Dim mString As String
Dim mFilter As SelectionFilterCollection
Dim varValues As String()
'Seek the beginning of the file
sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
'Create our class to hold the filter information
'this is necessary at this point to aleviate
'a warning that an uninitialized variable may
'cause an error
mFilter = New SelectionFilterCollection
While sr.EndOfStream <> True
mString = sr.ReadLine()
'If we have a header i.e. filter name
If mString.Substring(0, 3) = "***" Then
'Create a new SelectionFilterCollection to hold it
'recycling the previous mFilter
'We need to do this each time a new filter is found
'in the file stream
mFilter = New SelectionFilterCollection
'Add the filter name to the combo box
Me.ComboBox1.Items.Add(mString.Substring(3))
'Set the Filter collection name to the same name so we
'can identify it later when it is selected in the
'ComboBox
mFilter.Name = mString.Substring(3)
Else
'We don't have a filter header, so we must have filter
'information in tab delimited format
'so split it into the 3 parts
varValues = Split(mString, vbTab)
'and add those parts to our filter items
'See the SelectionFilterCollection Class for information on its structure
mFilter.Add(varValues(0), varValues(1), varValues(2))
End If
'If we have anything in the collection already ...
If mFilterCollection.Count > 0 Then
'make sure it isn't duplicated ...
If mFilterCollection.Item(mFilter.Name.ToString) Is Nothing Then
'then add it if needed
mFilterCollection.Add(mFilter, mFilter.Name)
End If
Else
'otherwise just add it
mFilterCollection.Add(mFilter, mFilter.Name)
End If
End While
'release the filter
mFilter = Nothing
'close the file stream
sr.Close()
End Sub
End Class
Class SelectionFilterCollection
'This class is designed to hold multiple
'block filters as an array of items
'FilterItem is a class that holds a block name
'attribute tag and a segregation value
'Define public members
Public Name As String
Public Item As FilterItem()
'New method
Public Sub New()
Name = ""
End Sub
'Add method
Public Function Add(ByVal BlockName As String, ByVal Attribute As String, ByVal Segregate As String) As FilterItem
If Item Is Nothing = True Then
ReDim Item(0)
Else
ReDim Preserve Item(UBound(Item) + 1)
End If
Dim NItem As New FilterItem
NItem.BlockName = BlockName
NItem.Attribute = Attribute
NItem.Segregate = Segregate
Item(UBound(Item)) = NItem
Return NItem
End Function
'Count method
Public Function Count() As Integer
If Item Is Nothing = True Then
Return 0
Else
Return (UBound(Item) + 1)
End If
End Function
End Class
Class FilterItem
'This class is designed to hold a block name
'an attribute associated with that block and
'whether or not the blocks should be identified
'seperately based on the attribute value
'Define public members
Public BlockName As String
Public Attribute As String
Public Segregate As String
'New method
Public Sub New()
BlockName = ""
Attribute = ""
Segregate = ""
End Sub
End Class
This part seems to work correctly, as there are no errors generated (I removed all error checking to ensure it would break when running though the paces ...
However, when I change the combobox value (I added all of the filters in the above code) it crashes when I select the last item in the collection ...
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim mFilter As SelectionFilterCollection
Dim mListItem As ListViewItem
'If we don't have an unnamed filter
'and if we are not saving additional
'data to the collection
If IsUnnamed() = False And mSavingData = False Then
'Clear the listview showing the filter criteria
If Me.ListView1.Items.Count > 0 Then
Me.ListView1.Items.Clear()
End If
'Set the filter collection to the item referenced
'in the collection, searching by "key name" (override 2)
mFilter = mFilterCollection.Item(Me.ComboBox1.Text)
'If we have a filter
If mFilter Is Nothing = False Then
'loop through each one in the collection of items
For Each fItem As FilterItem In mFilter.Item
'putting the data in the listview
mListItem = Me.ListView1.Items.Add(fItem.BlockName)
mListItem.SubItems.Add(fItem.Attribute)
mListItem.SubItems.Add(fItem.Segregate)
'release the list item
mListItem = Nothing
Next
End If
End If
End Sub
The program crashes and burns here saying that the object is not set .. I am not sure why ...
mFilter = mFilterCollection.Item(Me.ComboBox1.Text)
but only if I select the last item in the collection ... if there are 4 items, I can select the first three without a problem
Ideas?
Am I not putting the last item in the collection when I build it?
Is there an easier way?
Ok .. ignore that last question ... sure there is an easier way ... 'cause I ain't using it
-
At the first glance the problem is here:
Dim mListItem As ListViewItem
must be
Dim mListItem As ListViewItem = New ListViewItem
Not tested of course
~'J'~
-
that shouldn't the issue ... otherwise it wouldn't work on the first few items
Incidently, I added alot of error checking to the file reading and the problem seems to be that the last item isn't added for some reason.
It seems to fail on this bit ...
If mFilterCollection.Item(mFilter.Name.ToString) Is Nothing Then
mFilterCollection.Add(mFilter, mFilter.Name)
End If
Evidently, I cannot search the collection for a key ... is this correct?
-
That is NOT a problem: mListItem should not be declares as "New", because in the following code, this variable is assigned to a ListViewItem that is created by ListView.Items.Add() method.
To the OP:
The code should fail on the line
If mFilterCollection.Item(mFilter.Name.ToString) Is Nothing Then
mFilterCollection.Add(mFilter, mFilter.Name)
End If
You did not indicate what type of collection the mFilterCollection is (a Dictionary, most likely?). Anyway, as standard KeyedCollection's behaviour when a non-existing key is passed into its Item() method, it raises "KeyNotFoundException", not returns Nothing, as your code assumes, unless your collection overides the Item() method and specifically handled KeyNotFoundException and returns Nothing.
So, your code should be (assume it is a Dictionary)
If mFilterCollection.ContainsKey(TheKey) Then
''The collection has already an item with that key in it
Else
''The collection does not have a record with the given key
End
At the first glance the problem is here:
Dim mListItem As ListViewItem
must be
Dim mListItem As ListViewItem = New ListViewItem
Not tested of course
~'J'~
-
n.yuan , thanks .. that solved the problem
The collection is a generic collection type in VB.Net and not an AutoCAD collection. I use it to hold filter settings so the user can select them from a combobox ...
-
That is NOT a problem: mListItem should not be declares as "New", because in the following code, this variable is assigned to a ListViewItem that is created by ListView.Items.Add() method.
To the OP:
The code should fail on the line
If mFilterCollection.Item(mFilter.Name.ToString) Is Nothing Then
mFilterCollection.Add(mFilter, mFilter.Name)
End If
You did not indicate what type of collection the mFilterCollection is (a Dictionary, most likely?). Anyway, as standard KeyedCollection's behaviour when a non-existing key is passed into its Item() method, it raises "KeyNotFoundException", not returns Nothing, as your code assumes, unless your collection overides the Item() method and specifically handled KeyNotFoundException and returns Nothing.
So, your code should be (assume it is a Dictionary)
If mFilterCollection.ContainsKey(TheKey) Then
''The collection has already an item with that key in it
Else
''The collection does not have a record with the given key
End
At the first glance the problem is here:
Dim mListItem As ListViewItem
must be
Dim mListItem As ListViewItem = New ListViewItem
Not tested of course
~'J'~
Sorry, my bad
Thanks for the explanations
~'J'~
-
I know you received an answer, but I would also suggest putting all of your logic into your specialized collection classes.
Right now you have business logic in your UI; this makes it difficult to re-use your collection. Here are three classes that pretty much handle what the UI was taking care of. You’ll notice that the add method of the FilterCollection now can determine if there is already one in the collection. I also overloaded the Item property to allow for a string to be passed in returning a matching object from the collection, if there is one. Other than that, your original two classes are still pretty much the same.
Maybe turn OptionStrict On as well.
Public Class FilterCollection
Inherits List(Of SelectionFilterCollection)
Private Shared _NameMatch As String = ""
Public Shadows Sub Add(ByVal item As SelectionFilterCollection)
If Not Me.Contains(item) Then
MyBase.Add(item)
Else
'do what ever
End If
End Sub
Public Sub MakeCollection(ByVal FullFileName As String)
Using fs As IO.FileStream = New IO.FileStream(FullFileName, IO.FileMode.OpenOrCreate, IO.FileAccess.Read)
Using sr As New IO.StreamReader(fs)
Dim mString As String
Dim mFilter As SelectionFilterCollection
Dim varValues As String()
'Seek the beginning of the file
sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
'Create our class to hold the filter information
'this is necessary at this point to aleviate
'a warning that an uninitialized variable may
'cause an error
mFilter = New SelectionFilterCollection
While sr.EndOfStream <> True
mString = sr.ReadLine()
'If we have a header i.e. filter name
If mString.Substring(0, 3) = "***" Then
'Create a new SelectionFilterCollection to hold it
'recycling the previous mFilter
'We need to do this each time a new filter is found
'in the file stream
mFilter = New SelectionFilterCollection
'Set the Filter collection name to the same name so we
'can identify it later when it is selected in the
'ComboBox
mFilter.Name = mString.Substring(3)
Else
'We don't have a filter header, so we must have filter
'information in tab delimited format
'so split it into the 3 parts
varValues = Split(mString, vbTab)
'and add those parts to our filter items
'See the SelectionFilterCollection Class for information on its structure
mFilter.Add(varValues(0), varValues(1), varValues(2))
End If
'If we have anything in the collection already ...
Me.Add(mFilter)
End While
'release the filter
mFilter = Nothing
'close the file stream
sr.Close()
End Using
End Using
End Sub
Default Public Overloads ReadOnly Property Item(ByVal Name As String) As SelectionFilterCollection
Get
_NameMatch = name
Return Me.Find(AddressOf MatchName)
End Get
End Property
Private Shared Function MatchName(ByVal item As SelectionFilterCollection) As Boolean
Return item.Name = _NameMatch
End Function
End Class
Public Class SelectionFilterCollection
Inherits List(Of FilterItem)
Implements IEquatable(Of SelectionFilterCollection)
Private _Name As String = ""
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Public Function DoSomething() As Boolean
Return False
End Function
'Add method
Public Overloads Function Add(ByVal BlockName As String, ByVal Attribute As String, ByVal Segregate As String) As FilterItem
Dim NItem As New FilterItem
NItem.BlockName = BlockName
NItem.Attribute = Attribute
NItem.Segregate = Segregate
Me.Add(NItem)
Return NItem
End Function
Public Overloads Function Equals(ByVal other As SelectionFilterCollection) As Boolean Implements System.IEquatable(Of SelectionFilterCollection).Equals
Return Me.Name.Equals(other.Name)
End Function
End Class
Public Class FilterItem
Private _BlockName As String = ""
Private _Attribute As String = ""
Private _Segregate As String = ""
Public Property Attribute() As String
Get
Return _Attribute
End Get
Set(ByVal value As String)
_Attribute = value
End Set
End Property
Public Property BlockName() As String
Get
Return _BlockName
End Get
Set(ByVal value As String)
_BlockName = value
End Set
End Property
Public Property Segregate() As String
Get
Return _Segregate
End Get
Set(ByVal value As String)
_Segregate = value
End Set
End Property
End Class