System.ArgumentException: At least one object must implement IComparable - vb.net

In my MergeCollection class i have overide InsertItem method to check for specific case. Nevertheless when it comes to the line Items.Any(.. it throws me exception as below.
System.ArgumentException: 'At least one object must implement IComparable.'
Merge class:
Public Class Merge
Property Min As Integer
Property Max As Integer?
Property Value As Double
Public Sub New(min As Integer, max As Integer?, value As Integer)
Me.Min = min
Me.Max = max
Me.Value = value
End Sub
End Class
Public Enum SortCriteria
MinThenMax
MaxThenMin
End Enum
Some comparer:
Public Class MergeComparer
Implements IComparer(Of Merge)
Public SortBy As SortCriteria = SortCriteria.MinThenMax
Public Function Compare(x As Merge, y As Merge) As Integer Implements IComparer(Of Merge).Compare
'to be implemented
End Function
End Class
Collection class:
Public Class MergeCollection
Inherits Collection(Of Merge)
Public SortBy As SortCriteria = SortCriteria.MinThenMax
Protected Overrides Sub InsertItem(index As Integer, item As Merge)
if IsNothing(item.Max)
If Items.Any(Function(myObject) IsNothing(Items.Max)) Then
Return
End If
End If
MyBase.InsertItem(index, item)
End Sub
Public Sub Sort()
Dim allItems = Items.ToArray()
Array.Sort(allItems)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparison As Comparison(Of Merge))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparison)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparer As IComparer(Of Merge))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparer)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
End Class

The code asks for Items.Max. To find the Max value, it has to compare the Items to each other.
Public Class Merge
Implements IComparable(Of Merge)
Property Min As Integer
Property Max As Integer?
Property Value As Double
Public Sub New()
' empty constructor
End Sub
Public Sub New(min As Integer, max As Integer?, value As Integer)
Me.Min = min
Me.Max = max
Me.Value = value
End Sub
Public Overloads Function CompareTo(other As Merge) As Integer Implements IComparable(Of Merge).CompareTo
If other Is Nothing Then Return 1
' Could use
' Return (Me.Value).CompareTo(other.Value)
' instead of the following code...
If Me.Value > other.Value Then Return 1
If Me.Value = other.Value Then Return 0
Return -1
End Function
End Class
I guess that you want a different criterion for the comparison.

Related

How to assign typical NumericUpDown properties to custom NumericUpDown based control?

I'm trying to make a DataGridViewColumn that inherits all of the properties of a typical NumericUpDown control. I found an answer here on StackOverflow (https://stackoverflow.com/a/55788490/692250) and a MSDN article here (https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-host-controls-in-windows-forms-datagridview-cells?view=netframeworkdesktop-4.8&redirectedfrom=MSDN#code-snippet-1).
The code from the article works well, but... I cannot assign a Maximum nor Minimum value to the control (both are stuck at the "default" Minimum of 0 and Maximum of 100).
I tried to add in code from the SO answer (by adding in a Min and Max field) but doing so locks both in to 0 and unable to change.
The question in short: how do I add the necessary properties to allow to this work.
The custom control is question:
Public Class NumericUpDownColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New NumericUpDownCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If Not (value Is Nothing) AndAlso Not value.GetType().IsAssignableFrom(GetType(NumericUpDownCell)) Then
Throw New InvalidCastException("Must be an Integer")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Number Format
Me.Style.Format = "0"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing contol that CalendarCell uses.
Return GetType(NumericUpDownEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(Decimal)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
' Use the current date and time as the default value.
Return 0
End Get
End Property
End Class
Class NumericUpDownEditingControl
Inherits NumericUpDown
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
Me.DecimalPlaces = 0
End Sub
Public Property EditingControlFormattedValue() As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToString("#")
End Get
Set(ByVal value As Object)
If TypeOf value Is Decimal Then
Me.Value = Decimal.Parse(value.ToString)
End If
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToString("#")
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return False
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Focusing on it a bit more, I found that I can assign values to the Maximum and Minimum fields as so:
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
ctl.Maximum = 180
ctl.Minimum = -1
End Sub
But I cannot seem to pass to this field via the usual means of .Maximum or .Minimum on the constructor. Any tips/advice?
This is an easy fix and very easy to overlook.
In your NumericUpDownCell class you need to override the Clone method to include the new Minimum and Maximum properties, for example:
First, add the new properties the NumericUpDownCell class and then override the Clone function
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
...
Public Property Minimum() As Integer
Public Property Maximum() As Integer
...
Public Overrides Function Clone() As Object
Dim retVal As NumericUpDownCell = CType(MyBase.Clone, NumericUpDownCell)
retVal.Minimum = Me.Minimum
retVal.Maximum = Me.Maximum
Return retVal
End Function
End Class
Second, inside the NumericUpDownCell classes InitializeEditingControl method, add the two lines:
ctl.Minimum = Me.Minimum
ctl.Maximum = Me.Maximum
When you setup the new column, get the CellTemplate to set the new properties, as per:
Dim upDownColumn As New NumericUpDownColumn
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn .CellTemplate, NumericUpDownCell)
cellTemplate.Minimum = 10
cellTemplate.Maximum = 15
Or, following you preference to setup via the constructor, add a new constructor to the NumericUpdDownColumn class as per
Public Sub New(minValue As Integer, maxValue As Integer)
MyBase.New(New NumericUpDownCell())
Dim template As NumericUpDownCell = CType(CellTemplate, NumericUpDownCell)
template.Minimum = minValue
template.Maximum = maxValue
End Sub
and then use it like:
Dim upDownColumn As New NumericUpDownColumn(100, 150)
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn.CellTemplate, NumericUpDownCell)
DataGridView1.Columns.Add(upDownColumn)

Diffrence between Collection<T> and custom implement class

Some people advice to inherit from Collection to get collection class. Some other people advice to have class and implement interfaces from scratch. I would like to understand when to use one over another.
I see that when i use:
class MyCollection
Inherits Collection(Of SomeObject)
I have possibility to add, insert, for each etc that's probably because Collection<T> uses internally List<T>
However if i just do:
class MyCollection : IList(Of SomeObject), IEnumerable<SomeObject>, IEnumerable(Of SomeObject)
myList As List(Of SomeObject)
i can also implement such things like Add(), Remove(), for each
Is it like it's good to use Collection(Of T) because it's already contains all of this implemented interfaces and inner List(Of T) rather that implementing all interfaces myself in self class from scratch? Is it the point people advice to inherit from COllection(Of T)?
EDIT (for further discussion):
Public Class Merge
Property Size As Integer
Property Datee As Date
Property Min As Integer
Property Max As Integer?
Property Value As Double
Public Sub New(min As Integer, max As Integer?, value As Integer)
Me.Min = min
Me.Max = max
Me.Value = value
End Sub
End Class
Public Enum SortCriteria
MinThenMax
MaxThenMin
End Enum
Public Class MergeComparer
Implements IComparer(Of Merge) 'do oddzielnej klasy sortowania obiektu jak tutaj potrzebujemy IComparer a nie IComparable (ten jest bezposrednio na klasie)
Public SortBy As SortCriteria = SortCriteria.MinThenMax
Public Function Compare(x As Merge, y As Merge) As Integer Implements IComparer(Of Merge).Compare
'to be implemented
End Function
End Class
Public Class MergeCollection
Inherits Collection(Of Merge)
Public SortBy As SortCriteria = SortCriteria.MinThenMax
''' <summary>
''' Ovveride because
''' There could be only one item on list which contains Max prop = Nothing
''' </summary>
''' <param name="index"></param>
''' <param name="item"></param>
Protected Overrides Sub InsertItem(index As Integer, item As Merge)
if IsNothing(item.Max)
If Items.Any(Function(myObject) IsNothing(Items.Max)) Then
Return
End If
End If
MyBase.InsertItem(index, item)
End Sub
Public Sub Sort()
Dim allItems = Items.ToArray()
Array.Sort(allItems)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparison As Comparison(Of Merge))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparison)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparer As IComparer(Of Merge))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparer)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
End Class
Here's an example of a custom collection that implements Collection(Of T) and then adds it's own Sort method:
Public Class StringCollection
Inherits Collection(Of String)
Public Sub Sort()
Dim allItems = Items.ToArray()
Array.Sort(allItems)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
End Class
Sample usage:
Dim strings As New StringCollection
strings.Add("Peter")
strings.Add("Paul")
strings.Add("Mary")
strings.Sort()
For Each s In strings
Console.WriteLine(s)
Next
That Sort method relies on the IComparable implementation of the items themselves. If you want to support other sorting methods or your items don't implement IComparable then you can implement different Sort methods, e.g.
Public Class StringCollection
Inherits Collection(Of String)
Public Sub Sort()
Dim allItems = Items.ToArray()
Array.Sort(allItems)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparison As Comparison(Of String))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparison)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
End Class
Sample usage:
Dim strings As New StringCollection
strings.Add("Peter")
strings.Add("Paul")
strings.Add("Mary")
strings.Sort(Function(a, b) a.Length.CompareTo(b.Length))
For Each s In strings
Console.WriteLine(s)
Next
In that case, we're explicitly comparing the String items by their Length rather than their implicit "alphabetic" IComparable implementation.
You can include an overload of Sort for every relevant overload of Array.Sort and/or you can provide your own explicit comparisons if you want. Note that one of those overloads takes an IComparer. Here's how you might make use of that:
Public Class StringCollection
Inherits Collection(Of String)
Public Sub Sort()
Dim allItems = Items.ToArray()
Array.Sort(allItems)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparison As Comparison(Of String))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparison)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
Public Sub Sort(comparer As IComparer(Of String))
Dim allItems = Items.ToArray()
Array.Sort(allItems, comparer)
For i = 0 To allItems.GetUpperBound(0)
Items(i) = allItems(i)
Next
End Sub
End Class
Here's an example comparer:
Public Class StringLengthComparer
Implements IComparer, IComparer(Of String)
Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
Return Compare(CStr(x), CStr(y))
End Function
Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
Return x.Length.CompareTo(y.Length)
End Function
End Class
Sample usage:
Dim strings As New StringCollection
strings.Add("Peter")
strings.Add("Paul")
strings.Add("Mary")
Dim comparer As New StringLengthComparer
strings.Sort(comparer)
For Each s In strings
Console.WriteLine(s)
Next
We're sorting the items by their Length again but, this time, we're using an object that implements IComparer to make the comparisons rather than a Comparison(Of T) delegate. You should notice, though, that the Compare method of the IComparer in this last example takes pretty much the exact same form as the Lambda used to create the Comparison(Of T) delegate in the previous example.

How to make Arrays of Object in VB.net?

I am unable to debug this.
Is my procedure of array-making wrong?
The error it shows is
System.NullReferenceException :'Object reference not set to an instance of an object'
Module Module1
Class Toy
Private Name, ID As String
Private Price As Single
Private MinimumAge As Integer
Dim Count As Integer
Public Sub New()
End Sub
Public Sub SetName(ByVal N As String)
Name = N
End Sub
Public Sub SetID(ByVal I As String)
'VALIDATION CHECK FOR ID !
While Len(I) < 4
Console.WriteLine("Kindly Enter the ID with Lenght of Max 4 Characters")
End While
ID = I
End Sub
Public Sub SetPrice(ByVal SP As Single)
Price = SP
End Sub
Public Sub SetUserAge(ByVal SM As Integer)
'VALIDATION CHECK FOR AGE
While SM < 0 Or SM < 18
Console.WriteLine("Minimum age is 18")
End While
MinimumAge = SM
End Sub
Public Function GetName()
Return Name(Count)
End Function
Public Function GetID()
Return ID
End Function
Public Function GetPrice()
Return Price
End Function
Public Function GetAge()
Return MinimumAge
End Function
End Class
Class ComputerGame
Inherits Toy
Private Category, Consol As String
Public Sub New()
End Sub
Public Sub SetConsole(ByVal C As String)
While C <> "PS4" Or C <> "XBOX"
Console.WriteLine("Invalid console entered")
End While
Consol = C
End Sub
Public Sub SetCategory(ByVal SC As String)
Category = SC
End Sub
Public Function GetConsole()
Return Consol
End Function
Public Function GetCategory()
Return Category
End Function
End Class
Class Vehicle
Inherits Toy
Private Type As String
Private Length, Height, Weight As Single
Public Sub New()
End Sub
Public Sub SetType(ByVal TY As String)
While TY <> "Car" Or TY <> "Bus" Or TY <> "Truck"
Console.WriteLine("vehicle is not in list !")
End While
Type = TY
End Sub
Public Sub SetLength(ByVal SL As Single)
Length = SL
End Sub
Public Sub SetHeight(ByVal SH As Single)
Height = SH
End Sub
Public Sub SetWeight(ByVal SW As Integer)
Weight = SW
End Sub
Public Function GetT()
Return Type
End Function
Public Function GetLength()
Return Length
End Function
Public Function GetHeight()
Return Height
End Function
Public Function GetWeight()
Return Weight
End Function
End Class
Sub Main()
Dim ThisGame(6) As Toy
ThisGame(6) = New Toy
ThisGame(1).SetID("23456a")
ThisGame(2).SetID("236789b")
Console.WriteLine(ThisGame(1).GetID)
Console.ReadKey()
End Sub
End Module
This is what your Toy class should look like using the long method of Properties. Notice that you have Public Properties and a private field to store the values (sometimes called a backer field) Each Property has a Get and a Set procedure where you can add your validation code.
Public Class Toy
Private _Name As String
Public Property Name As String
Get
Return _Name
End Get
Set(value As String)
_Name = value
End Set
End Property
Private _ID As String
Public Property ID As String
Get
Return _ID
End Get
Set(value As String)
If value.Length < 4 Then
Console.WriteLine("Kindly Enter the ID with Lenght of Max 4 Characters")
Exit Property
End If
_ID = value
End Set
End Property
Private _Price As Single
Public Property Price As Single
Get
Return _Price
End Get
Set(value As Single)
_Price = value
End Set
End Property
Private _MinimumAge As Integer
Public Property MinimumAge As Integer
Get
Return _MinimumAge
End Get
Set(value As Integer)
If value < 0 Or value < 18 Then
Console.WriteLine("Minimum age is 18")
Exit Property
End If
_MinimumAge = value
End Set
End Property
Private _Count As Integer
Public Property Count As Integer
Get
Return _Count
End Get
Set(value As Integer)
_Count = value
End Set
End Property
End Class
Current versions have Automatic Properties where the compiler provides the getter, setter, and backer fields. Notice that you can still write your own Get and Set where you need to add code.
Public Class Toy
Public Property Name As String
Public Property Price As Single
Public Property Count As Integer
Private _ID As String
Public Property ID As String
Get
Return _ID
End Get
Set(value As String)
If value.Length < 4 Then
Console.WriteLine("Kindly Enter the ID with Lenght of Max 4 Characters")
Exit Property
End If
_ID = value
End Set
End Property
Private _MinimumAge As Integer
Public Property MinimumAge As Integer
Get
Return _MinimumAge
End Get
Set(value As Integer)
If value < 0 Or value < 18 Then
Console.WriteLine("Minimum age is 18")
Exit Property
End If
_MinimumAge = value
End Set
End Property
End Class
Please read the in line comments in regards to your array.
Sub Main()
Dim ThisGame(6) As Toy 'You are defining and array with 7 elements of Toy
'Each element is Nothing at this point and you will get a null exception error if you
'try to access an element.
ThisGame(6) = New Toy 'Here you have created an instance of your class for the 7th element
'The other six elements are still nothing so the next 3 lines will get
'NullReferenceException: 'Object reference not set to an instance of an object.'
'ThisGame(1).ID = "23456a"
'ThisGame(2).ID = "236789b"
ThisGame(1) = New Toy
ThisGame(1).ID = "23456a"
ThisGame(2) = New Toy
ThisGame(2).ID = "236789b"
'Remember the first element in you array is ThisGame(0)
Console.WriteLine(ThisGame(1).ID)
Console.ReadKey()
End Sub

VB.Net check for existing keys in a collection

I have a class that inherits from the CollectionBase and when adding items I want to detect whether the collection already contains the key that is going to be inserted. If it does I want to send a warning via a MsgBox(). Here is the code & what I've tried
<Serializable()> Public Class validationList
Inherits CollectionBase
Public Function Add(ByVal Item As validationItem) As Integer
MsgBox(Me.List.Contains(Item))
Return Me.List.Add(Item)
End Function
Default Public ReadOnly Property Item(ByVal index As Integer) As validationItem
Get
Return CType(List.Item(index), validationItem)
End Get
End Property
Public Sub Remove(ByVal index As Integer)
Me.List.RemoveAt(index)
End Sub
Public Function IndexOf(ByVal key As validationItem)
Return List.IndexOf(key)
End Function
Public Sub AddRange(ByVal item() As validationItem)
For counter As Integer = 0 To item.GetLength(0) - 1
List.Add(item(counter))
Next
End Sub
End Class
<Serializable()> Public Class validationItem
Private _key As validationTypes
Private _value As String
Public Enum validationTypes
man = 0
num = 1
End Enum
Public Property Value As String
Get
Return _value
End Get
Set(ByVal Value As String)
_value = Value
End Set
End Property
Public Property Key As validationTypes
Get
Return _key
End Get
Set(ByVal value As validationTypes)
_key = value
End Set
End Property
Public Sub New()
' Empty constructor is needed for serialization
End Sub
Public Sub New(ByVal k As validationTypes, ByVal v As String)
_key = k
_value = v
End Sub
End Class
Public Class textbox
Inherits System.Windows.Forms.TextBox
Private _validation As New validationList
<System.ComponentModel.DesignerSerializationVisibility(Content)>
Public Property validation As validationList
Get
Return _validation
End Get
Set(ByVal value As validationList)
_validation = value
End Set
End Property
End Class
In the add method I tried to check whether the collection already has this item. But it always returns -1.
Here is code that adds a new item to the collection
Textbox1.validation.Add(New validationItem With {.Key = validationItem.validationTypes.man, .Value = "1"})
To make Contains work, you'll have to implement Equals/GetHashCode on validationItem or implement the IEquatable(Of T) interface:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable(Of T).Equals method for T (the type of values in the list).
Here's an example implementation for Equals/GetHashCode that checks both, Key and Value:
<Serializable> _
Public Class validationItem
Protected Overloads Function Equals(other As validationItem) As Boolean
Return _value = other._value AndAlso _key = other._key
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If obj Is Nothing Then
Return False
End If
If Me Is obj Then
Return True
End If
If obj.GetType() IsNot Me.GetType() Then
Return False
End If
Return Equals(DirectCast(obj, validationItem))
End Function
Public Overrides Function GetHashCode() As Integer
Return ((If(_value IsNot Nothing, _value.GetHashCode(), 0)) * 397) Xor CInt(_key)
End Function
...
End Class
You could also use use LINQ, here's an example that only checks for Key:
Public Function Add(ByVal Item As validationItem) As Integer
If Me.List.OfType(Of validationItem).Any(Function(i) i.Key = Item.Key) Then
' Do something '
Else
Return Me.List.Add(Item)
End If
End Function
You need to just check for whether the key exist or not and only show it if it exists:
Public Function Add(ByVal Item As validationItem) As Integer
If Me.List.Contains(Item) Then MsgBox("The key already exists")
Return Me.List.Add(Item)
End Function
As it stands you are just returning the result of the Contains method which is a Boolean (hence the -1)

Populating a combo box with a list of functions - Need Advice

I'm looking for some advice on the best way to handle this.
I have a list of about 200 "Functions" which are listed in a combo box. When the user selects a 'function' from the list, I need to return the functionID (integer).
I know this can be done easily by binding a dataset to the key and value of the combobox, I'm just not sure about the best way to populate the dataset.
I feel that the way I'm doing it currently is very convoluted:
I currently have a txt file as an embedded resource which I write to a temporary directory, then I use the following code to read in that text file and populate that box by setting the combobox's datasource and Display Member. It does this by way of a custom class which is implementing System.Collections.IList.
I have pasted the code below. The reason I want to simplify it is that I dislike writing the text file to the disk, because sometimes it fails.
I'm looking for a way to populate my combobox and return my ID, without writing anything to the user's temp folder.
I am open to changing the format of the embedded resource, and or the code.
The fnlist.txt is formatted currently as follows.
index, Function Name, ID
The index is only included for sorting (to keep NONE at the bottom, and unknown function at the top), and I suppose is not strictly required.
#Region "Function lookup"
Dim path As String = System.IO.Path.GetTempPath
Dim _objFnXtef As New clsFunctionXref(path & "fnList.txt")
Private Sub populate_list()
functionlist.DataSource = _objFnXtef
functionlist.DisplayMember = "StrFunction"
End Sub 'Populates the function list
Function get_index(ByVal fnid As Integer)
Dim iLookupNumber As Integer = fnid
Dim tmpFnInfo As New clsFunctionInfo
Dim iReturnIdx As Integer = -1
If iLookupNumber <> 0 Then
tmpFnInfo.IFunctionNumber = iLookupNumber
iReturnIdx = _objFnXtef.IndexOf(tmpFnInfo)
If iReturnIdx <> -1 Then
Return iReturnIdx - 1
Else
Return get_index(9999)
End If
End If
Return 0
End Function 'Returns index of specified function number
#End Region 'All function list functions
Here is the code when a user changes the drop down:
Private Sub functionlist_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles functionlist.SelectedIndexChanged
Dim iReturnFuctionID As Integer = 0
Dim tmpFnInfo As New clsFunctionInfo
tmpFnInfo = _objFnXtef(functionlist.SelectedIndex)
iReturnFuctionID = tmpFnInfo.IFunctionNumber
Func = (iReturnFuctionID)
End Sub
And here is the supporting class:
Imports Microsoft.VisualBasic.FileIO
Public Class clsFunctionInfo
Private _idxFunction As Integer
Public Property IdxFunction() As Integer
Get
Return _idxFunction
End Get
Set(ByVal value As Integer)
_idxFunction = value
End Set
End Property
Private _strFunction As String
Public Property StrFunction() As String
Get
Return _strFunction
End Get
Set(ByVal value As String)
_strFunction = value
End Set
End Property
Private _iFunctionNumber As Integer
Public Property IFunctionNumber() As Integer
Get
Return _iFunctionNumber
End Get
Set(ByVal value As Integer)
_iFunctionNumber = value
End Set
End Property
End Class
Public Class clsFunctionXref
Implements System.Collections.IList
Private _colFunctionInfo As New Collection
Private _filePath As String
Public Property FilePath() As String
Get
Return _filePath
End Get
Set(ByVal value As String)
_filePath = value
End Set
End Property
Public Sub New(ByVal filename As String)
_filePath = filename
Dim _idx As Integer = 1
Dim fields As String()
Dim delimiter As String = ","
Dim iFnx As Integer
Using parser As New TextFieldParser(filename)
parser.SetDelimiters(delimiter)
While Not parser.EndOfData
' Read in the fields for the current line
fields = parser.ReadFields()
Try
iFnx = Convert.ToInt16(fields(0).ToString)
Catch ex As Exception
MessageBox.Show("Error reading file. " & ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
Dim objFunction As New clsFunctionInfo
objFunction.IdxFunction = _idx
objFunction.IFunctionNumber = iFnx
objFunction.StrFunction = fields(1).ToString
Me.Add(objFunction)
_idx += 1
End While
End Using
End Sub
Public Function Add(ByVal value As Object) As Integer Implements System.Collections.IList.Add
If _colFunctionInfo.Contains(value.IFunctionNumber.ToString) Then
SyncLock Me.SyncRoot
_colFunctionInfo.Remove(value.IFunctionNumber.ToString)
End SyncLock
ReIndex()
End If
SyncLock Me.SyncRoot
_colFunctionInfo.Add(value, value.IFunctionNumber.ToString)
End SyncLock
End Function
Public Sub Clear() Implements System.Collections.IList.Clear
SyncLock Me.SyncRoot
_colFunctionInfo.Clear()
End SyncLock
End Sub
Public Function Contains(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
If _colFunctionInfo.Contains(value.IFunctionNumber.ToString) Then
Return True
Else
Return False
End If
End Function
Public ReadOnly Property Count() As Integer Implements System.Collections.ICollection.Count
Get
Return _colFunctionInfo.Count
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.IList.IsReadOnly
Get
Return False
End Get
End Property
Public Sub Remove(ByVal value As Object) Implements System.Collections.IList.Remove
If _colFunctionInfo.Contains(value.IFunctionNumber.ToString) Then
SyncLock Me.SyncRoot
_colFunctionInfo.Remove(value.IFunctionNumber.ToString)
End SyncLock
ReIndex()
End If
End Sub
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return _colFunctionInfo.GetEnumerator
End Function
Public Sub Insert(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
SyncLock Me.SyncRoot
If _colFunctionInfo.Contains(value.IFunctionNumber.ToString) Then
_colFunctionInfo.Remove(value.IFunctionNumber.ToString)
End If
If index < _colFunctionInfo.Count Then
_colFunctionInfo.Add(value, value.IFunctionNumber.ToString, index - 1)
Else
_colFunctionInfo.Add(value, value.IFunctionNumber.ToString)
End If
End SyncLock
ReIndex()
End Sub
Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
SyncLock Me.SyncRoot
If _colFunctionInfo.Count <= index And index > 0 Then
_colFunctionInfo.Remove(index)
End If
End SyncLock
ReIndex()
End Sub
Private Sub ReIndex()
SyncLock Me.SyncRoot
Dim iReIndex As Integer = 1
Dim colTemp As New Collection
For Each obj As clsFunctionInfo In _colFunctionInfo
obj.IdxFunction = iReIndex
colTemp.Add(obj, obj.IFunctionNumber)
iReIndex += 1
Next
_colFunctionInfo.Clear()
For Each obj1 As clsFunctionInfo In colTemp
_colFunctionInfo.Add(obj1, obj1.IFunctionNumber.ToString)
Next
colTemp.Clear()
End SyncLock
End Sub
Public ReadOnly Property IsSynchronized() As Boolean Implements System.Collections.ICollection.IsSynchronized
Get
Return True
End Get
End Property
Public ReadOnly Property SyncRoot() As Object Implements System.Collections.ICollection.SyncRoot
Get
Dim _syncRoot As New Object
Return _syncRoot
End Get
End Property
Public ReadOnly Property IsFixedSize() As Boolean Implements System.Collections.IList.IsFixedSize
Get
Return False
End Get
End Property
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo
For Each obj As clsFunctionInfo In _colFunctionInfo
array(index) = obj
index += 1
Next
End Sub
Public Function IndexOf(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
SyncLock Me.SyncRoot
Dim tmpFnInfo As New clsFunctionInfo
Dim tmpFunctionNumber As Integer
Dim tmpidx As Integer = -1
tmpFnInfo = DirectCast(value, clsFunctionInfo)
tmpFunctionNumber = tmpFnInfo.IFunctionNumber
For Each obj In _colFunctionInfo
tmpFnInfo = DirectCast(obj, clsFunctionInfo)
If tmpFunctionNumber = tmpFnInfo.IFunctionNumber Then
tmpidx = tmpFnInfo.IdxFunction
Exit For
End If
Next
Return tmpidx
End SyncLock
End Function
Default Public Property Item(ByVal index As Integer) As Object Implements System.Collections.IList.Item
Get
index += 1
Return _colFunctionInfo(index)
End Get
Set(ByVal value As Object)
End Set
End Property
End Class
I'm sorry that this is so long, but I know that someone on here has some great suggestions on how to handle this because I'm having a little trouble wrapping my head around it. I think I've been starring at it too long.
since you have the text file as an embedded resource, you can open a stream to the file from there, without having to write it to disk. The ResourceReader class should help you.