|
Accessories for Collections
|
|
One the most routines operations performed on a database consists of reviewing its
values. To assist you with this, the .NET Framework
provides the IEnumerator and the IEnumerable interfaces that are defined in the System.Collections namespace.
Their generic equivalences can be found in the System.Collections.Generic
namespace. After implementing these interfaces, you can use the For Each operator to
visit each value of the database.
To implement the System.Collections.IEnumerator interface, you must
derive a class from it. Then, you must define the Reset(), the MoveNext()
methods, and the Current property. Here is an example:
Imports System.ComponentModel
Public Class Enumerator
Implements IEnumerator
Private Names() As String
Private Cur As Integer
Public Sub New(ByVal lst() As String)
Names = lst
Cur = -1
End Sub
Public ReadOnly Property Current() Implements IEnumerator.Current
Get
Return Names(Cur)
End Get
End Property
Public Sub Reset() Implements IEnumerator.Reset
Cur = -1
End Sub
Public Function Movenext() As Boolean Implements IEnumerator.MoveNext
Cur = Cur + 1
If Cur < Names.Length Then
Return True
Else
Return False
End If
End Function
End Class
To implement the System.Collections.IEnumerable interface, you must
derive a class from it. When implementing the class, you must define an
accessory method and the GetEnumerator() method that returns an IEnumerator
object. Here is an example:
Imports System.Collections
Public Class Enumerable
Implements IEnumerable
Private Names() As String
Public Sub Identify(ByVal Values() As String)
Names = Values
Dim i As Integer
For i = 0 To Values.Length - 1
Names(i) = Values(i)
Next
End Sub
Public Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New Enumerator(Names)
End Function
End Class
Once you have implemented the interfaces, you can use For
Each. Here is an example:
Public Class Exercise
Private Sub Exercise_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
Dim strName As String
Dim FullNames(7) As String
FullNames(0) = "Gertrude Monay"
FullNames(1) = "Paul Bertrand Yamaguchi"
FullNames(2) = "Hermine Ngaleu"
FullNames(3) = "Francine Mukoko"
FullNames(4) = "Joseph Walters"
FullNames(5) = "Patricia Katts"
FullNames(6) = "Helen Cranston"
FullNames(7) = "Paul Motto"
Dim coll As Enumerable = New Enumerable
coll.Identify(FullNames)
For Each strName In coll
lbxNames.Items.Add(strName)
Next
End Sub
End Class

While the IEnumerator and the IEnumerable interfaces serve
as valuable accessories that allow a collection class to support enumeration, to actually
create a collection class, there are other interfaces you can use to
implement the functionality you want for your collection.
When you want to use
a collection in your application, you may first check what classes are
available in the .NET Framework. If you do not find a suitable class, you
can create your own that implements one or more interfaces. As it happens,
the .NET Framework ships with many of them and your next step is to choose
which one you prefer. Some of the most commonly used interfaces are
- System.Collections.IComparer and System.Collections.Generic.IComparer:If you derive a class from this interface, you can
define how two objects would be compared for similarity or difference
- System.Collections.IDictionary and System.Collections.Generic.IDictionary: This interface is used to create a collection class
where each item is made of a key=value combination
|
The ICollection Interface
|
|
One of
the primary pieces of information you should provide about a the values in a
database is the number of values that a list is (currently) holding. When creating a collection
class, to prepare it to provide this valuable information, you can (should)
implement an interface named ICollection. The ICollection
interface is defined in the System.Collections namespace while its
equivalent of the same name is defined in the System.Collections.Generic
namespace. This means
that, if you are creating a class that implements it, you should include this
namespace in the file. Here is an example for the System.Collections.ICollection
interface:
Public Class BookCollection
Implements ICollection
End Class
To assist you with keeping track of the number of items in a
collection, the ICollection interface is equipped with a property named Count,
which you must implement. To do this, you can create a private member variable
that will actually keep a count of the number of items. The Count property can
then be used to communicate this information to the clients of the class. Here is
an example:
Public Class BookCollection
Implements ICollection
Private NumberOfBooks As Integer
Public Sub New()
NumberOfBooks = 0
End Sub
Public ReadOnly Property Count() As Integer_
Implements ICollection.Count
Get
Return NumberOfBooks
End Get
End Property
End Class
The ICollection interface also allows its implementer
to copy some of its items to an array. To provide this functionality, the
interface is equipped with a method named CopyTo, which you must
implement. The syntax of this
method is:
Sub CopyTo(array As Array, index As Integer)
This method takes two arguments. The first argument is the
array that will receive the items. The second argument is the index of the item
from where the copying operation will begin. Here is an example:
Public Class BookCollection
Implements ICollection
Private Books() As String
Private NumberOfBooks As Integer
Public Sub New()
NumberOfBooks = 0
End Sub
Public ReadOnly Property Count() As Integer _
Implements ICollection.Count
Get
Return NumberOfBooks
End Get
End Property
Public Sub CopyTo(ByVal ary As Array, ByVal index As Integer) _
Implements ICollection.CopyTo
Dim i As Integer
Dim bks(Count) As String
For i = 0 To Count - 1
bks(i) = Books(i)
ary = bks
i = i + 1
Next
End Sub
End Class
If you create a collection class, you can provide the
ability to enumerate its items. When this is done, some time to time, you will
want to identify or to know what item is currently being accessed. In case other
collection classes are using the same function at the time you are accessing
this information, you should have an object that is responsible for synchronizing the
collection. To do this in your ICollection-based class, you must
implement a property named SyncRoot. This property must return an Object
object. Here is an example:
Public Class BookCollection
Implements ICollection
. . . No Change
Public ReadOnly Property SyncRoot() As Object _
Implements ICollection.SyncRoot
Get
Return Me
End Get
End Property
End Class
Besides the ability to specify the number of items in a
collection, a class that implements the ICollection interface must
retrieve a value that indicates whether its item is synchronized. To give this
information, you must implement a Boolean property named IsSynchronized.
Here is an example:
Public Class BookCollection
Implements ICollection
. . . No Change
Public ReadOnly Property IsSynchronized() As Boolean _
Implements ICollection.IsSynchronized
Get
Return False
End Get
End Property
End Class
System.Collections.ICollection (and System.Collections.Generic.ICollection)
exdepends the IEnumerable interface.
This means that you should be able to use For Each in your ICollection-based
class but you must create the functionality yourself, which is done by
implementing the GetEnumerator() method. Even if you do not want to
support this feature, you still must provide at least a skeleton for this
method. Here is an example:
Public Class BookCollection
Implements ICollection
Private Books() As String
Private NumberOfBooks As Integer
Public Sub New()
NumberOfBooks = 0
End Sub
Public ReadOnly Property Count() As Integer _
Implements ICollection.Count
Get
Return NumberOfBooks
End Get
End Property
Public Sub CopyTo(ByVal ary As Array, ByVal index As Integer) _
Implements ICollection.CopyTo
Dim i As Integer
Dim bks(Count) As String
For i = 0 To Count - 1
bks(i) = Books(i)
ary = bks
i = i + 1
Next
End Sub
Public ReadOnly Property SyncRoot() As Object _
Implements ICollection.SyncRoot
Get
Return Me
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean _
Implements ICollection.IsSynchronized
Get
Return False
End Get
End Property
Public Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return Nothing
End Function
End Class
While it provides the minimum functionality of a collection,
the System.Collections.ICollection (and the System.Collections.Generic.ICollection)
interface is not equipped to perform the regular
operations of a collection class, such as adding, retrieving, or deleting items
from a set.
To assist you with creating a collection class as complete as possible, the
.NET Framework provides an interface named IList. The IList
interface is defined in the System.Collections namespace and its
equivalent of the same name is defined in the System.Collections.Generic
namespace. The interface is equipped with the methods necessary to add, insert, delete, or
retrieve items from a collection. Because the functionalities of these methods
may not suit you, to use these features, you must create a class that implements
them.
|
Practical
Learning: Starting a Custom Collection Class
|
|
- Start Microsoft Visual Basic and create a new Windows Forms Application named MusicalInstrumentStore1
- To create a dialog box, on the main menu, click Project -> Add Windows
Form...
- Set the name to CategoryEditor and click Add
- Design the form as follows:
 |
| Control |
Text |
Name |
Other Properties |
| Label |
&Category: |
|
|
| TextBox |
|
txtCategory |
Modifiers: Public |
| Button |
OK |
btnOK |
DialogResult: OK |
| Button |
Cancel |
btnCancel |
DialogResult: Cancel |
|
| Form Property |
Value |
| FormBorderStyle |
FixedDialog |
| Text |
Category Editor |
| StartPosition |
CenterScreen |
| AcceptButton |
btnOK |
| CancelButton |
btnCancel |
| MaximizeBox |
False |
| MinimizeBox |
False |
| ShowInTaskbar |
False |
- To create a dialog box, on the main menu, click Project -> Add Windows
Form...
- Set the name to TypeEditor and click Add
- Design the form as follows:
 |
| Control |
Text |
Name |
Other Properties |
| Label |
&Item Type: |
|
|
| TextBox |
|
txtItemType |
Modifiers: Public |
| Button |
OK |
btnOK |
DialogResult: OK |
| Button |
Cancel |
btnCancel |
DialogResult: Cancel |
|
| Form Property |
Value |
| FormBorderStyle |
FixedDialog |
| Text |
Type Editor |
| StartPosition |
CenterScreen |
| AcceptButton |
btnOK |
| CancelButton |
btnCancel |
| MaximizeBox |
False |
| MinimizeBox |
False |
| ShowInTaskbar |
False |
- To create a dialog box, in the Solution Explorer, right-click
MusicalInstrumentStore2 -> Add -> Windows
Form...
- Set the name to ItemEditor and click Add
- Design the form as follows:
 |
| Control |
Text |
Name |
Other Properties |
| Label |
&Item #: |
|
|
| TextBox |
|
txtItemNumber |
|
| Label |
&Category: |
|
|
| ComboBox |
|
cbxCategories |
|
| Button |
New C&ategory... |
btnNewCategory |
|
| Label |
&Item Type: |
|
|
| ComboBox |
|
cbxItemTypes |
|
| Button |
New &Item Type... |
btnNewItemType |
|
| Label |
Item &Name: |
|
|
| TextBox |
|
txtItemName |
|
| Label |
&Unit Price: |
|
|
| TextBox |
0.00 |
txtUnitPrice |
TextAlign: Right |
| Button |
Picture... |
btnPicture |
|
| TextBox |
|
txtPicturePath |
|
| PictureBox |
|
pbxPicturePath |
SizeMode: Zoom |
| Button |
Create |
btnCreate |
|
| Button |
Close |
btnClose |
DialogResult: Cancel |
| OpenFileDialog |
(Name): dlgOpen
Title: Select Item Picture
DefaultExt: jpg
Filter: JPEG Files (*.jpg,*.jpeg)|*.jpg|GIF Files (*.gif)|*.gif|Bitmap Files
(*.bmp)|*.bmp|PNG Files (*.png)|*.png |
|
| Form Property |
Value |
| FormBorderStyle |
FixedDialog |
| Text |
Musical Instrument Store - Item Editor |
| StartPosition |
CenterScreen |
| MaximizeBox |
False |
| MinimizeBox |
False |
| ShowInTaskbar |
False |
- Double-click the New Category button and implement it as follows:
Private Sub btnNewCategory_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnNewCategory.Click
Dim Editor As CategoryEditor = New CategoryEditor
If Editor.ShowDialog() = DialogResult.OK Then
If Editor.txtCategory.Text.Length > 0 Then
Dim NewCategory As String = Editor.txtCategory.Text
' Make sure the category is not yet in the list
If cbxCategories.Items.Contains(NewCategory) Then
MsgBox(NewCategory& " is already in the list")
Else
' Since this is a new category, add it to the combo box
cbxCategories.Items.Add(NewCategory)
' Just in case the user wanted to use this new category
' select it
cbxCategories.Text = NewCategory
End If
End If
End If
End Sub
|
- In the Class name combo box, select btnNewItemType
- In the Method Name combo box, select Click and implement the event as follows:
Private Sub btnNewItemType_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnNewItemType.Click
Dim Editor As TypeEditor = New TypeEditor
If Editor.ShowDialog() = DialogResult.OK Then
If Editor.txtItemType.Text.Length > 0 Then
Dim NewType As String = Editor.txtItemType.Text
' Make sure the type is not yet in the list
If cbxTypes.Items.Contains(NewType) Then
MsgBox("The list already contains " & NewType)
Else
cbxTypes.Items.Add(NewType)
cbxTypes.Text = NewType
End If
End If
End If
End Sub
|
- In the Class name combo box, select btnPicture
- In the Method Name combo box, select Click and implement the event as follows:
Private Sub btnPicture_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnPicture.Click
If dlgPicture.ShowDialog() = DialogResult.OK Then
txtPicturePath.Text = dlgPicture.FileName
pbxStoreItem.Image = Image.FromFile(txtPicturePath.Text)
End If
End Sub
|
- In the Solution Explorer, right-click Form1.vb and click Rename
- Type MusicStore.vb and press Enter twice (to display the form)
- Design the form as follows:
 |
| Control |
Text |
Name |
Other Properties |
| Label |
 |
Item Category: |
|
|
| ComboBox |
 |
|
cbxCategories |
|
| Label |
 |
Items Type: |
|
|
| ComboBox |
 |
|
cbxTypes |
|
| Label |
 |
Available Items |
|
|
| Button |
 |
New Store Item... |
btnNewStoreItem |
|
| ListView |
 |
|
lvwStoreItems |
View: Details
FullRowSelect: True
GridLines: True |
| Columns |
| (Name) |
Text |
TextAlign |
Width |
| colItemNumber |
Item # |
|
|
| colItemName |
Item Name/Description |
|
320 |
| colUnitPrice |
Unit Price |
Right |
|
|
| PictureBox |
 |
|
pbxStoreItem |
SizeMode: Zoom |
| GroupBox |
 |
Selected Items |
|
|
| Label |
 |
Item # |
|
|
| Label |
 |
Description |
|
|
| Label |
 |
Unit Price |
|
|
| Label |
 |
Qty |
|
|
| Label |
 |
Sub Total |
|
|
| TextBox |
 |
|
txtItemNumber1 |
|
| TextBox |
 |
|
txtDescription1 |
|
| TextBox |
 |
0.00 |
txtUnitPrice1 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity1 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal1 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove1 |
|
| TextBox |
 |
|
txtItemNumber2 |
|
| TextBox |
 |
|
txtDescription2 |
|
| TextBox |
 |
0.00 |
txtUnitPrice2 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity2 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal2 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove2 |
|
| TextBox |
 |
|
txtItemNumber3 |
|
| TextBox |
 |
|
txtDescription3 |
|
| TextBox |
 |
0.00 |
txtUnitPrice3 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity3 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal3 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove3 |
|
| TextBox |
 |
|
txtItemNumber4 |
|
| TextBox |
 |
|
txtDescription4 |
|
| TextBox |
 |
0.00 |
txtUnitPrice4 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity4 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal4 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove4 |
|
| TextBox |
 |
|
txtItemNumber5 |
|
| TextBox |
 |
|
txtDescription5 |
|
| TextBox |
 |
0.00 |
txtUnitPrice5 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity5 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal5 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove5 |
|
| TextBox |
 |
|
txtItemNumber6 |
|
| TextBox |
 |
|
txtDescription6 |
|
| TextBox |
 |
0.00 |
txtUnitPrice6 |
TextAlign: Right |
| TextBox |
 |
0 |
txtQuantity6 |
TextAlign: Right |
| TextBox |
 |
0.00 |
txtSubTotal6 |
TextAlign: Right |
| Button |
 |
Remove |
btnRemove6 |
|
| GroupBox |
 |
Order Summary |
|
|
| Label |
 |
Items Total: |
|
|
| TextBox |
 |
0.00 |
txtItemsTotal |
TextAlign: Right |
| Label |
 |
Tax Rate: |
|
|
| TextBox |
 |
7.75 |
|
TextAlign: Right |
| Label |
 |
% |
|
|
| Label |
 |
Tax Amount: |
|
|
| TextBox |
 |
0.00 |
txtTaxAmount |
TextAlign: Right |
| Label |
 |
Order Total: |
|
|
| TextBox |
 |
0.00 |
txtOrderTotal |
TextAlign: Right |
| Button |
 |
Save |
btnSave |
|
| Label |
 |
Receipt #: |
|
|
| TextBox |
 |
0.00 |
txtReceiptNumber |
|
| Button |
 |
&Open |
btnOpen |
|
| Button |
 |
New Order |
btnNewOrder |
|
| Button |
 |
Close |
btnClose |
|
|
- To create a new class, in the Solution Explorer, right-click
MusicalInstrumentStore2 -> Add -> Class...
- Set the Name to StoreItem and press Enter
- Change the file as follows:
<Serializable()> Public Class StoreItem
Private nbr As String
Private cat As String
Private tp As String
Private nm As String
Private prc As Double
Public Property ItemNumber() As String
Get
Return nbr
End Get
Set(ByVal value As String)
nbr = value
End Set
End Property
Public Property Category() As String
Get
Return cat
End Get
Set(ByVal value As String)
cat = value
End Set
End Property
Public Property Type() As String
Get
Return tp
End Get
Set(ByVal value As String)
tp = value
End Set
End Property
Public Property ItemName() As String
Get
Return nm
End Get
Set(ByVal value As String)
nm = value
End Set
End Property
Public Property UnitPrice() As Double
Get
Return prc
End Get
Set(ByVal value As Double)
prc = value
End Set
End Property
Public Overrides Function Equals(ByVal Same As Object) As Boolean
If (nbr = Same.nbr) And _
(cat = Same.cat) And _
(tp = Same.tp) And _
(nm = Same.nm) And _
(prc = Same.prc) Then
Return True
Else
Return False
End If
End Function
Public Sub New()
nbr = "000000"
cat = "Accessories"
tp = "Accessories"
nm = "Unknown"
prc = 0.0
End Sub
Public Sub StoreItem(ByVal itmNumber As String)
nbr = itmNumber
cat = "Accessories"
tp = "Accessories"
nm = "Unknown"
prc = 0.0
End Sub
Public Sub New(ByVal itmNumber As String, ByVal strCategory As String, _
ByVal strType As String, ByVal strName As String, _
ByVal dPrice As Double)
nbr = itmNumber
cat = strCategory
tp = strType
nm = strName
prc = dPrice
End Sub
End Class
|
- Save the file
As mentioned above, to create a collection, you can derive
it from the IList
interface. Here is an example:
Imports System.Collections
Public Class BookCollection
Implements IList
End Class
This System.Collections.IList interface is declared as follows:
Public Interface IList _
Implements ICollection, IEnumerable
This System.Collections.Generic.IList interface is declared as follows:
Public Interface IList(Of T) _
Implements ICollection(Of T), _
IEnumerable(Of T), _
IEnumerable
This means that the IList interface exdends both the ICollection
and the IEnumerable interfaces. This also implies that you must implement
the members of these parent interfaces. In other words, you must implement the
Count property, the SyncRoot property, the IsSynchronized
property, and the CopyTo() method of the ICollection interface.
If you use Microsoft Visual Basic 2008 to create the
project, as soon as you type the Implements IList line and press Enter, the
studio would generate the complete skeleton code for you:
Public Class BookCollection
Implements IList
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _
Implements System.Collections.ICollection.CopyTo
End Sub
Public ReadOnly Property Count() As Integer _
Implements System.Collections.ICollection.Count
Get
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean _
Implements System.Collections.ICollection.IsSynchronized
Get
End Get
End Property
Public ReadOnly Property SyncRoot() As Object _
Implements System.Collections.ICollection.SyncRoot
Get
End Get
End Property
Public Function GetEnumerator() As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
End Function
Public Function Add(ByVal value As Object) As Integer _
Implements System.Collections.IList.Add
End Function
Public Sub Clear() Implements System.Collections.IList.Clear
End Sub
Public Function Contains(ByVal value As Object) As Boolean _
Implements System.Collections.IList.Contains
End Function
Public Function IndexOf(ByVal value As Object) As Integer _
Implements System.Collections.IList.IndexOf
End Function
Public Sub Insert(ByVal index As Integer, ByVal value As Object) _
Implements System.Collections.IList.Insert
End Sub
Public ReadOnly Property IsFixedSize() As Boolean _
Implements System.Collections.IList.IsFixedSize
Get
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean _
Implements System.Collections.IList.IsReadOnly
Get
End Get
End Property
Default Public Property Item(ByVal index As Integer) As Object _
Implements System.Collections.IList.Item
Get
End Get
Set(ByVal value As Object)
End Set
End Property
Public Sub Remove(ByVal value As Object) _
Implements System.Collections.IList.Remove
End Sub
Public Sub RemoveAt(ByVal index As Integer) _
Implements System.Collections.IList.RemoveAt
End Sub
End Class
You can then customize it. From what we learned with ICollection, here are examples of implementing these
members for the System.Collections.IList interface:
Public Class BookCollection
Implements IList
Private Counter As Integer
Private Objects(10) As Object
Public Sub New()
Counter = 0
End Sub
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _
Implements System.Collections.ICollection.CopyTo
End Sub
Public ReadOnly Property Count() As Integer _
Implements System.Collections.ICollection.Count
Get
Return Counter
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean _
Implements System.Collections.ICollection.IsSynchronized
Get
Return False
End Get
End Property
Public ReadOnly Property SyncRoot() As Object _
Implements System.Collections.ICollection.SyncRoot
Get
Return Me
End Get
End Property
End Class
You must also implement the System.Collections.GetEnumerator()
(or the System.Collections.Generic.GetEnumerator()) method of
the System.Collections.IEnumerable (or of the System.Collections.Generic.IEnumerable) interface. If you
do not have time to completely implement
it, you can simply return Nothing. Here is an example for the System.Collections.IList
interface:
Public Function GetEnumerator() As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return Nothing
End Function
|
Practical
Learning: Implementing the IList Interface
|
|
- To create a new class, in the Class View, right-click
MusicalInstrumentStore2 -> Add -> Class...
- Set the Name to StoreItems and press Enter
- Click the right side of the Public Class StoreItems line and press Enter
- Type Implements IList and press Enter.
Notice that a complete skeleton code has been generated
- Change the file as follows:
<Serializable()> Public Class StoreItems
Implements IList
Private Counter As Integer
Private Items() As Object = New Object
Public Sub New()
Counter = 0
End Sub
Public Sub CopyTo(ByVal array As System.Array, _
ByVal index As Integer) _
Implements System.Collections.ICollection.CopyTo
End Sub
Public ReadOnly Property Count() As Integer _
Implements System.Collections.ICollection.Count
Get
Return Counter
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean _
Implements System.Collections.ICollection.IsSynchronized
Get
Return False
End Get
End Property
Public ReadOnly Property SyncRoot() As Object _
Implements System.Collections.ICollection.SyncRoot
Get
Return Me
End Get
End Property
Public Function GetEnumerator() As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return Nothing
End Function
' This method is used to add a new item to the collection
Public Function Add(ByVal value As Object) As Integer _
Implements System.Collections.IList.Add
End Function
' This methods deletes all items from the collection
Public Sub Clear() Implements System.Collections.IList.Clear
End Sub
' This method is used to find out whether the item
' passed as argument exists in the collection
Public Function Contains(ByVal value As Object) As Boolean _
Implements System.Collections.IList.Contains
End Function
' This method is used to check whether the item passed as
' argument exists in the collection. If so, it returns its index
Public Function IndexOf(ByVal value As Object) As Integer _
Implements System.Collections.IList.IndexOf
End Function
' This method can be used to insert an item at
' a certain position inside the collection
Public Sub Insert(ByVal index As Integer, ByVal value As Object) _
Implements System.Collections.IList.Insert
End Sub
Public ReadOnly Property IsFixedSize() As Boolean Implements _
System.Collections.IList.IsFixedSize
Get
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean _
Implements System.Collections.IList.IsReadOnly
Get
End Get
End Property
Default Public Property Item(ByVal index As Integer) As Object _
Implements System.Collections.IList.Item
Get
Return Nothing
End Get
Set(ByVal value As Object)
End Set
End Property
' This method first checks the existence of the item passed
' as argument. If the item exists, the method deletes it
Public Sub Remove(ByVal value As Object) _
Implements System.Collections.IList.Remove
End Sub
' This method is used to delete the item positioned
' at the index passed as argument
Public Sub RemoveAt(ByVal index As Integer) _
Implements System.Collections.IList.RemoveAt
End Sub
End Class
|
- Save the file
In the next sections, we will see how to add values to a
list of a database. As you add or insert values in a list, the Count
property grows. If your collection is array-based, when you start it, you
specify the number of values that the list will contain. In theory, you cannot
add new values beyond that number. In reality, you can increase the size of an
array and then add a new item. If your collection is a linked list, you are also
not confined to the laws of space (unless your computer runs out of memory).
If you create a list whose number of values must be constant,
the user cannot add values beyond the maximum allowed number. Therefore, before
adding a value, you can first check whether the collection has a fixed size or
not. To give you this information, the IList interface is equipped with
a Boolean read-only property named IsFxedSize. This property simply lets
the user know whether the collection has a fixed number of items.
Here is an example of implementing this property for the System.Collections.IList
interface:
Public ReadOnly Property IsFixedSize() As Boolean _
Implements System.Collections.IList.IsFixedSize
Get
Return False
End Get
End Property
|
Practical
Learning: Implementing the IsFixedSize Property
|
|
- Change the IsFixedSize() method as follows:
Public ReadOnly Property IsFixedSize() As Boolean Implements _
System.Collections.IList.IsFixedSize
Get
Return False
End Get
End Property
|
- Save the file
Most databases are meant to receive new values. If you
want, you can create a list that cannot receive new values. To support
this, the IList interface is equipped with the Boolean IsReadOnly
property. If a list is read-only, it would prevent the clients from
adding items to it.
Here is an example of implementing the IsReadOnly
property for the System.Collections.IList interface:
Public ReadOnly Property IsReadOnly() As Boolean _
Implements System.Collections.IList.IsReadOnly
Get
Return False
End Get
End Property
|
Practical
Learning: Setting the Read-Only Effect
|
|
- Change the IsReadOnly method as follows:
Public ReadOnly Property IsReadOnly() As Boolean _
Implements System.Collections.IList.IsReadOnly
Get
Return False
End Get
End Property
|
- Save the file
|
Populating the Collection
|
|
As it should be obvious, the primary operation to perform on a list is to
populate it with at least one value. To support this, the System.Collections.IList interface
is equipped with a method named
Add. Its syntax is: Function Add(value As Object) As Integer
This method takes one argument as the value to add to the
list. If your collection is an array, you can first check that there is
still enough room in the list to add a new item. In reality, this is never
an issue with the System.Collections.IList interface:
- If there is still room in the collection, the value would be added to the
list
- If there is not enough room, the value would simply not be added. There
would not be a problem and the program would not crash. In fact, no
exception would be thrown if the value was not added because of lack of
space. On the other hand, since the compiler would not let you know that there
was a problem with "logistic", you may not know whether the value was added or not. Therefore, if you are concerned with
knowing whether the value was added, you must provide this functionality yourself
If the method succeeds with the addition, it returns the position where the
value was added in the list. This is usually the last position in the list.
Here is an example:
Public Function Add(ByVal value As Object) As Integer _
Implements System.Collections.IList.Add
' Check whether there is still room in
' the array to add a new item
If Counter < Objects.Length Then
' Since there is room, put the new item to the end
Objects(Counter) = value
' increase the number of items
Counter = Counter + 1
' Return the index of the item that was added
Return Counter - 1
' Since the item could not be added, return a negative index
Else
Return -1
End If
End Function
|
Practical
Learning: Adding an Item to the Collection
|
|
- Change the code of the StoreItems.Add() method as follows:
' This method is used to add a new item to the collection
Public Function Add(ByVal value As Object) As Integer _
Implements System.Collections.IList.Add
' Find out if the array is getting too small for the next item(s)
' If it is, increase its size by 5
If Count = Items.Length Then
Array.Resize(Items, Items.Length + 5)
End If
If Counter < Items.Length Then
Items(Counter) = value
Counter = Counter + 1
Return Counter - 1
Else
Return -1
End If
End Function
|
- In the Class View, double-click ItemEditor
- In the top section of the file, add the following:
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Public Class ItemEditor
|
- In the Solution Explorer, right-click ItemEditor.vb and click View
Code
- In the Class name combo box, select btnCreate
- In the Method Name combo box, select Click and implement the event
as follows:
Private Sub btnCreate_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnCreate.Click
Dim StreamStoreItem As FileStream
Dim Item As StoreItem = New StoreItem
Dim items As StoreItems = New StoreItems
Dim FormatterStoreItem As BinaryFormatter = New BinaryFormatter
' If this directory doesn't exist, create it
Directory.CreateDirectory("C:\Musical Instrument Store")
' This is the file that holds the list of items
Dim Filename As String = "C:\Musical Instrument Store\StoreItems.mis"
' Create a random number that will be used to identify the item
Dim RndNumber As Random = New Random
txtItemNumber.Text = RndNumber.Next(100000, 999999)
' Make sure the user had selected a category
If cbxCategories.Text.Length = 0 Then
MsgBox("You must specify the item's category")
cbxCategories.Focus()
Exit Sub
End If
' Make sure the user had selected a type
If cbxTypes.Text.Length = 0 Then
MsgBox("You must specify the item's type")
cbxTypes.Focus()
Exit Sub
End If
' Make sure the user had entered a name/description
If txtItemName.Text.Length = 0 Then
MsgBox("You must enter the name (or a " & _
"short description) for the item")
txtItemName.Focus()
Exit Sub
End If
' Make sure the user had typed a price for the item
If txtUnitPrice.Text.Length = 0 Then
MsgBox("You must enter the price of the item")
txtUnitPrice.Focus()
Exit Sub
End If
' Before saving the new item, find out if there was
' already a file that holds the list of items
' If that file exists, open it and store its items
' in our StoreItems list
If File.Exists(Filename) Then
StreamStoreItem = New FileStream(Filename, _
FileMode.Open, _
FileAccess.Read, _
FileShare.Read)
Try
' Retrieve the list of items from file
items = CType(FormatterStoreItem.Deserialize(StreamStoreItem), _
StoreItems)
Finally
StreamStoreItem.Close()
End Try
End If
' Create the music item
item.ItemNumber = txtItemNumber.Text
item.Category = cbxCategories.Text
item.Type = cbxTypes.Text
item.ItemName = txtItemName.Text
item.UnitPrice = cdbl(txtUnitPrice.Text)
' Call the Add method of our collection class to add the item
items.Add(item)
' Save the list
StreamStoreItem = New FileStream(Filename, _
FileMode.Create, _
FileAccess.Write, _
FileShare.Write)
Try
FormatterStoreItem.Serialize(StreamStoreItem, items)
If txtPicturePath.Text.Length <> 0 Then
Dim flePicture As FileInfo = New FileInfo(txtPicturePath.Text)
flePicture.CopyTo("C:\Musical Instrument Store\" & _
txtItemNumber.Text & flePicture.Extension)
End If
' After saving the item, reset the form
txtItemNumber.Text = RndNumber.Next(100000, 999999).ToString()
cbxCategories.Text = ""
cbxTypes.Text = ""
txtItemName.Text = ""
txtUnitPrice.Text = "0.00"
txtPicturePath.Text = ""
pbxStoreItem.Image = Nothing
Finally
StreamStoreItem.Close()
End Try
End Sub
|
- Save the file
When you call the System.Collections.IList.Add() method, it adds the
new value to the end of the list. Sometimes, you will want the new value to be
insert somewhere inside the list. To support this operation, both the System.Collections.IList
and the System.Collections.Generic.IList interfaces provide a method named
Insert. The syntax of the System.Collections.IList.Insert() method
is:
Sub Insert(index As Integer, value As Object)
The syntax of the System.Collections.Generic.IList.Insert()
method is:
Sub Insert(index As Integer, item As T)
This method takes two arguments. The second argument is the
value that will be inserted into the list. The argument must hold a valid
value. Because this method takes an Object object, if your collection is
using a different type of value, you may have to cast it to Object. The
first argument is the index of the item that will precede the new one.
As mentioned for the System.Collections.IList. Add() method, there are a few things
you should know about this operation's success or lack of it:
- If the index argument holds a negative value or a value higher than
the allowed number (for example if the list is an array) of the items
(depending on how you implement the method), the new value would not be
added, the compiler would not throw an exception, and therefore nothing
would let you know that the value was not added. If you want to find out
whether the value was formally or actually inserted or not, you must create
the functionality yourself
- If the value argument is not valid, again depending on how you
structure your class, either the value would not be inserted or something
else would go wrong. Fortunately, if the value argument is of the
type of
a class you created yourself, the compiler would produce an error such as
letting you know that the argument is holding a value that is not conform to its
property or member variable
|
Locating an Item in the Collection
|
|
|
This Default Item of the Collection
|
|
While using a list, various operations require that
you know the object you are currently accessing. To provide this operation, you
must create an indexed property. This property should take an index and return
the type of object that makes up the list. Here is an example:
Default Public Property Item(ByVal index As Integer) As Object _
Implements System.Collections.IList.Item
Get
Return Objects(index)
End Get
Set(ByVal value As Object)
Objects(index) = value
End Set
End Property
After creating this property, you can then access an item
using its index and applying the () operator on its instance. Remember that if
you want to use For Each, you must appropriately implement the IEnumerable.GetEnumerator()
method.
|
Practical
Learning: Identifying this Item in the Collection
|
|
- In the Class View, click StoreItems
- In the lower part of the Class View, double-click Item(Integer) As Object
- Change the code of the property as
follows:
Default Public Property Item(ByVal index As Integer) As Object _
Implements System.Collections.IList.Item
Get
Return Items(index)
End Get
Set(ByVal value As Object)
Items(index) = value
End Set
End Property
|
- In the Solution Explorer, right-click ItemEditor.vb and click View
Code
- In the Class name combo box, select (ItemEditor Events)
- In the Method Name combo box, select Load and implement the event
as follows:
Private Sub ItemEditor_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
Dim i As Integer
Dim RndNumber As Random = New Random
' Since all values seem ready, prepare to process the item
Dim Item As StoreItem = New StoreItem
Dim Items As StoreItems = New StoreItems
Dim FormatterStoreItem As BinaryFormatter = New BinaryFormatter
' This is the file that holds the list of items
Dim Filename As String = "C:\Musical Instrument Store\StoreItems.mis"
If File.Exists(Filename) Then
Dim StreamStoreItem As FileStream = New FileStream(Filename, _
FileMode.Open, _
FileAccess.Read, _
FileShare.Read)
Try
' Retrieve the list of items from file
Items = CType(FormatterStoreItem.Deserialize(StreamStoreItem), StoreItems)
' Display the categories in the combo box
For i = 0 To Items.Count - 1
Item = CType(Items(i), StoreItem)
If Not cbxCategories.Items.Contains(Item.Category) Then
cbxCategories.Items.Add(Item.Category)
End If
Next
' Display the items types in the combo box
For i = 0 To Items.Count - 1
Item = CType(Items(i), StoreItem)
If Not cbxTypes.Items.Contains(Item.Type) Then
cbxTypes.Items.Add(Item.Type)
End If
Next
Finally
StreamStoreItem.Close()
End Try
Else
' Create a random number that will be used
' to identify the item
txtItemNumber.Text = RndNumber.Next(100000, 999999).ToString()
' Make sure the user had selected a category
cbxCategories.Text = ""
cbxTypes.Text = ""
End If
End Sub
|
- In the Solution Explorer, right-click MusicStore.vb and click View Code
- Make the following changes:
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Public Class MusicStore
Private Items As StoreItems
Private iFilename As Integer
Private IsNewCustomerOrder As Boolean
Private Sub LoadMusicStore()
' Since all values seem ready, prepare to process the item
Dim i As Integer
items = New StoreItems
Dim FormatterStoreItem As BinaryFormatter = New BinaryFormatter
' This is the file that holds the list of items
Dim Filename As String = "C:\Musical Instrument Store\StoreItems.mis"
If File.Exists(Filename) Then
Dim StreamStoreItem As FileStream = New FileStream(Filename, _
FileMode.Open, _
FileAccess.Read, _
FileShare.Read)
Try
' Retrieve the list of items from file
Items = CType(FormatterStoreItem.Deserialize(StreamStoreItem), _
StoreItems)
' Display the categories in the combo box
For i = 0 To items.Count - 1
Dim item As StoreItem = CType(items(i), StoreItem)
If Not cbxCategories.Items.Contains(item.Category) Then
cbxCategories.Items.Add(item.Category)
End If
Next
Finally
StreamStoreItem.Close()
End Try
End If
End Sub
End Class
|
- In the Class Name combo box, select btnNewStoreItem
- In the Method Name combo box, select Click and implement the event as follows:
Private Sub btnNewStoreItem_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnNewStoreItem.Click
Dim Editor As ItemEditor = New ItemEditor
' Create a random number to get it ready for
' the user creating a new store item
Dim RndNumber As Random = New Random
Editor.txtItemNumber.Text = RndNumber.Next(100000, 999999)
If Editor.ShowDialog() = DialogResult.Cancel Then
LoadMusicStore()
End If
End Sub
|
- In the Class Name combo box, select cbxCategories
- In the Method Name combo box, select SelectedIndexChanged and implement the event as
follows:
Private Sub cbxCategories_SelectedIndexChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles cbxCategories.SelectedIndexChanged
Dim i As Integer
cbxTypes.Items.Clear()
cbxTypes.Text = ""
lvwStoreItems.Items.Clear()
pbxStoreItem.Image = Nothing
' If the current store inventory is empty, don't do anything
If Items.Count = 0 Then Exit Sub
' Get the item selected in the combo box
Dim strCategory = CStr(cbxCategories.SelectedItem)
' Before doing anything, remove everything from the Types combo box
' This eliminates the possibility of adding the same item(s) that
' would exist already in the combo box
cbxTypes.Items.Clear()
' Check each item from the store inventory
For i = 0 To Items.Count - 1
' Get the current item from the store inventory
Dim Item = CType(Items(i), StoreItem)
' If that item is the same as the one selected in the combo box...
If Item.Category = strCategory Then
' ... get ready to add its corresponding type
' But check first that the type is not
' already in the Types combo box
' If it's not yet there, then add it
If Not cbxTypes.Items.Contains(Item.Type) Then
cbxTypes.Items.Add(Item.Type)
End If
End If
Next
End Sub
|
- In the Class Name combo box, select cbxTypes
- In the Method Name combo box, select SelectedIndexChanged and implement the event as
follows:
Private Sub cbxTypes_SelectedIndexChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles cbxTypes.SelectedIndexChanged
Dim i As Integer
cbxTypes.Text = ""
pbxStoreItem.Image = Nothing
Dim lviStoreItem As ListViewItem
' If the current store inventory is empty, don't do anything
If Items.Count = 0 Then Exit Sub
' Get the item selected in the Categories combo box
Dim strCategory As String = CStr(cbxCategories.SelectedItem)
' Get the item selected in theTypes combo box
Dim strType As String = CStr(cbxTypes.SelectedItem)
' Empty the Available Items list view because
' we are about to (re)populate it
lvwStoreItems.Items.Clear()
' Check each item from the store inventory
For i = 0 To Items.Count - 1
' Get the current item from the inventory
Dim Item As StoreItem = CType(Items(i), StoreItem)
' If the category of that item is the same as the one
' selected in the combo box and the type of that item is
' the same as the one selected in the Types combo box...
If (Item.Category = strCategory) And _
(Item.Type = strType) Then
' ... then display it in the Available Items list view
lviStoreItem = lvwStoreItems.Items.Add(Item.ItemNumber)
lviStoreItem.SubItems.Add(Item.ItemName)
lviStoreItem.SubItems.Add(FormatNumber(Item.UnitPrice))
End If
Next
End Sub
|
- In the Class Name combo box, select lvwStoreItems
- In the Method Name combo box, select
SelectedIndexChanged and implement the event as follows:
Private Sub lvwStoreItems_SelectedIndexChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles lvwStoreItems.SelectedIndexChanged
Dim i As Integer
Dim strItemNumber As String
Dim Item As StoreItem = New StoreItem
If (lvwStoreItems.SelectedItems.Count = 0) Or _
(lvwStoreItems.SelectedItems.Count > 1) Then
Exit Sub
End If
strItemNumber = lvwStoreItems.SelectedItems(0).SubItems(0).Text
For i = 0 To Items.Count - 1
Dim itm As StoreItem = CType(Items(i), StoreItem)
If itm.ItemNumber = strItemNumber Then
Item = itm
End If
Next
' Make a list of the picture files
Dim FolderName As String = "C:\Musical Instrument Store"
Dim dirStoreItems As DirectoryInfo = New DirectoryInfo(FolderName)
Dim PictureFiles() As FileInfo = dirStoreItems.GetFiles("*.jpg")
' Look for a file that holds the same name as the item number
For Each fle As FileInfo In PictureFiles
' Get the name of the file without its extension
Dim fwe As String = Path.GetFileNameWithoutExtension(fle.FullName)
If fwe = strItemNumber Then
pbxStoreItem.Image = Image.FromFile(FolderName & _
"\" & Item.ItemNumber& ".jpg")
End If
Next
End Sub
|
- Under the above End Sub line, create a new procedure as follows;
' This function calculates the current total order
' and updates the order
Private Sub CalculateOrder()
Dim SubTotal1 As Double
Dim SubTotal2 As Double
Dim SubTotal3 As Double
Dim SubTotal4 As Double
Dim SubTotal5 As Double
Dim SubTotal6 As Double
Dim ItemsTotal As Double
Dim TaxRate As Double
Dim TaxAmount As Double
Dim OrderTotal As Double
' Retrieve the value of each sub total
Try
SubTotal1 = CDbl(txtSubTotal1.Text)
Catch
MsgBox("Invalid Value")
End Try
Try
SubTotal2 = CDbl(txtSubTotal2.Text)
Catch
MsgBox("Invalid Value")
End Try
Try
SubTotal3 = CDbl(txtSubTotal3.Text)
Catch
MsgBox("Invalid Value")
End Try
Try
SubTotal4 = CDbl(txtSubTotal4.Text)
Catch
MsgBox("Invalid Value")
End Try
Try
SubTotal5 = CDbl(txtSubTotal5.Text)
Catch
MsgBox("Invalid Value")
End Try
Try
SubTotal6 = CDbl(txtSubTotal6.Text)
Catch
MsgBox("Invalid Value")
End Try
' Calculate the total value of the sub totals
ItemsTotal = SubTotal1 + SubTotal2 + SubTotal3 + _
SubTotal4 + SubTotal5 + SubTotal6
' Display the total order in the appropriate text box
txtItemsTotal.Text = FormatNumber(ItemsTotal)
Try
TaxRate = CDbl(txtTaxRate.Text)
Catch
MsgBox("Invalid Tax Rate")
txtTaxRate.Text = "7.75"
txtTaxRate.Focus()
End Try
TaxAmount = ItemsTotal * TaxRate / 100
OrderTotal = ItemsTotal + TaxAmount
txtTaxAmount.Text = FormatNumber(TaxAmount)
txtOrderTotal.Text = FormatNumber(OrderTotal)
End Sub
|
- In the Class Name combo box, select txtItemNumber1
- In the Method Name combo box, select Leave and implement the event as follows:
Private Sub txtItemNumber1_Leave(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtItemNumber1.Leave
Dim ItemFound As Boolean = False
Dim Item As StoreItem = New StoreItem
Dim strItemNumber As String = txtItemNumber1.Text
For Each Itm As StoreItem In Items
If Itm.ItemNumber = strItemNumber Then
ItemFound = True
txtDescription1.Text = Itm.ItemName
txtUnitPrice1.Text = Itm.UnitPrice.ToString("F")
txtQuantity1.Text = "1"
txtSubTotal1.Text = Itm.UnitPrice.ToString("F")
CalculateOrder()
End If
Next
If ItemFound = False Then
MsgBox("There is no store item with that number")
txtDescription1.Text = ""
txtUnitPrice1.Text = "0.00"
txtQuantity1.Text = "0"
txtSubTotal1.Text = "0.00"
End If
End Sub
|
- Under the above End Sub line, define the following event
Public Sub Item1Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice1.Leave, txtQuantity1.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity1.Text)
Catch
MsgBox("Invalid quantity value for item 1")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice1.Text)
Catch
MsgBox("Invalid unit price for item 1")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal1.Text = FormatNumber(SubTotal)
btnRemove1.Enabled = True
' Update the order
CalculateOrder()
End Sub
|
- In the Class Name combo box, select btnRemove1
- On the form, double-click the top Remove button and implement the event as
follows:
Private Sub btnRemove1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove1.Click
txtItemNumber1.Text = ""
txtDescription1.Text = ""
txtUnitPrice1.Text = "0.00"
txtQuantity1.Text = "0"
txtSubTotal1.Text = "0.00"
btnRemove1.Enabled = False
CalculateOrder()
End Sub
|
- Complete the file as follows:
Public Sub Item2Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice2.Leave, txtQuantity2.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity2.Text)
Catch
MsgBox("Invalid quantity value for item 2")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice2.Text)
Catch
MsgBox("Invalid unit price for item 2")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal2.Text = SubTotal.ToString()
btnRemove2.Enabled = True
' Update the order
CalculateOrder()
End Sub
Private Sub btnRemove2_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove2.Click
txtItemNumber2.Text = ""
txtDescription2.Text = ""
txtUnitPrice2.Text = "0.00"
txtQuantity2.Text = "0"
txtSubTotal2.Text = "0.00"
btnRemove2.Enabled = False
CalculateOrder()
End Sub
Public Sub Item3Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice3.Leave, txtQuantity3.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity3.Text)
Catch
MsgBox("Invalid quantity value for item 3")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice3.Text)
Catch
MsgBox("Invalid unit price for item 3")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal3.Text = SubTotal.ToString()
btnRemove3.Enabled = True
' Update the order
CalculateOrder()
End Sub
Private Sub btnRemove3_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove3.Click
txtItemNumber3.Text = ""
txtDescription3.Text = ""
txtUnitPrice3.Text = "0.00"
txtQuantity3.Text = "0"
txtSubTotal3.Text = "0.00"
btnRemove3.Enabled = False
CalculateOrder()
End Sub
Public Sub Item4Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice4.Leave, txtQuantity4.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity4.Text)
Catch
MsgBox("Invalid quantity value for item 4")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice4.Text)
Catch
MsgBox("Invalid unit price for item 4")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal4.Text = SubTotal.ToString()
btnRemove4.Enabled = True
' Update the order
CalculateOrder()
End Sub
Private Sub btnRemove4_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove4.Click
txtItemNumber4.Text = ""
txtDescription4.Text = ""
txtUnitPrice4.Text = "0.00"
txtQuantity4.Text = "0"
txtSubTotal4.Text = "0.00"
btnRemove4.Enabled = False
CalculateOrder()
End Sub
Public Sub Item5Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice5.Leave, txtQuantity5.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity5.Text)
Catch
MsgBox("Invalid quantity value for item 5")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice5.Text)
Catch
MsgBox("Invalid unit price for item 5")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal5.Text = SubTotal.ToString()
btnRemove5.Enabled = True
' Update the order
CalculateOrder()
End Sub
Private Sub btnRemove5_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove5.Click
txtItemNumber5.Text = ""
txtDescription5.Text = ""
txtUnitPrice5.Text = "0.00"
txtQuantity5.Text = "0"
txtSubTotal5.Text = "0.00"
btnRemove5.Enabled = False
CalculateOrder()
End Sub
Public Sub Item6Leave(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles txtUnitPrice6.Leave, txtQuantity6.Leave
Dim Quantity As Integer
Dim UnitPrice As Double
Dim SubTotal As Double
' Get the quantity of the current item
Try
Quantity = CInt(txtQuantity6.Text)
Catch
MsgBox("Invalid quantity value for item 6")
End Try
' Get the unit price of the current item
Try
UnitPrice = CDbl(txtUnitPrice6.Text)
Catch
MsgBox("Invalid unit price for item 6")
End Try
' Calculate the current sub total
SubTotal = Quantity * UnitPrice
' Display the new sub total in the corresponding text box
txtSubTotal6.Text = SubTotal.ToString()
btnRemove6.Enabled = True
' Update the order
CalculateOrder()
End Sub
Private Sub btnRemove6_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnRemove6.Click
txtItemNumber6.Text = ""
txtDescription6.Text = ""
txtUnitPrice6.Text = "0.00"
txtQuantity6.Text = "0"
txtSubTotal6.Text = "0.00"
btnRemove6.Enabled = False
CalculateOrder()
End Sub
|
- In the Class Name combo box, select txtTaxRate
- In the Method Name combo box, select Leave and
implement the event as follows:
Private Sub txtTaxRate_Leave(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtTaxRate.Leave
CalculateOrder()
End Sub
|
- In the Class Name combo box, select btnNewCustomerOrder
- In the Method Name combo box, select Click and
implement the event as follows:
Private Sub btnNewCustomerOrder_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnNewCustomerOrder.Click
' We will store our files in the following folder
Dim FolderName As String = "C:\Musical Instrument Store\Receipts"
Dim Folder = Directory.CreateDirectory(FolderName)
' Get the list of files, if any, from our directory
Dim ListOfFiles = Folder.GetFiles()
Dim Filename As String = ""
' If there is no file in the directory,
' then we will use 1000 as the first file name
If ListOfFiles.Length = 0 Then
iFilename = 1000
Else ' If there was at least one file in the directory
' Get a reference to the last file
Dim LastFile = ListOfFiles(ListOfFiles.Length - 1)
' Get the name of the last file without its extension
Dim fwe As String = Path.GetFileNameWithoutExtension(LastFile.FullName)
' Increment the name of the file by 1
iFilename = CInt(fwe) + 1
End If
' Update our global name of the file
Filename = FolderName & "\" & CStr(iFilename)& ".cos"
txtReceiptNumber.Text = CStr(iFilename)
cbxCategories.Text = ""
cbxTypes.Text = ""
lvwStoreItems.Items.Clear()
txtItemNumber1.Text = ""
txtDescription1.Text = ""
txtUnitPrice1.Text = "0.00"
txtQuantity1.Text = "0"
txtSubTotal1.Text = "0.00"
txtItemNumber2.Text = ""
txtDescription2.Text = ""
txtUnitPrice2.Text = "0.00"
txtQuantity2.Text = "0"
txtSubTotal2.Text = "0.00"
txtItemNumber3.Text = ""
txtDescription3.Text = ""
txtUnitPrice3.Text = "0.00"
txtQuantity3.Text = "0"
txtSubTotal3.Text = "0.00"
txtItemNumber4.Text = ""
txtDescription4.Text = ""
txtUnitPrice4.Text = "0.00"
txtQuantity4.Text = "0"
txtSubTotal4.Text = "0.00"
txtItemNumber5.Text = ""
txtDescription5.Text = ""
txtUnitPrice5.Text = "0.00"
txtQuantity5.Text = "0"
txtSubTotal5.Text = "0.00"
txtItemNumber6.Text = ""
txtDescription6.Text = ""
txtUnitPrice6.Text = "0.00"
txtQuantity6.Text = "0"
txtSubTotal6.Text = "0.00"
txtItemsTotal.Text = "0.00"
txtTaxRate.Text = "7.75"
txtTaxAmount.Text = "0.00"
txtOrderTotal.Text = "0.00"
End Sub
|
- In the Class Name combo box, select btnSave
- In the Method Name combo box, select Click and implement the event as follows:
Private Sub btnSave_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnSave.Click
Dim fwe As String
Dim Filename As String
Dim LastFile As FileInfo
' We will store our files in the following folder
Dim FolderName As String = "C:\Musical Instrument Store\Receipts"
Dim Folder = Directory.CreateDirectory(FolderName)
' Get the list of files, if any, from our directory
Dim ListOfFiles() As FileInfo = Folder.GetFiles()
' If this is a new customer order,
' get ready to create a name for the file
If IsNewCustomerOrder = True Then
' If there is no file in the directory,
' then we will use 1000 as the first file name
If ListOfFiles.Length = 0 Then
iFilename = 1000
Else ' If there was at least one file in the directory
' Get a reference to the last file
LastFile = ListOfFiles(ListOfFiles.Length - 1)
' Get the name of the last file without its extension
fwe = Path.GetFileNameWithoutExtension(LastFile.FullName)
' Increment the name of the file by 1
iFilename = CInt(fwe) + 1
End If
' Update our global name of the file
Filename = FolderName & "\" & iFilename.ToString()& ".cos"
txtReceiptNumber.Text = iFilename.ToString()
IsNewCustomerOrder = False
' If a cleaning order was already opened, we will simply update it
Else
Filename = "C:\Musical Instrument Store\Receipts\" & _
txtReceiptNumber.Text & ".cos"
End If
Dim wrtCustomerOrder As StreamWriter = New StreamWriter(Filename)
Try
wrtCustomerOrder.WriteLine(txtItemNumber1.Text)
wrtCustomerOrder.WriteLine(txtDescription1.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice1.Text)
wrtCustomerOrder.WriteLine(txtQuantity1.Text)
wrtCustomerOrder.WriteLine(txtItemNumber2.Text)
wrtCustomerOrder.WriteLine(txtDescription2.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice2.Text)
wrtCustomerOrder.WriteLine(txtQuantity2.Text)
wrtCustomerOrder.WriteLine(txtItemNumber3.Text)
wrtCustomerOrder.WriteLine(txtDescription3.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice3.Text)
wrtCustomerOrder.WriteLine(txtQuantity3.Text)
wrtCustomerOrder.WriteLine(txtItemNumber4.Text)
wrtCustomerOrder.WriteLine(txtDescription4.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice4.Text)
wrtCustomerOrder.WriteLine(txtQuantity4.Text)
wrtCustomerOrder.WriteLine(txtItemNumber5.Text)
wrtCustomerOrder.WriteLine(txtDescription5.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice5.Text)
wrtCustomerOrder.WriteLine(txtQuantity5.Text)
wrtCustomerOrder.WriteLine(txtItemNumber6.Text)
wrtCustomerOrder.WriteLine(txtDescription6.Text)
wrtCustomerOrder.WriteLine(txtUnitPrice6.Text)
wrtCustomerOrder.WriteLine(txtQuantity6.Text)
wrtCustomerOrder.WriteLine(txtItemsTotal.Text)
wrtCustomerOrder.WriteLine(txtTaxRate.Text)
Finally
wrtCustomerOrder.Close()
End Try
End Sub
|
- In the Class Name combo box, select
- Double-click the Open button and implement its event as follows:
Private Sub btnOpen_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnOpen.Click
Dim FolderName = "C:\Musical Instrument Store\Receipts"
Dim strFilename As String = "C:\Musical Instrument Store\Receipts\1000.cos"
If txtReceiptNumber.Text = "" Then
Exit Sub
Else
Try
strFilename = FolderName & "\" & _
txtReceiptNumber.Text & ".cos"
Dim rdrCustomerOrder As StreamReader = New StreamReader(strFilename)
Try
txtItemNumber1.Text = rdrCustomerOrder.ReadLine()
txtDescription1.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice1.Text = rdrCustomerOrder.ReadLine()
txtQuantity1.Text = rdrCustomerOrder.ReadLine()
Item1Leave(sender, e)
txtItemNumber2.Text = rdrCustomerOrder.ReadLine()
txtDescription2.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice2.Text = rdrCustomerOrder.ReadLine()
txtQuantity2.Text = rdrCustomerOrder.ReadLine()
Item2Leave(sender, e)
txtItemNumber3.Text = rdrCustomerOrder.ReadLine()
txtDescription3.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice3.Text = rdrCustomerOrder.ReadLine()
txtQuantity3.Text = rdrCustomerOrder.ReadLine()
Item3Leave(sender, e)
txtItemNumber4.Text = rdrCustomerOrder.ReadLine()
txtDescription4.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice4.Text = rdrCustomerOrder.ReadLine()
txtQuantity4.Text = rdrCustomerOrder.ReadLine()
Item4Leave(sender, e)
txtItemNumber5.Text = rdrCustomerOrder.ReadLine()
txtDescription5.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice5.Text = rdrCustomerOrder.ReadLine()
txtQuantity5.Text = rdrCustomerOrder.ReadLine()
Item5Leave(sender, e)
txtItemNumber6.Text = rdrCustomerOrder.ReadLine()
txtDescription6.Text = rdrCustomerOrder.ReadLine()
txtUnitPrice6.Text = rdrCustomerOrder.ReadLine()
txtQuantity6.Text = rdrCustomerOrder.ReadLine()
Item6Leave(sender, e)
txtItemsTotal.Text = rdrCustomerOrder.ReadLine()
txtTaxRate.Text = rdrCustomerOrder.ReadLine()
CalculateOrder()
IsNewCustomerOrder = False
Finally
rdrCustomerOrder.Close()
End Try
Catch Exc As FileNotFoundException
MsgBox("There is no customer order with that receipt number")
End Try
End If
End Sub
|
- In the Class Name combo box, select (MusicStore Events)
- In the Method Name combo box, select Load and implement the event as
follows:
Private Sub MusicStore_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Me.Load
btnNewCustomerOrder_Click(sender, e)
LoadMusicStore()
IsNewCustomerOrder = True
End Sub
|
- In the Class Name combo box, select btnClose
- Double-click the Close and implement its Click event as follows:
Private Sub btnClose_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnClose.Click
End
End Sub
|
- Execute the application to test it
- Access the Item Editor dialog box and create a few items as follows
(let the computer create the item numbers):
| Category |
Type |
Item Name |
Unit Price |
Picture |
| Guitars |
Electric |
Gibson Les Paul Vintage Mahogany Electric Guitar |
745.95 |
Picture |
| Bass |
Electric 4-String |
Epiphone Thunderbird IV Bass |
325.85 |
Picture |
| Keyboards |
Synthesizers |
Alesis QS8.2 88 Key Synthesizer |
825.50 |
Picture |
| Guitars |
Acoustic |
Gretsch Guitars G100 Synchromatic Archtop Acoustic Guitar |
595.95 |
Picture |
| Drums |
Drum Set |
Pulse Pro 5-Piece Drum Set with Cymbals |
395.95 |
Picture |
| Keyboards |
Pianos |
Roland RD-700SX Digital Piano |
2195.00 |
|
| Accessories |
Cables |
Mogami Gold AES/EBU Interconnect Cable with Neutrik XLR |
45.85 |
|
| Guitars |
Acoustic-Electric |
Ibanez V Series V70CE Dreadnought Cutaway Acoustic-Electric Guitar |
225.50 |
|
| Guitars |
Electric |
Schecter C-1 Hellraiser Electric Guitar |
650.00 |
Picture |
| Keyboards |
Synthesizers |
Roland V Synth GT Elastic Audio Synthesizer Keyboard |
2895.50 |
Picture |
| Bass |
Electric 5-String |
Fender Jazz Bass 24 V 5-String Bass Guitar |
825.50 |
Picture |
| Guitars |
Electric |
Fender Standard Stratocaster Left-Handed Electric Guitar |
425.85 |
Picture |
| Recording |
Microphone |
MXL V63M Studio Condenser Microphone |
72.95 |
Picture |
| Guitars |
Acoustic |
Yamaha FD01S Acoustic Folk Guitar |
185.95 |
|
| Book/CD/DVD |
Instructional |
Hal Leonard Amazing Phrasing - Alto Sax (Book/CD) |
18.00 |
|
| Guitars |
Classical & Nylon |
Alvarez Artist Series AC60S Classical Acoustic Guitar |
275.95 |
Picture |
| Guitars |
Acoustic |
Washburn D100DL Acoustic Guitar |
150.50 |
Picture |
| Drums |
Electronic Percussion |
Boss DR-670 Dr. Rhythm Drum Machine |
275.85 |
Picture |
| Recording |
Microphone |
Shure SM58 Mic |
95.95 |
Picture |
| Accessories |
Cables |
Live Wire HPE325 Headphone and Extension Cable |
10.95 |
Picture |
| Bass |
Acoustic Fretted |
Ibanez AEB10E Acoustic-Electric Bass Guitar with Onboard Tuner |
350.00 |
Picture |
| Drums |
World Percussion |
Latin Percussion Conga 3-Pack with Bongos |
595.95 |
Picture |
| Keyboards |
Synthesizers |
Roland JUNO-D 61-Key Synthesizer |
595.95 |
Picture |
| Drums |
World Percussion |
Latin Percussion Aspire Conga Set with Bongos and Stand |
425.50 |
Picture |
| Recording |
Microphone |
AKG Perception 200 Condenser Microphone |
160.00 |
Picture |
|
 |
 |
 |
|
- Create a few customers' orders and click Save then New Order each
time. Here are examples:
- Close the form and return to your programming environment
|
Enumerating the Collection For Each Item
|
|
One of the most valuables features of Visual Basic is
to use the For Each loop to enumerate the members of a collection. To make
this possible, you must implement the IEnumerator interface in your
collection class. Following the rules of interface implementation, you must
override the members of IEnumerator.
|
Checking the Existence of an Item
|
|
One of the routine operations you can perform on a list is
to find out whether it contains a certain value. To assist you with this
operation, the System.Collections.IList interface is equipped with a method named Contains. Its syntax is:
Function Contains(value As Object) As Boolean
This method takes as argument the value to look for. If the
value is found in the list, the method returns True. If no value is found in the
collection, this method returns False.
Here is an example of implementing this method:
Public Function Contains(ByVal value As Object) As Boolean _
Implements System.Collections.IList.Contains
Dim i As Integer
For i = 0 To Count - 1
If Objects(i) = value Then
Return True
i = i + 1
End If
Next
Return False
End Function
This method calls the Equals() method of the objects
that make up the list to
find out whether the value argument exists in the collection. If this
method produces a wrong result, especially if you are using your own class to
represent the item, you may have to override your own Equals()
method.
|
Getting the Index of an Item
|
|
The System.Collections.IList.Contains() method is used to check whether a particular
value (already)
exists in the collection. If you know that a certain item exists in the
collection but you do not know its index inside the list, the IList interface can
assist you through a method named IndexOf. Its syntax is:
Function IndexOf(value As Object) As Integer
This method takes as argument the value to look for in the list. If the
value is found in the collection, the method returns its index. If there is no
value defined like that, the method returns -1. Here is an example of implementing
this method:
Public Function IndexOf(ByVal value As Object) As Integer _
Implements System.Collections.IList.IndexOf
Dim i As Integer
For i = 0 To Count - 1
If Objects(i) = value Then
Return 1
i = i + 1
End If
Next
Return -1
End Function
This method calls the Equals() method of the objects that make up the
collection to find out whether
the value argument exists in the list. If this method produces a
wrong result, especially if you are using your own class to represent the value,
you may have to override your own Equals() method.
|
Deleting Values in the List
|
|
|
Deleting a Value by its Index
|
|
If a value is not necessary in your list, you can delete it.
Probably the simplest way to delete a value is to specify its position in the
list. To support this operation, both the System.Collections.IList and
the System.Collections.Generic.IList interfaces are equipped with a
method named RemoveAt. The syntax of the RemoveAt() method is is
the same for both interfaces and it is:
Sub RemoveAt(index As Integer)
This method takes as argument the index of the value to be
removed. Here is an example:
Public Sub RemoveAt(ByVal index As Integer) _
Implements System.Collections.IList.RemoveAt
Dim i As Integer
If (index >= 0) And (index < Count) Then
For i = index To Count - 1
Objects(i) = Objects(i + 1)
Counter = Counter - 1
Next
End If
End Sub
|
Deleting an Item by its Value
|
|
The problem with deleting a value based on its index is
that, if you are not sure, you could delete the wrong value. An alternative is to
first precisely define the value you want to get rid of, and then hand the value
itself to the compiler that would remove it. To support this approach, the System.Collections.IList
interface is equipped with a method named Remove() and whose syntax is:
Sub Remove(value As Object)
This method takes as argument the value to be deleted. This
means that the compiler will first look for the value in the list. If it finds
that value, it removes it. If there is no value like that, nothing happens
(the compiler does not throw an exception. Here is an example of implementing
this method:
Public Sub Remove(ByVal value As Object) _
Implements System.Collections.IList.Remove
RemoveAt(IndexOf(value))
End Sub
To remove all value from a list at once, you can implement Clear()
method of the System.Collections.IList interface. Its syntax is:
Sub Clear
Here is an example of implementing it:
Public Class BookCollection
Implements IList
Private Counter As Integer
Private Objects(10) As Object
Public Sub New()
Counter = 0
End Sub
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) _
Implements System.Collections.ICollection.CopyTo
End Sub
Public ReadOnly Property Count() As Integer _
Implements System.Collections.ICollection.Count
Get
Return Counter
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean _
Implements System.Collections.ICollection.IsSynchronized
Get
Return False
End Get
End Property
Public ReadOnly Property SyncRoot() As Object _
Implements System.Collections.ICollection.SyncRoot
Get
Return Me
End Get
End Property
Public Function GetEnumerator() As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return Nothing
End Function
Public Function Add(ByVal value As Object) As Integer _
Implements System.Collections.IList.Add
' Check whether there is still room in
' the array to add a new item
If Counter < Objects.Length Then
' Since there is room, put the new item to the end
Objects(Counter) = value
' increase the number of items
Counter = Counter + 1
' Return the index of the item that was added
Return Counter - 1
' Since the item could not be added, return a negative index
Else
Return -1
End If
End Function
Public Sub Clear() Implements System.Collections.IList.Clear
Counter = 0
End Sub
Public Function Contains(ByVal value As Object) As Boolean _
Implements System.Collections.IList.Contains
Dim i As Integer
For i = 0 To Count - 1
If Objects(i) = value Then
Return True
i = i + 1
End If
Next
Return False
End Function
Public Function IndexOf(ByVal value As Object) As Integer _
Implements System.Collections.IList.IndexOf
Dim i As Integer
For i = 0 To Count - 1
If Objects(i) = value Then
Return 1
i = i + 1
End If
Next
Return -1
End Function
Public Sub Insert(ByVal index As Integer, ByVal value As Object) _
Implements System.Collections.IList.Insert
End Sub
Public ReadOnly Property IsFixedSize() As Boolean _
Implements System.Collections.IList.IsFixedSize
Get
Return False
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean _
Implements System.Collections.IList.IsReadOnly
Get
Return False
End Get
End Property
Default Public Property Item(ByVal index As Integer) As Object _
Implements System.Collections.IList.Item
Get
Return Objects(index)
End Get
Set(ByVal value As Object)
Objects(index) = value
End Set
End Property
Public Sub Remove(ByVal value As Object) _
Implements System.Collections.IList.Remove
RemoveAt(IndexOf(value))
End Sub
Public Sub RemoveAt(ByVal index As Integer) _
Implements System.Collections.IList.RemoveAt
Dim i As Integer
If (index >= 0) And (index < Count) Then
For i = index To Count - 1
Objects(i) = Objects(i + 1)
Counter = Counter - 1
Next
End If
End Sub
End Class
|
|